diff options
Diffstat (limited to 'hacks/metaballs.c')
-rw-r--r-- | hacks/metaballs.c | 436 |
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" */ + |