From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / hacks / metaballs.c
1 /* MetaBalls, 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 - "metaballs.c"
12  *
13  * [01/24/03] - W.P. van Paassen: Additional features
14  * [12/29/02] - W.P. van Paassen: Port to X for use with XScreenSaver, the shadebob hack by Shane Smit was used as a template
15  * [12/26/02] - 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 /*blob structure*/
24 typedef struct 
25 {
26   short xpos,ypos;
27 } BLOB;
28
29 struct state {
30   Display *dpy;
31   Window window;
32
33   unsigned short iWinWidth, iWinHeight;
34   char *sColor;
35
36   unsigned int nBlobCount;
37   unsigned char radius;
38   unsigned char delta;
39   unsigned char dradius;
40   unsigned short sradius;
41   unsigned char **blob;
42   BLOB *blobs;
43   unsigned char **blub;
44
45   int delay, cycles;
46   signed short iColorCount;
47   unsigned long *aiColorVals;
48   XImage *pImage;
49   GC gc;
50   int draw_i;
51 };
52
53
54 #undef BELLRAND
55 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
56
57 static void init_blob(struct state *st, BLOB *blob)
58 {
59   blob->xpos = st->iWinWidth/4  + BELLRAND(st->iWinWidth/2)  - st->radius;
60   blob->ypos = st->iWinHeight/4 + BELLRAND(st->iWinHeight/2) - st->radius;
61 }
62
63 static void Execute( struct state *st )
64 {
65         int i, j, k;
66
67         /* clear st->blub array */
68         for (i = 0; i < st->iWinHeight; ++i)
69           memset(st->blub[i], 0, st->iWinWidth * sizeof(unsigned char));
70
71         /* move st->blobs */
72         for (i = 0; i < st->nBlobCount; i++)
73         {
74           st->blobs[i].xpos += -st->delta + (int)((st->delta + .5f) * frand(2.0));
75           st->blobs[i].ypos += -st->delta + (int)((st->delta + .5f) * frand(2.0));
76         }
77
78         /* draw st->blobs to st->blub array */
79         for (k = 0; k < st->nBlobCount; ++k)
80           { 
81             if (st->blobs[k].ypos > -st->dradius && st->blobs[k].xpos > -st->dradius && st->blobs[k].ypos < st->iWinHeight && st->blobs[k].xpos < st->iWinWidth)
82               {
83                 for (i = 0; i < st->dradius; ++i)
84                   {
85                     if (st->blobs[k].ypos + i >= 0 && st->blobs[k].ypos + i < st->iWinHeight)
86                       {
87                         for (j = 0; j < st->dradius; ++j)
88                           {
89                             if (st->blobs[k].xpos + j >= 0 && st->blobs[k].xpos + j < st->iWinWidth)
90                               {
91                                 if (st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] < st->iColorCount-1)
92                                   {
93                                     if (st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] + st->blob[i][j] > st->iColorCount-1)
94                                       st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] = st->iColorCount-1;
95                                     else 
96                                       st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] += st->blob[i][j];     
97                                   }
98                               }
99                           }
100                       }
101                   }
102               }
103             else
104               init_blob(st, st->blobs + k);
105           }
106
107         memset( st->pImage->data, 0, st->pImage->bytes_per_line * st->pImage->height);
108
109         /* draw st->blub array to screen */
110         for (i = 0; i < st->iWinHeight; ++i)
111           {
112             for (j = 0; j < st->iWinWidth; ++j)
113               {
114                 if (st->aiColorVals[st->blub[i][j]] > 0)
115                   XPutPixel( st->pImage, j, i, st->aiColorVals[st->blub[i][j]] );
116               }
117           }
118
119         XPutImage( st->dpy, st->window, st->gc, st->pImage,
120                    0, 0, 0, 0, st->iWinWidth, st->iWinHeight );
121 }
122
123 static unsigned long * SetPalette(struct state *st )
124 {
125         XWindowAttributes XWinAttribs;
126         XColor Color, *aColors;
127         signed short iColor;
128         float nHalfColors;
129         
130         XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
131         
132         Color.red =   random() % 0xFFFF;
133         Color.green = random() % 0xFFFF;
134         Color.blue =  random() % 0xFFFF;
135
136         if( strcasecmp( st->sColor, "random" ) && !XParseColor( st->dpy, XWinAttribs.colormap, st->sColor, &Color ) )
137                 fprintf( stderr, "%s: color %s not found in database. Choosing to random...\n", progname, st->sColor );
138
139 #ifdef VERBOSE
140         printf( "%s: Base color (RGB): <%d, %d, %d>\n", progname, Color.red, Color.green, Color.blue );
141 #endif  /*  VERBOSE */
142
143         st->iColorCount = get_integer_resource(st->dpy,  "ncolors", "Integer" );
144         if( st->iColorCount <   2 )     st->iColorCount = 2;
145         if( st->iColorCount > 255 )     st->iColorCount = 255;
146
147         aColors    = calloc( st->iColorCount, sizeof(XColor) );
148         st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) );
149         
150         for( iColor=0; iColor < st->iColorCount; iColor++ )
151         {
152                 nHalfColors = st->iColorCount / 2.0F;
153                 /* Black -> Base Color */
154                 if( iColor < (st->iColorCount/2) )
155                 {
156                         aColors[ iColor ].red   = ( Color.red   / nHalfColors ) * iColor;
157                         aColors[ iColor ].green = ( Color.green / nHalfColors ) * iColor;
158                         aColors[ iColor ].blue  = ( Color.blue  / nHalfColors ) * iColor;
159                 }
160                 /* Base Color -> White */
161                 else
162                 {
163                         aColors[ iColor ].red   = ( ( ( 0xFFFF - Color.red )   / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.red;
164                         aColors[ iColor ].green = ( ( ( 0xFFFF - Color.green ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.green;
165                         aColors[ iColor ].blue  = ( ( ( 0xFFFF - Color.blue )  / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.blue;
166                 }
167
168                 if( !XAllocColor( st->dpy, XWinAttribs.colormap, &aColors[ iColor ] ) )
169                 {
170                         /* start all over with less colors */   
171                         XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, iColor, 0 );
172                         free( aColors );
173                         free( st->aiColorVals );
174                         (st->iColorCount)--;
175
176                         if (st->iColorCount < 6)
177                           {
178                             fprintf (stderr, "%s: insufficient colors!\n",
179                                      progname);
180                             exit (1);
181                           }
182
183                         aColors     = calloc( st->iColorCount, sizeof(XColor) );
184                         st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) );
185                         iColor = -1;
186                 }
187                 else
188                         st->aiColorVals[ iColor ] = aColors[ iColor ].pixel;
189         }
190
191         free( aColors );
192
193         XSetWindowBackground( st->dpy, st->window, st->aiColorVals[ 0 ] );
194
195         return st->aiColorVals;
196 }
197
198
199 static void Initialize( struct state *st )
200 {
201         XGCValues gcValues;
202         XWindowAttributes XWinAttribs;
203         int /*iBitsPerPixel,*/ i, j;
204         unsigned int distance_squared;
205         float fraction;
206
207         /* Create the Image for drawing */
208         XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
209
210         /* Find the preferred bits-per-pixel. (jwz) */
211 #if 0
212         {
213                 int pfvc = 0;
214                 XPixmapFormatValues *pfv = XListPixmapFormats( st->dpy, &pfvc );
215                 for( i=0; i<pfvc; i++ )
216                         if( pfv[ i ].depth == XWinAttribs.depth )
217                         {
218                                 iBitsPerPixel = pfv[ i ].bits_per_pixel;
219                                 break;
220                         }
221                 if( pfv )
222                         XFree (pfv);
223         }
224 #endif
225
226         /*  Create the GC. */
227         st->gc = XCreateGC( st->dpy, st->window, 0, &gcValues );
228
229         st->pImage = XCreateImage( st->dpy, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
230                                                           XWinAttribs.width, XWinAttribs.height, BitmapPad( st->dpy ), 0 );
231         (st->pImage)->data = calloc((st->pImage)->bytes_per_line, (st->pImage)->height);
232
233         st->iWinWidth = XWinAttribs.width;
234         st->iWinHeight = XWinAttribs.height;
235
236         /*  Get the base color. */
237         st->sColor = get_string_resource(st->dpy,  "color", "Color" );
238
239         /*  Get the st->delta. */
240         st->delta = get_integer_resource(st->dpy,  "delta", "Integer" );
241         if (st->delta < 1)
242           st->delta = 1;
243         else if (st->delta > 20)
244           st->delta = 20;
245         
246         /*  Get the st->radius. */
247         st->radius = get_integer_resource(st->dpy,  "radius", "Integer" );
248         if (st->radius < 2)
249           st->radius = 2;
250         if (st->radius > 100)
251           st->radius = 100;
252         
253         st->radius = (st->radius / 100.0) * (st->iWinHeight >> 3);
254         if (st->radius >= 128) /* should use UCHAR_MAX? */
255           st->radius = 127; /* st->dradius should fit in u_char */
256
257         st->dradius = st->radius * 2;
258         st->sradius = st->radius * st->radius;
259
260         /* create st->blob */
261         st->blob = malloc ( st->dradius * sizeof(unsigned char*));
262         for (i = 0; i < st->dradius; ++i)
263           st->blob[i] = malloc( st->dradius * sizeof(unsigned char));
264
265         /* create st->blub array */
266         st->blub = malloc( st->iWinHeight * sizeof(unsigned char*));
267         for (i = 0; i < st->iWinHeight; ++i)
268           st->blub[i] = malloc( st->iWinWidth * sizeof(unsigned char));
269
270         /* create st->blob */
271         for (i = -st->radius; i < st->radius; ++i)
272           {
273             for (j = -st->radius; j < st->radius; ++j)
274               {
275                 distance_squared = i * i + j * j;
276                 if (distance_squared <= st->sradius)
277                   {
278                     /* compute density */     
279                     fraction = (float)distance_squared / (float)st->sradius;
280                     st->blob[i + st->radius][j + st->radius] = pow((1.0 - (fraction * fraction)),4.0) * 255.0;
281                   }
282                 else
283                   {
284                     st->blob[i + st->radius][j + st->radius] = 0;
285                   }
286               }    
287           }
288         
289         for (i = 0; i < st->nBlobCount; i++)
290           {
291             init_blob(st, st->blobs + i);
292           }
293 }
294
295 static void *
296 metaballs_init (Display *dpy, Window window)
297 {
298   struct state *st = (struct state *) calloc (1, sizeof(*st));
299 #ifdef VERBOSE
300   time_t nTime = time( NULL );
301   unsigned short iFrame = 0;
302 #endif  /*  VERBOSE */
303
304   st->dpy = dpy;
305   st->window = window;
306
307   st->nBlobCount = get_integer_resource(st->dpy,  "count", "Integer" );
308   if( st->nBlobCount > 255 ) st->nBlobCount = 255;
309   if( st->nBlobCount <  2 ) st->nBlobCount = 2;
310
311   if( ( st->blobs = calloc( st->nBlobCount, sizeof(BLOB) ) ) == NULL )
312     {
313       fprintf( stderr, "%s: Could not allocate %d Blobs\n", progname, st->nBlobCount );
314       abort();
315     }
316 #ifdef VERBOSE 
317   printf( "%s: Allocated %d Blobs\n", progname, st->nBlobCount );
318 #endif  /*  VERBOSE */
319
320   Initialize( st );
321
322   st->delay = get_integer_resource(st->dpy,  "delay", "Integer" );
323   st->cycles = get_integer_resource(st->dpy,  "cycles", "Integer" );
324
325   st->draw_i = -1;
326   return st;
327 }
328
329
330
331 static unsigned long
332 metaballs_draw (Display *dpy, Window window, void *closure)
333 {
334   struct state *st = (struct state *) closure;
335
336   if( st->draw_i < 0 || st->draw_i++ >= st->cycles )
337     {
338       int i;
339       XWindowAttributes XWinAttribs;
340       XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
341
342       memset( st->pImage->data, 0, st->pImage->bytes_per_line * st->pImage->height );
343       XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, st->iColorCount, 0 );
344       free( st->aiColorVals );
345       st->aiColorVals = SetPalette( st );
346       XClearWindow( st->dpy, st->window );
347       for (i = 0; i < st->nBlobCount; i++)
348         {
349           init_blob(st, st->blobs + i);
350         }
351       st->draw_i = 0;
352     }
353
354   Execute( st );
355
356 #ifdef VERBOSE
357   iFrame++;
358   if( nTime - time( NULL ) )
359     {
360       printf( "%s: %d FPS\n", progname, iFrame );
361       nTime = time( NULL );
362       iFrame = 0;
363     }
364 #endif  /*  VERBOSE */
365
366   return st->delay;
367 }
368
369
370 static void
371 metaballs_reshape (Display *dpy, Window window, void *closure, 
372                  unsigned int w, unsigned int h)
373 {
374 }
375
376 static Bool
377 metaballs_event (Display *dpy, Window window, void *closure, XEvent *event)
378 {
379   return False;
380 }
381
382 static void
383 metaballs_free (Display *dpy, Window window, void *closure)
384 {
385 #if 0
386   struct state *st = (struct state *) closure;
387         free( st->pImage->data );
388         XDestroyImage( st->pImage );
389         free( st->aiColorVals );
390         free( st->blobs );
391         for (i = 0; i < st->iWinHeight; ++i)
392           free( st->blub[i] );
393         free( st->blub );
394         for (i = 0; i < st->dradius; ++i)
395           free( st->blob[i] );
396         free( st->blob );
397 #endif
398 }
399
400
401 static const char *metaballs_defaults [] = {
402   ".background: black",
403   ".foreground: white",
404   "*color:    random",
405   "*count:    10",
406   "*cycles:   1000",
407   "*ncolors:  256",
408   "*delay:    10000",
409   "*radius:   100",
410   "*delta:   3",
411 #ifdef USE_IPHONE
412   "*ignoreRotation: True",
413 #endif
414   0
415 };
416
417 static XrmOptionDescRec metaballs_options [] = {
418   { "-color",   ".color",   XrmoptionSepArg, 0 },
419   { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
420   { "-count",   ".count",   XrmoptionSepArg, 0 },
421   { "-delay",   ".delay",   XrmoptionSepArg, 0 },
422   { "-cycles",  ".cycles",  XrmoptionSepArg, 0 },
423   { "-radius",  ".radius",  XrmoptionSepArg, 0 },
424   { "-delta",  ".delta",  XrmoptionSepArg, 0 },
425   { 0, 0, 0, 0 }
426 };
427
428
429 XSCREENSAVER_MODULE ("MetaBalls", metaballs)
430
431 /* End of Module - "metaballs.c" */
432