summaryrefslogtreecommitdiffstats
path: root/hacks/metaballs.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/metaballs.c')
-rw-r--r--hacks/metaballs.c436
1 files changed, 436 insertions, 0 deletions
diff --git a/hacks/metaballs.c b/hacks/metaballs.c
new file mode 100644
index 0000000..3a2ae73
--- /dev/null
+++ b/hacks/metaballs.c
@@ -0,0 +1,436 @@
+/* MetaBalls, Copyright (c) 2002-2003 W.P. van Paassen <peter@paassen.tmfweb.nl>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Module - "metaballs.c"
+ *
+ * [01/24/03] - W.P. van Paassen: Additional features
+ * [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
+ * [12/26/02] - W.P. van Paassen: Creation for the Demo Effects Collection (http://demo-effects.sourceforge.net)
+ */
+
+#include <math.h>
+#include "screenhack.h"
+
+/*#define VERBOSE*/
+
+/*blob structure*/
+typedef struct
+{
+ short xpos,ypos;
+} BLOB;
+
+struct state {
+ Display *dpy;
+ Window window;
+
+ unsigned short iWinWidth, iWinHeight;
+ char *sColor;
+
+ unsigned int nBlobCount;
+ unsigned char radius;
+ unsigned char delta;
+ unsigned char dradius;
+ unsigned short sradius;
+ unsigned char **blob;
+ BLOB *blobs;
+ unsigned char **blub;
+
+ int delay, cycles;
+ signed short iColorCount;
+ unsigned long *aiColorVals;
+ XImage *pImage;
+ GC gc;
+ int draw_i;
+};
+
+
+#undef BELLRAND
+#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
+
+static void init_blob(struct state *st, BLOB *blob)
+{
+ blob->xpos = st->iWinWidth/4 + BELLRAND(st->iWinWidth/2) - st->radius;
+ blob->ypos = st->iWinHeight/4 + BELLRAND(st->iWinHeight/2) - st->radius;
+}
+
+static void Execute( struct state *st )
+{
+ int i, j, k;
+
+ /* clear st->blub array */
+ for (i = 0; i < st->iWinHeight; ++i)
+ memset(st->blub[i], 0, st->iWinWidth * sizeof(unsigned char));
+
+ /* move st->blobs */
+ for (i = 0; i < st->nBlobCount; i++)
+ {
+ st->blobs[i].xpos += -st->delta + (int)((st->delta + .5f) * frand(2.0));
+ st->blobs[i].ypos += -st->delta + (int)((st->delta + .5f) * frand(2.0));
+ }
+
+ /* draw st->blobs to st->blub array */
+ for (k = 0; k < st->nBlobCount; ++k)
+ {
+ 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)
+ {
+ for (i = 0; i < st->dradius; ++i)
+ {
+ if (st->blobs[k].ypos + i >= 0 && st->blobs[k].ypos + i < st->iWinHeight)
+ {
+ for (j = 0; j < st->dradius; ++j)
+ {
+ if (st->blobs[k].xpos + j >= 0 && st->blobs[k].xpos + j < st->iWinWidth)
+ {
+ if (st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] < st->iColorCount-1)
+ {
+ if (st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] + st->blob[i][j] > st->iColorCount-1)
+ st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] = st->iColorCount-1;
+ else
+ st->blub[st->blobs[k].ypos + i][st->blobs[k].xpos + j] += st->blob[i][j];
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ init_blob(st, st->blobs + k);
+ }
+
+ memset( st->pImage->data, 0, st->pImage->bytes_per_line * st->pImage->height);
+
+ /* draw st->blub array to screen */
+ for (i = 0; i < st->iWinHeight; ++i)
+ {
+ for (j = 0; j < st->iWinWidth; ++j)
+ {
+ if (st->aiColorVals[st->blub[i][j]] > 0)
+ XPutPixel( st->pImage, j, i, st->aiColorVals[st->blub[i][j]] );
+ }
+ }
+
+ XPutImage( st->dpy, st->window, st->gc, st->pImage,
+ 0, 0, 0, 0, st->iWinWidth, st->iWinHeight );
+}
+
+static unsigned long * SetPalette(struct state *st )
+{
+ XWindowAttributes XWinAttribs;
+ XColor Color, *aColors;
+ signed short iColor;
+ float nHalfColors;
+
+ XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
+
+ Color.red = random() % 0xFFFF;
+ Color.green = random() % 0xFFFF;
+ Color.blue = random() % 0xFFFF;
+
+ if( strcasecmp( st->sColor, "random" ) && !XParseColor( st->dpy, XWinAttribs.colormap, st->sColor, &Color ) )
+ fprintf( stderr, "%s: color %s not found in database. Choosing to random...\n", progname, st->sColor );
+
+#ifdef VERBOSE
+ printf( "%s: Base color (RGB): <%d, %d, %d>\n", progname, Color.red, Color.green, Color.blue );
+#endif /* VERBOSE */
+
+ st->iColorCount = get_integer_resource(st->dpy, "ncolors", "Integer" );
+ if( st->iColorCount < 2 ) st->iColorCount = 2;
+ if( st->iColorCount > 255 ) st->iColorCount = 255;
+
+ aColors = calloc( st->iColorCount, sizeof(XColor) );
+ st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) );
+
+ for( iColor=0; iColor < st->iColorCount; iColor++ )
+ {
+ nHalfColors = st->iColorCount / 2.0F;
+ /* Black -> Base Color */
+ if( iColor < (st->iColorCount/2) )
+ {
+ aColors[ iColor ].red = ( Color.red / nHalfColors ) * iColor;
+ aColors[ iColor ].green = ( Color.green / nHalfColors ) * iColor;
+ aColors[ iColor ].blue = ( Color.blue / nHalfColors ) * iColor;
+ }
+ /* Base Color -> White */
+ else
+ {
+ aColors[ iColor ].red = ( ( ( 0xFFFF - Color.red ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.red;
+ aColors[ iColor ].green = ( ( ( 0xFFFF - Color.green ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.green;
+ aColors[ iColor ].blue = ( ( ( 0xFFFF - Color.blue ) / nHalfColors ) * ( iColor - nHalfColors ) ) + Color.blue;
+ }
+
+ if( !XAllocColor( st->dpy, XWinAttribs.colormap, &aColors[ iColor ] ) )
+ {
+ /* start all over with less colors */
+ XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, iColor, 0 );
+ free( aColors );
+ free( st->aiColorVals );
+ (st->iColorCount)--;
+
+ if (st->iColorCount < 6)
+ {
+ fprintf (stderr, "%s: insufficient colors!\n",
+ progname);
+ exit (1);
+ }
+
+ aColors = calloc( st->iColorCount, sizeof(XColor) );
+ st->aiColorVals = calloc( st->iColorCount, sizeof(unsigned long) );
+ iColor = -1;
+ }
+ else
+ st->aiColorVals[ iColor ] = aColors[ iColor ].pixel;
+ }
+
+ free( aColors );
+
+ XSetWindowBackground( st->dpy, st->window, st->aiColorVals[ 0 ] );
+
+ return st->aiColorVals;
+}
+
+
+static void Initialize( struct state *st )
+{
+ XGCValues gcValues;
+ XWindowAttributes XWinAttribs;
+ int /*iBitsPerPixel,*/ i, j;
+ unsigned int distance_squared;
+ float fraction;
+
+ /* Create the Image for drawing */
+ XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
+
+ /* Find the preferred bits-per-pixel. (jwz) */
+#if 0
+ {
+ int pfvc = 0;
+ XPixmapFormatValues *pfv = XListPixmapFormats( st->dpy, &pfvc );
+ for( i=0; i<pfvc; i++ )
+ if( pfv[ i ].depth == XWinAttribs.depth )
+ {
+ iBitsPerPixel = pfv[ i ].bits_per_pixel;
+ break;
+ }
+ if( pfv )
+ XFree (pfv);
+ }
+#endif
+
+ /* Create the GC. */
+ st->gc = XCreateGC( st->dpy, st->window, 0, &gcValues );
+
+ st->pImage = XCreateImage( st->dpy, XWinAttribs.visual, XWinAttribs.depth, ZPixmap, 0, NULL,
+ XWinAttribs.width, XWinAttribs.height, BitmapPad( st->dpy ), 0 );
+ (st->pImage)->data = calloc((st->pImage)->bytes_per_line, (st->pImage)->height);
+
+ st->iWinWidth = XWinAttribs.width;
+ st->iWinHeight = XWinAttribs.height;
+
+ /* Get the base color. */
+ st->sColor = get_string_resource(st->dpy, "color", "Color" );
+
+ /* Get the st->delta. */
+ st->delta = get_integer_resource(st->dpy, "delta", "Integer" );
+ if (st->delta < 1)
+ st->delta = 1;
+ else if (st->delta > 20)
+ st->delta = 20;
+
+ /* Get the st->radius. */
+ st->radius = get_integer_resource(st->dpy, "radius", "Integer" );
+ if (st->radius < 2)
+ st->radius = 2;
+ if (st->radius > 100)
+ st->radius = 100;
+
+ st->radius = (st->radius / 100.0) * (st->iWinHeight >> 3);
+ if (st->radius >= 128) /* should use UCHAR_MAX? */
+ st->radius = 127; /* st->dradius should fit in u_char */
+
+ if (st->iWinWidth < 100 || st->iWinHeight < 100) /* tiny window */
+ if (st->radius < 20)
+ st->radius = 20;
+
+ st->dradius = st->radius * 2;
+ st->sradius = st->radius * st->radius;
+
+ /* create st->blob */
+ st->blob = malloc ( st->dradius * sizeof(unsigned char*));
+ for (i = 0; i < st->dradius; ++i)
+ st->blob[i] = malloc( st->dradius * sizeof(unsigned char));
+
+ /* create st->blub array */
+ st->blub = malloc( st->iWinHeight * sizeof(unsigned char*));
+ for (i = 0; i < st->iWinHeight; ++i)
+ st->blub[i] = malloc( st->iWinWidth * sizeof(unsigned char));
+
+ /* create st->blob */
+ for (i = -st->radius; i < st->radius; ++i)
+ {
+ for (j = -st->radius; j < st->radius; ++j)
+ {
+ distance_squared = i * i + j * j;
+ if (distance_squared <= st->sradius)
+ {
+ /* compute density */
+ fraction = (float)distance_squared / (float)st->sradius;
+ st->blob[i + st->radius][j + st->radius] = pow((1.0 - (fraction * fraction)),4.0) * 255.0;
+ }
+ else
+ {
+ st->blob[i + st->radius][j + st->radius] = 0;
+ }
+ }
+ }
+
+ for (i = 0; i < st->nBlobCount; i++)
+ {
+ init_blob(st, st->blobs + i);
+ }
+}
+
+static void *
+metaballs_init (Display *dpy, Window window)
+{
+ struct state *st = (struct state *) calloc (1, sizeof(*st));
+#ifdef VERBOSE
+ time_t nTime = time( NULL );
+ unsigned short iFrame = 0;
+#endif /* VERBOSE */
+
+ st->dpy = dpy;
+ st->window = window;
+
+ st->nBlobCount = get_integer_resource(st->dpy, "count", "Integer" );
+ if( st->nBlobCount > 255 ) st->nBlobCount = 255;
+ if( st->nBlobCount < 2 ) st->nBlobCount = 2;
+
+ if( ( st->blobs = calloc( st->nBlobCount, sizeof(BLOB) ) ) == NULL )
+ {
+ fprintf( stderr, "%s: Could not allocate %d Blobs\n", progname, st->nBlobCount );
+ abort();
+ }
+#ifdef VERBOSE
+ printf( "%s: Allocated %d Blobs\n", progname, st->nBlobCount );
+#endif /* VERBOSE */
+
+ Initialize( st );
+
+ st->delay = get_integer_resource(st->dpy, "delay", "Integer" );
+ st->cycles = get_integer_resource(st->dpy, "cycles", "Integer" );
+
+ st->draw_i = -1;
+ return st;
+}
+
+
+
+static unsigned long
+metaballs_draw (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+
+ if( st->draw_i < 0 || st->draw_i++ >= st->cycles )
+ {
+ int i;
+ XWindowAttributes XWinAttribs;
+ XGetWindowAttributes( st->dpy, st->window, &XWinAttribs );
+
+ memset( st->pImage->data, 0, st->pImage->bytes_per_line * st->pImage->height );
+ XFreeColors( st->dpy, XWinAttribs.colormap, st->aiColorVals, st->iColorCount, 0 );
+ free( st->aiColorVals );
+ st->aiColorVals = SetPalette( st );
+ XClearWindow( st->dpy, st->window );
+ for (i = 0; i < st->nBlobCount; i++)
+ {
+ init_blob(st, st->blobs + i);
+ }
+ st->draw_i = 0;
+ }
+
+ Execute( st );
+
+#ifdef VERBOSE
+ iFrame++;
+ if( nTime - time( NULL ) )
+ {
+ printf( "%s: %d FPS\n", progname, iFrame );
+ nTime = time( NULL );
+ iFrame = 0;
+ }
+#endif /* VERBOSE */
+
+ return st->delay;
+}
+
+
+static void
+metaballs_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+}
+
+static Bool
+metaballs_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ return False;
+}
+
+static void
+metaballs_free (Display *dpy, Window window, void *closure)
+{
+#if 0
+ struct state *st = (struct state *) closure;
+ free( st->pImage->data );
+ XDestroyImage( st->pImage );
+ free( st->aiColorVals );
+ free( st->blobs );
+ for (i = 0; i < st->iWinHeight; ++i)
+ free( st->blub[i] );
+ free( st->blub );
+ for (i = 0; i < st->dradius; ++i)
+ free( st->blob[i] );
+ free( st->blob );
+#endif
+}
+
+
+static const char *metaballs_defaults [] = {
+ ".background: black",
+ ".foreground: white",
+ "*color: random",
+ "*count: 10",
+ "*cycles: 1000",
+ "*ncolors: 256",
+ "*delay: 10000",
+ "*radius: 100",
+ "*delta: 3",
+#ifdef HAVE_MOBILE
+ "*ignoreRotation: True",
+#endif
+ 0
+};
+
+static XrmOptionDescRec metaballs_options [] = {
+ { "-color", ".color", XrmoptionSepArg, 0 },
+ { "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
+ { "-count", ".count", XrmoptionSepArg, 0 },
+ { "-delay", ".delay", XrmoptionSepArg, 0 },
+ { "-cycles", ".cycles", XrmoptionSepArg, 0 },
+ { "-radius", ".radius", XrmoptionSepArg, 0 },
+ { "-delta", ".delta", XrmoptionSepArg, 0 },
+ { 0, 0, 0, 0 }
+};
+
+
+XSCREENSAVER_MODULE ("MetaBalls", metaballs)
+
+/* End of Module - "metaballs.c" */
+