From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / eruption.c
1 /* Eruption, Copyright (c) 2002-2003 W.P. van Paassen <peter@paassen.tmfweb.nl>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  *
11  * Module - "eruption.c"
12  *
13  * [02-2003] - W.P. van Paassen: Improvements, added some code of jwz from the pyro hack for a spherical distribution of the particles
14  * [01-2003] - W.P. van Paassen: Port to X for use with XScreenSaver, the shadebob hack by Shane Smit was used as a template
15  * [04-2002] - W.P. van Paassen: Creation for the Demo Effects Collection (http://demo-effects.sourceforge.net)
16  */
17
18 #include <math.h>
19 #include "screenhack.h"
20
21 /*#define VERBOSE*/ 
22
23 /* Slightly whacked, for better explosions
24  */
25 #define PI_2000 6284
26 #define SPREAD 15
27
28 /*particle structure*/
29 typedef struct 
30 {
31   short xpos, ypos, xdir, ydir;
32   unsigned char colorindex;
33   unsigned char dead;
34 } PARTICLE;
35
36 struct state {
37   Display *dpy;
38   Window window;
39
40  int sin_cache[PI_2000];
41  int cos_cache[PI_2000];
42
43   PARTICLE *particles;
44   unsigned short iWinWidth, iWinHeight;
45   unsigned char **fire;
46   unsigned short nParticleCount;
47   unsigned char xdelta, ydelta, decay;
48   signed char gravity;
49   signed short heat;
50
51   int cycles, delay;
52   GC gc;
53   signed short iColorCount;
54   unsigned long *aiColorVals;
55   XImage *pImage;
56
57   int draw_i;
58 };
59
60 static void
61 cache(struct state *st) /* jwz */
62 {               /*needs to be run once. Could easily be */
63   int i;        /*reimplemented to run and cache at compile-time,*/
64   double dA;    
65   for (i=0; i<PI_2000; i++)
66     {
67       dA=sin(((double) (random() % (PI_2000/2)))/1000.0);
68       /*Emulation of spherical distribution*/
69       dA+=asin(frand(1.0))/M_PI_2*0.1;
70       /*Approximating the integration of the binominal, for
71         well-distributed randomness*/
72       st->cos_cache[i]=-abs((int) (cos(((double)i)/1000.0)*dA*st->ydelta));
73       st->sin_cache[i]=(int) (sin(((double)i)/1000.0)*dA*st->xdelta);
74     }
75 }
76
77 static void init_particle(struct state *st, PARTICLE* particle, unsigned short xcenter, unsigned short ycenter)
78 {
79   int v = random() % PI_2000;
80   particle->xpos = xcenter - SPREAD + (random() % (SPREAD * 2));
81   particle->ypos = ycenter - SPREAD + (random() % (SPREAD * 2));;
82   particle->xdir = st->sin_cache[v];
83   particle->ydir = st->cos_cache[v];
84   particle->colorindex = st->iColorCount-1;
85   particle->dead = 0;
86 }
87
88 static void Execute( struct state *st )
89 {
90   int i, j;
91   unsigned int temp;
92
93   /* move and draw particles into st->fire array */
94   
95   for (i = 0; i < st->nParticleCount; i++)
96     {
97       if (!st->particles[i].dead)
98         {
99           st->particles[i].xpos += st->particles[i].xdir;
100           st->particles[i].ypos += st->particles[i].ydir;
101           
102           /* is particle dead? */
103           
104           if (st->particles[i].colorindex == 0)
105             {
106               st->particles[i].dead = 1;
107               continue;
108             }
109           
110           if (st->particles[i].xpos < 1)
111             {
112               st->particles[i].xpos = 1;
113               st->particles[i].xdir = -st->particles[i].xdir - 4;
114               st->particles[i].colorindex = st->iColorCount;
115             }
116           else if (st->particles[i].xpos >= st->iWinWidth - 2)
117             {
118               st->particles[i].xpos = st->iWinWidth - 2;
119               if (st->particles[i].xpos < 1) st->particles[i].xpos = 1;
120               st->particles[i].xdir = -st->particles[i].xdir + 4;
121               st->particles[i].colorindex = st->iColorCount;
122             }
123           
124           if (st->particles[i].ypos < 1)
125             {
126               st->particles[i].ypos = 1;
127               st->particles[i].ydir = -st->particles[i].ydir;
128               st->particles[i].colorindex = st->iColorCount;
129             }
130           else if (st->particles[i].ypos >= st->iWinHeight - 3)
131             {
132               st->particles[i].ypos = st->iWinHeight- 3;
133               if (st->particles[i].ypos < 1) st->particles[i].ypos = 1;
134               st->particles[i].ydir = (-st->particles[i].ydir >> 2) - (random() % 2);
135               st->particles[i].colorindex = st->iColorCount;
136             }
137
138           
139           /* st->gravity kicks in */
140           st->particles[i].ydir += st->gravity;
141           
142           /* particle cools off */
143           st->particles[i].colorindex--;
144           
145           /* draw particle */
146           if (st->iWinHeight <= 2 || st->iWinWidth <= 2) continue;
147           st->fire[st->particles[i].ypos][st->particles[i].xpos] = st->particles[i].colorindex;
148           st->fire[st->particles[i].ypos][st->particles[i].xpos - 1] = st->particles[i].colorindex;
149           st->fire[st->particles[i].ypos + 1][st->particles[i].xpos] = st->particles[i].colorindex;
150           st->fire[st->particles[i].ypos - 1][st->particles[i].xpos] = st->particles[i].colorindex;
151           st->fire[st->particles[i].ypos][st->particles[i].xpos + 1] = st->particles[i].colorindex;
152         }
153     }
154   
155   /* create st->fire effect */ 
156   for (i = 0; i < st->iWinHeight; i++)
157     {
158       for (j = 0; j < st->iWinWidth; j++)
159         {
160           if (j + 1 >= st->iWinWidth)
161             temp = 0;
162           else
163             temp = st->fire[i][j + 1];
164
165           if (j - 1 >= 0)  
166             temp += st->fire[i][j - 1];
167
168           if (i - 1 >= 0)
169             {
170               temp += st->fire[i - 1][j];
171               if (j - 1 >= 0)
172                 temp += st->fire[i - 1][j - 1];
173               if (j + 1 < st->iWinWidth)
174                 temp += st->fire[i - 1][j + 1];
175             }
176           
177           if (i + 1 < st->iWinHeight)
178             {
179               temp += st->fire[i + 1][j];
180               if (j + 1 < st->iWinWidth)
181                 temp += st->fire[i + 1][j + 1];
182               if (j - 1 >= 0)
183                 temp += st->fire[i + 1][j - 1];
184             }
185           
186           temp >>= 3;
187           
188           if (temp > st->decay)
189             {
190               temp -= st->decay;
191             }
192           else
193             temp = 0;
194           
195           st->fire[i][j] = temp;
196         }
197     }
198   
199   memset( st->pImage->data, 0, st->pImage->bytes_per_line * st->pImage->height );
200   
201   /* draw st->fire array to screen */
202   for (i = 0; i < st->iWinHeight; ++i)
203     {
204       for (j = 0; j < st->iWinWidth; ++j)
205         {
206           if (st->fire[i][j] > 0)
207             XPutPixel( st->pImage, j, i, st->aiColorVals[ st->fire[i][j] ] );
208         }
209     }
210   XPutImage( st->dpy, st->window, st->gc, st->pImage,
211              0,0,0,0, st->iWinWidth, st->iWinHeight );
212 }
213
214 static unsigned long * SetPalette(struct state *st)
215 {
216         XWindowAttributes XWinAttribs;
217         XColor Color, *aColors;
218         signed short iColor;
219
220         XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
221         
222         st->iColorCount = get_integer_resource(st->dpy,  "ncolors", "Integer" );
223         if( st->iColorCount < 16 )      st->iColorCount = 16;
224         if( st->iColorCount > 255 )     st->iColorCount = 256;
225
226         aColors    = calloc( st->iColorCount, sizeof(XColor) );
227         st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) );
228         
229         Color.red = Color.green = Color.blue = 65535 / st->iColorCount;
230
231         /* create st->fire palette */
232         for( iColor=0; iColor < st->iColorCount; iColor++ )
233         {
234           if (iColor < st->iColorCount >> 3)
235             {
236               /* black to blue */
237               aColors[iColor].red = 0;
238               aColors[iColor].green = 0;
239               aColors[iColor].blue = Color.blue * (iColor << 1);
240             }
241           else if (iColor < st->iColorCount >> 2)
242             {
243               /* blue to red */
244               signed short temp = (iColor - (st->iColorCount >> 3));
245               aColors[iColor].red = Color.red * (temp << 3);
246               aColors[iColor].green = 0;
247               aColors[iColor].blue = 16383 - Color.blue * (temp << 1);
248             }
249           else if (iColor < (st->iColorCount >> 2) + (st->iColorCount >> 3))
250             {
251               /* red to yellow */
252               signed short temp = (iColor - (st->iColorCount >> 2)) << 3;
253               aColors[iColor].red = 65535;
254               aColors[iColor].green = Color.green * temp;
255               aColors[iColor].blue = 0;
256             }
257           else if (iColor < st->iColorCount >> 1)
258             {
259               /* yellow to white */
260               signed int temp = (iColor - ((st->iColorCount >> 2) + (st->iColorCount >> 3))) << 3;
261               aColors[iColor].red = 65535;
262               aColors[iColor].green = 65535;
263               aColors[iColor].blue = Color.blue * temp;
264             }
265           else
266             {
267               /* white */
268               aColors[iColor].red = aColors[iColor].green = aColors[iColor].blue = 65535;
269             }
270
271           if( !XAllocColor( st->dpy, XWinAttribs.colormap, &aColors[ iColor ] ) )
272             {
273               /* start all over with less colors */     
274               XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, iColor, 0 );
275               free( aColors );
276               free( st->aiColorVals );
277               (st->iColorCount)--;
278               aColors     = calloc( st->iColorCount, sizeof(XColor) );
279               st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) );
280               iColor = -1;
281             }
282           else
283             st->aiColorVals[ iColor ] = aColors[ iColor ].pixel;
284         }
285
286         if (st->heat < st->iColorCount)
287           st->iColorCount = st->heat;
288         
289         free( aColors );
290
291         XSetWindowBackground( st->dpy, st->window, st->aiColorVals[ 0 ] );
292
293         return st->aiColorVals;
294 }
295
296
297 static void Initialize( struct state *st )
298 {
299         XGCValues gcValues;
300         XWindowAttributes XWinAttribs;
301         int /*iBitsPerPixel,*/ i;
302
303         /* Create the Image for drawing */
304         XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
305
306         /* Find the preferred bits-per-pixel. (jwz) */
307         {
308                 int pfvc = 0;
309                 XPixmapFormatValues *pfv = XListPixmapFormats( st->dpy, &pfvc );
310                 for( i=0; i<pfvc; i++ )
311                         if( pfv[ i ].depth == XWinAttribs.depth )
312                         {
313                                 /*iBitsPerPixel = pfv[ i ].bits_per_pixel;*/
314                                 break;
315                         }
316                 if( pfv )
317                         XFree (pfv);
318         }
319
320         /*  Create the GC. */
321         st->gc = XCreateGC( st->dpy, st->window, 0, &gcValues );
322
323         st->pImage = XCreateImage( st->dpy, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
324                                                           XWinAttribs.width, XWinAttribs.height, BitmapPad( st->dpy ), 0 );
325         (st->pImage)->data = calloc((st->pImage)->bytes_per_line, (st->pImage)->height);
326
327         st->iWinWidth = XWinAttribs.width;
328         st->iWinHeight = XWinAttribs.height;
329
330         /* create st->fire array */
331         st->fire = calloc( st->iWinHeight, sizeof(unsigned char*));
332         for (i = 0; i < st->iWinHeight; ++i)
333           st->fire[i] = calloc( st->iWinWidth, sizeof(unsigned char));
334
335         /*create st->particles */
336         st->particles = malloc (st->nParticleCount * sizeof(PARTICLE));
337 }
338
339 static void *
340 eruption_init (Display *dpy, Window window)
341 {
342   struct state *st = (struct state *) calloc (1, sizeof(*st));
343         XWindowAttributes XWinAttribs;
344         unsigned short sum = 0;
345 #ifdef VERBOSE
346         time_t nTime = time( NULL );
347         unsigned short iFrame = 0;
348 #endif  /*  VERBOSE */
349
350   st->dpy = dpy;
351   st->window = window;
352
353         st->nParticleCount = get_integer_resource(st->dpy,  "particles", "Integer" );
354         if (st->nParticleCount < 100)
355           st->nParticleCount = 100;
356         if (st->nParticleCount > 2000)
357           st->nParticleCount = 2000;
358
359         st->decay = get_integer_resource(st->dpy,  "cooloff", "Integer" );
360         if (st->decay <= 0)
361           st->decay = 0;
362         if (st->decay > 10)
363           st->decay = 10;
364
365         st->gravity = get_integer_resource(st->dpy,  "gravity", "Integer" );
366         if (st->gravity < -5)
367           st->gravity = -5;
368         if (st->gravity > 5)
369           st->gravity = 5;
370
371         st->heat = get_integer_resource(st->dpy,  "heat", "Integer" );
372         if (st->heat < 64)
373           st->heat = 64;
374         if (st->heat > 256)
375           st->heat = 256;
376
377 #ifdef VERBOSE 
378         printf( "%s: Allocated %d st->particles\n", progclass, st->nParticleCount );
379 #endif  /*  VERBOSE */
380
381         Initialize( st );
382
383         st->ydelta = 0;
384         while (sum < (st->iWinHeight >> 1) - SPREAD)
385           {
386             st->ydelta++;
387             sum += st->ydelta;
388           }
389
390         sum = 0;
391         while (sum < (st->iWinWidth >> 3))
392           {
393             st->xdelta++;
394             sum += st->xdelta;
395           }
396
397         st->delay = get_integer_resource(st->dpy,  "delay", "Integer" );
398         st->cycles = get_integer_resource(st->dpy,  "cycles", "Integer" );
399
400         cache(st);
401         
402         XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
403         XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, st->iColorCount, 0 );
404         free( st->aiColorVals );
405         st->aiColorVals = SetPalette( st );
406         XClearWindow( st->dpy, st->window );
407         memset( st->pImage->data, 0, st->pImage->bytes_per_line * st->pImage->height );
408
409         st->draw_i = -1;
410
411         return st;
412 }
413
414
415 static unsigned long
416 eruption_draw (Display *dpy, Window window, void *closure)
417 {
418   struct state *st = (struct state *) closure;
419             
420   if( st->draw_i < 0 || st->draw_i++ >= st->cycles )
421     {
422       /* compute random center */
423       unsigned short xcenter, ycenter;
424       xcenter = random() % st->iWinWidth;
425       ycenter = random() % st->iWinHeight;
426                 
427       for (st->draw_i = 0; st->draw_i < st->nParticleCount; st->draw_i++)
428         init_particle(st, st->particles + st->draw_i, xcenter, ycenter);
429       st->draw_i = 0;
430     }
431             
432   Execute( st );
433             
434 #ifdef VERBOSE
435   iFrame++;
436   if( nTime - time( NULL ) )
437     {
438       printf( "%s: %d FPS\n", progclass, iFrame );
439       nTime = time( NULL );
440       iFrame = 0;
441     }
442 #endif  /*  VERBOSE */
443
444   return st->delay;
445 }
446         
447
448 static void
449 eruption_reshape (Display *dpy, Window window, void *closure, 
450                  unsigned int w, unsigned int h)
451 {
452   struct state *st = (struct state *) closure;
453   XWindowAttributes XWinAttribs;
454   int i;
455
456   for (i = 0; i < st->iWinHeight; ++i)
457     free (st->fire[i]);
458
459   st->iWinWidth = w;
460   st->iWinHeight = h;
461
462   free (st->fire);
463   st->fire = calloc( st->iWinHeight, sizeof(unsigned char*));
464   for (i = 0; i < st->iWinHeight; ++i)
465     st->fire[i] = calloc( st->iWinWidth, sizeof(unsigned char));
466
467   XDestroyImage( st->pImage );
468   XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
469   st->pImage = XCreateImage( st->dpy, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
470                                                           XWinAttribs.width, XWinAttribs.height, BitmapPad( st->dpy ), 0 );
471   (st->pImage)->data = calloc((st->pImage)->bytes_per_line, (st->pImage)->height);
472
473   st->draw_i = -1;
474 }
475
476 static Bool
477 eruption_event (Display *dpy, Window window, void *closure, XEvent *event)
478 {
479   return False;
480 }
481
482 static void
483 eruption_free (Display *dpy, Window window, void *closure)
484 {
485 #if 0
486   struct state *st = (struct state *) closure;
487         XDestroyImage( st->pImage );
488         free( st->aiColorVals );
489         for (i = 0; i < st->iWinHeight; ++i)
490           free( st->fire[i] );
491         free( st->fire );
492         free( st->particles );
493 #endif
494 }
495
496
497 static const char *eruption_defaults [] = {
498   ".background: black",
499   ".foreground: white",
500   "*fpsTop:     true",
501   "*cycles:   80",
502   "*ncolors:  256",
503   "*delay:    10000",
504   "*particles: 300",
505   "*cooloff: 2",
506   "*gravity: 1",
507   "*heat: 256",
508   0
509 };
510
511 static XrmOptionDescRec eruption_options [] = {
512   { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
513   { "-delay",   ".delay",   XrmoptionSepArg, 0 },
514   { "-cycles",  ".cycles",  XrmoptionSepArg, 0 },
515   { "-particles",  ".particles",  XrmoptionSepArg, 0 },
516   { "-cooloff",  ".cooloff",  XrmoptionSepArg, 0 },
517   { "-gravity",  ".gravity",  XrmoptionSepArg, 0 },
518   { "-heat",  ".heat",  XrmoptionSepArg, 0 },
519   { 0, 0, 0, 0 }
520 };
521
522
523 XSCREENSAVER_MODULE ("Eruption", eruption)
524
525 /* End of Module - "eruption.c" */
526