2 * Substrate (dragorn@kismetwireless.net)
3 * Directly ported code from complexification.net Substrate art
4 * http://complexification.net/gallery/machines/substrate/applet_s/substrate_s.pde
8 * Albuquerque, New Mexico
13 * 1.1 dragorn Jan 04 2005 Fixed some indenting, typo in errors for parsing
15 * 1.1 dagraz Jan 04 2005 Added option for circular cracks (David Agraz)
16 * Cleaned up issues with timeouts in start_crack (DA)
17 * 1.0 dragorn Oct 10 2004 First port done
19 * Directly based the hacks of:
21 * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski <jwz@jwz.org>
23 * Permission to use, copy, modify, distribute, and sell this software and its
24 * documentation for any purpose is hereby granted without fee, provided that
25 * the above copyright notice appear in all copies and that both that
26 * copyright notice and this permission notice appear in supporting
27 * documentation. No representations are made about the suitability of this
28 * software for any purpose. It is provided "as is" without express or
33 #include "screenhack.h"
35 /* this program goes faster if some functions are inline. The following is
36 * borrowed from ifs.c */
37 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
44 /* Raw colormap extracted from pollockEFF.gif */
45 static const char *rgb_colormap[] = {
46 "#201F21", "#262C2E", "#352626", "#372B27",
47 "#302C2E", "#392B2D", "#323229", "#3F3229",
48 "#38322E", "#2E333D", "#333A3D", "#473329",
49 "#40392C", "#40392E", "#47402C", "#47402E",
50 "#4E402C", "#4F402E", "#4E4738", "#584037",
51 "#65472D", "#6D5D3D", "#745530", "#755532",
52 "#745D32", "#746433", "#7C6C36", "#523152",
53 "#444842", "#4C5647", "#655D45", "#6D5D44",
54 "#6C5D4E", "#746C43", "#7C6C42", "#7C6C4B",
55 "#6B734B", "#73734B", "#7B7B4A", "#6B6C55",
56 "#696D5E", "#7B6C5D", "#6B7353", "#6A745D",
57 "#727B52", "#7B7B52", "#57746E", "#687466",
58 "#9C542B", "#9D5432", "#9D5B35", "#936B36",
59 "#AA7330", "#C45A27", "#D95223", "#D85A20",
60 "#DB5A23", "#E57037", "#836C4B", "#8C6B4B",
61 "#82735C", "#937352", "#817B63", "#817B6D",
62 "#927B63", "#D9893B", "#E49832", "#DFA133",
63 "#E5A037", "#F0AB3B", "#8A8A59", "#B29A58",
64 "#89826B", "#9A8262", "#888B7C", "#909A7A",
65 "#A28262", "#A18A69", "#A99968", "#99A160",
66 "#99A168", "#CA8148", "#EB8D43", "#C29160",
67 "#C29168", "#D1A977", "#C9B97F", "#F0E27B",
68 "#9F928B", "#C0B999", "#E6B88F", "#C8C187",
69 "#E0C886", "#F2CC85", "#F5DA83", "#ECDE9D",
70 "#F5D294", "#F5DA94", "#F4E784", "#F4E18A",
71 "#F4E193", "#E7D8A7", "#F1D4A5", "#F1DCA5",
72 "#F4DBAD", "#F1DCAE", "#F4DBB5", "#F5DBBD",
73 "#F4E2AD", "#F5E9AD", "#F4E3BE", "#F5EABE",
74 "#F7F0B6", "#D9D1C1", "#E0D0C0", "#E7D8C0",
75 "#F1DDC6", "#E8E1C0", "#F3EDC7", "#F6ECCE",
76 "#F8F2C7", "#EFEFD0", 0
80 /* Synthesis of data from Crack:: and SandPainter:: */
83 float ys, xs, t_inc; /* for curvature calculations */
87 unsigned long sandcolor;
100 unsigned int initial_cracks;
103 unsigned int max_num;
105 int grains; /* number of grains in the sand painting */
109 crack *cracks; /* grid of cracks */
110 int *cgrid; /* grid of actual crack placement */
112 /* Raw map of pixels we need to keep for alpha blending */
113 unsigned long int *off_img;
117 unsigned long *parsedcolors;
118 unsigned long fgcolor;
119 unsigned long bgcolor;
124 unsigned int wireframe;
132 unsigned int max_cycles;
135 XWindowAttributes xgwa;
141 *xrealloc(void *p, size_t size)
144 if ((ret = realloc(p, size)) == NULL) {
145 fprintf(stderr, "%s: out of memory\n", progname);
154 struct field *f = xrealloc(NULL, sizeof(struct field));
157 f->initial_cracks = 0;
164 f->parsedcolors = NULL;
171 f->circle_percent = 0;
175 /* Quick references to pixels in the offscreen map and in the crack grid */
176 #define ref_pixel(f, x, y) ((f)->off_img[(y) * (f)->width + (x)])
177 #define ref_cgrid(f, x, y) ((f)->cgrid[(y) * (f)->width + (x)])
179 static inline void start_crack(struct field *f, crack *cr)
181 /* synthesis of Crack::findStart() and crack::startCrack() */
188 /* shift until crack is found */
189 while ((!found) && (timeout++ < 10000)) {
190 px = (int) (random() % f->width);
191 py = (int) (random() % f->height);
193 if (ref_cgrid(f, px, py) < 10000)
198 /* We timed out. Use our default values */
201 ref_cgrid(f, px, py) = cr->t;
205 a = ref_cgrid(f, px, py);
207 if ((random() % 100) < 50) {
208 /* conversion of the java int(random(-2, 2.1)) */
209 a -= 90 + (frand(4.1) - 2.0);
211 a += 90 + (frand(4.1) - 2.0);
214 if ((random() % 100) < f->circle_percent) {
215 float r; /* radius */
219 cr->degrees_drawn = 0;
221 r = 10 + (random() % ((f->width + f->height) / 2));
223 if ((random() % 100) < 50) {
227 /* arc length = r * theta => theta = arc length / r */
228 radian_inc = STEP / r;
229 cr->t_inc = radian_inc * 360 / 2 / M_PI;
231 cr->ys = r * sin(radian_inc);
232 cr->xs = r * ( 1 - cos(radian_inc));
239 /* Condensed from Crack::startCrack */
240 cr->x = px + ((float) 0.61 * cos(a * M_PI / 180));
241 cr->y = py + ((float) 0.61 * sin(a * M_PI / 180));
246 static inline void make_crack(struct field *f)
250 if (f->num < f->max_num) {
251 /* make a new crack */
252 f->cracks = (crack *) xrealloc(f->cracks, sizeof(crack) * (f->num + 1));
254 cr = &(f->cracks[f->num]);
257 cr->sandg = (frand(0.2) - 0.01);
258 cr->sandcolor = f->parsedcolors[random() % f->numcolors];
259 cr->crack_num = f->num;
261 cr->degrees_drawn = 0;
263 /* We could use these values in the timeout case of start_crack */
265 cr->x = random() % f->width;
266 cr->y = random() % f->height;
267 cr->t = random() % 360;
276 static inline void point2rgb(int depth, unsigned long c, int *r, int *g, int *b)
282 /* This program idiotically does not go through a color map, so
283 we have to hardcode in knowledge of how jwxyz.a packs pixels!
284 Fix it to go through st->colors[st->ncolors] instead!
286 *r = (c & 0x00ff0000) >> 16;
287 *g = (c & 0x0000ffff) >> 8;
288 *b = (c & 0x000000ff);
290 *g = (c & 0xff00) >> 8;
291 *r = (c & 0xff0000) >> 16;
296 *g = ((c >> 5) & 0x3f) << 2;
297 *r = ((c >> 11) & 0x1f) << 3;
298 *b = (c & 0x1f) << 3;
301 *g = ((c >> 5) & 0x1f) << 3;
302 *r = ((c >> 10) & 0x1f) << 3;
303 *b = (c & 0x1f) << 3;
308 static inline unsigned long rgb2point(int depth, int r, int g, int b)
310 unsigned long ret = 0;
317 /* This program idiotically does not go through a color map, so
318 we have to hardcode in knowledge of how jwxyz.a packs pixels!
319 Fix it to go through st->colors[st->ncolors] instead!
321 ret = 0xFF000000 | (r << 16) | (g << 8) | b;
323 ret |= (r << 16) | (g << 8) | b;
327 ret = ((r>>3) << 11) | ((g>>2)<<5) | (b>>3);
330 ret = ((r>>3) << 10) | ((g>>3)<<5) | (b>>3);
337 /* alpha blended point drawing -- this is Not Right and will likely fail on
338 * non-intel platforms as it is now, needs fixing */
339 static inline unsigned long
340 trans_point(struct state *st,
341 int x1, int y1, unsigned long myc, float a,
344 if ((x1 >= 0) && (x1 < f->width) && (y1 >= 0) && (y1 < f->height)) {
346 ref_pixel(f, x1, y1) = myc;
348 int or = 0, og = 0, ob = 0;
349 int r = 0, g = 0, b = 0;
353 c = ref_pixel(f, x1, y1);
355 point2rgb(f->visdepth, c, &or, &og, &ob);
356 point2rgb(f->visdepth, myc, &r, &g, &b);
358 nr = or + (r - or) * a;
359 ng = og + (g - og) * a;
360 nb = ob + (b - ob) * a;
362 c = rgb2point(f->visdepth, nr, ng, nb);
364 ref_pixel(f, x1, y1) = c;
374 region_color(struct state *st, GC fgc, struct field *f, crack *cr)
376 /* synthesis of Crack::regionColor() and SandPainter::render() */
389 /* move perpendicular to crack */
390 rx += (0.81 * sin(cr->t * M_PI/180));
391 ry -= (0.81 * cos(cr->t * M_PI/180));
396 if ((cx >= 0) && (cx < f->width) && (cy >= 0) && (cy < f->height)) {
398 if (f->cgrid[cy * f->width + cx] > 10000) {
408 /* SandPainter stuff here */
411 cr->sandg += (frand(0.1) - 0.050);
417 if (cr->sandg > maxg)
422 /* Lay down grains of sand */
423 w = cr->sandg / (grains - 1);
425 for (i = 0; i < grains; i++) {
426 drawx = (cr->x + (rx - cr->x) * sin(cr->sandp + sin((float) i * w)));
427 drawy = (cr->y + (ry - cr->y) * sin(cr->sandp + sin((float) i * w)));
430 c = trans_point(st, drawx, drawy, cr->sandcolor, (0.1 - i / (grains * 10.0)), f);
432 XSetForeground(st->dpy, fgc, c);
433 XDrawPoint(st->dpy, st->window, fgc, (int) drawx, (int) drawy);
434 XSetForeground(st->dpy, fgc, f->fgcolor);
438 static void build_substrate(struct field *f)
457 /* erase the crack grid */
458 f->cgrid = (int *) xrealloc(f->cgrid, sizeof(int) * f->height * f->width);
459 memset(f->cgrid, 10001, f->height * f->width * sizeof(int));
461 /* Not necessary now that make_crack ensures we have usable default
462 * values in start_crack's timeout case
463 * make random crack seeds *
464 for (tx = 0; tx < 16; tx++) {
465 ty = (int) (random() % (f->width * f->height - 1));
466 f->cgrid[ty] = (int) random() % 360;
470 /* make the initial cracks */
471 for (tx = 0; tx < f->initial_cracks; tx++)
477 movedrawcrack(struct state *st, GC fgc, struct field *f, int cracknum)
479 /* Basically Crack::move() */
482 crack *cr = &(f->cracks[cracknum]);
484 /* continue cracking */
486 cr->x += ((float) STEP * cos(cr->t * M_PI/180));
487 cr->y += ((float) STEP * sin(cr->t * M_PI/180));
495 cr->x += ((float) cr->ys * cos(cr->t * M_PI/180));
496 cr->y += ((float) cr->ys * sin(cr->t * M_PI/180));
498 cr->x += ((float) cr->xs * cos(cr->t * M_PI/180 - M_PI / 2));
499 cr->x += ((float) cr->xs * sin(cr->t * M_PI/180 - M_PI / 2));
502 cr->degrees_drawn += abs(cr->t_inc);
506 /* modification of random(-0.33,0.33) */
507 cx = (int) (cr->x + (frand(0.66) - 0.33));
508 cy = (int) (cr->y + (frand(0.66) - 0.33));
511 if ((cx >= 0) && (cx < f->width) && (cy >= 0) && (cy < f->height)) {
512 /* draw sand painter if we're not wireframe */
514 region_color(st, fgc, f, cr);
516 /* draw fgcolor crack */
517 ref_pixel(f, cx, cy) = f->fgcolor;
518 XDrawPoint(st->dpy, st->window, fgc, cx, cy);
520 if ( cr->curved && (cr->degrees_drawn > 360) ) {
521 /* completed the circle, stop cracking */
522 start_crack(f, cr); /* restart ourselves */
523 make_crack(f); /* generate a new crack */
526 else if ((f->cgrid[cy * f->width + cx] > 10000) ||
527 (abs(f->cgrid[cy * f->width + cx] - cr->t) < 5)) {
528 /* continue cracking */
529 f->cgrid[cy * f->width + cx] = (int) cr->t;
530 } else if (abs(f->cgrid[cy * f->width + cx] - cr->t) > 2) {
531 /* crack encountered (not self), stop cracking */
532 start_crack(f, cr); /* restart ourselves */
533 make_crack(f); /* generate a new crack */
536 /* out of bounds, stop cracking */
538 /* need these in case of timeout in start_crack */
539 cr->x = random() % f->width;
540 cr->y = random() % f->height;
541 cr->t = random() % 360;
543 start_crack(f, cr); /* restart ourselves */
544 make_crack(f); /* generate a new crack */
550 static void build_img(Display *dpy, Window window, XWindowAttributes xgwa, GC fgc,
558 f->off_img = (unsigned long *) xrealloc(f->off_img, sizeof(unsigned long) *
559 f->width * f->height);
561 memset(f->off_img, f->bgcolor, sizeof(unsigned long) * f->width * f->height);
566 substrate_init (Display *dpy, Window window)
568 struct state *st = (struct state *) calloc (1, sizeof(*st));
573 st->f = init_field();
575 st->growth_delay = (get_integer_resource(st->dpy, "growthDelay", "Integer"));
576 st->max_cycles = (get_integer_resource(st->dpy, "maxCycles", "Integer"));
577 st->f->initial_cracks = (get_integer_resource(st->dpy, "initialCracks", "Integer"));
578 st->f->max_num = (get_integer_resource(st->dpy, "maxCracks", "Integer"));
579 st->f->wireframe = (get_boolean_resource(st->dpy, "wireFrame", "Boolean"));
580 st->f->grains = (get_integer_resource(st->dpy, "sandGrains", "Integer"));
581 st->f->circle_percent = (get_integer_resource(st->dpy, "circlePercent", "Integer"));
583 if (st->f->initial_cracks <= 2) {
584 fprintf(stderr, "%s: Initial cracks must be greater than 2\n", progname);
588 if (st->f->max_num <= 10) {
589 fprintf(stderr, "%s: Maximum number of cracks must be less than 10\n",
594 if (st->f->circle_percent < 0) {
595 fprintf(stderr, "%s: circle percent must be at least 0\n", progname);
599 if (st->f->circle_percent > 100) {
600 fprintf(stderr, "%s: circle percent must be less than 100\n", progname);
604 XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
606 st->f->height = st->xgwa.height;
607 st->f->width = st->xgwa.width;
608 st->f->visdepth = st->xgwa.depth;
610 /* Count the colors in our map and assign them in a horrifically inefficient
611 * manner but it only happens once */
612 while (rgb_colormap[st->f->numcolors] != NULL) {
613 st->f->parsedcolors = (unsigned long *) xrealloc(st->f->parsedcolors,
614 sizeof(unsigned long) *
615 (st->f->numcolors + 1));
616 if (!XParseColor(st->dpy, st->xgwa.colormap, rgb_colormap[st->f->numcolors], &tmpcolor)) {
617 fprintf(stderr, "%s: couldn't parse color %s\n", progname,
618 rgb_colormap[st->f->numcolors]);
622 if (!XAllocColor(st->dpy, st->xgwa.colormap, &tmpcolor)) {
623 fprintf(stderr, "%s: couldn't allocate color %s\n", progname,
624 rgb_colormap[st->f->numcolors]);
628 st->f->parsedcolors[st->f->numcolors] = tmpcolor.pixel;
633 st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
634 "foreground", "Foreground");
635 st->gcv.background = get_pixel_resource(st->dpy, st->xgwa.colormap,
636 "background", "Background");
637 st->fgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
639 st->f->fgcolor = st->gcv.foreground;
640 st->f->bgcolor = st->gcv.background;
642 /* Initialize stuff */
643 build_img(st->dpy, st->window, st->xgwa, st->fgc, st->f);
644 build_substrate(st->f);
650 substrate_draw (Display *dpy, Window window, void *closure)
652 struct state *st = (struct state *) closure;
655 if ((st->f->cycles % 10) == 0) {
657 /* Restart if the window size changes */
658 XGetWindowAttributes(st->dpy, st->window, &st->xgwa);
660 if (st->f->height != st->xgwa.height || st->f->width != st->xgwa.width) {
661 st->f->height = st->xgwa.height;
662 st->f->width = st->xgwa.width;
663 st->f->visdepth = st->xgwa.depth;
665 build_substrate(st->f);
666 build_img(st->dpy, st->window, st->xgwa, st->fgc, st->f);
667 XSetForeground(st->dpy, st->fgc, st->gcv.background);
668 XFillRectangle(st->dpy, st->window, st->fgc, 0, 0, st->xgwa.width, st->xgwa.height);
669 XSetForeground(st->dpy, st->fgc, st->gcv.foreground);
673 for (tempx = 0; tempx < st->f->num; tempx++) {
674 movedrawcrack(st, st->fgc, st->f, tempx);
679 if (st->f->cycles >= st->max_cycles && st->max_cycles != 0) {
680 build_substrate(st->f);
681 build_img(st->dpy, st->window, st->xgwa, st->fgc, st->f);
682 XSetForeground(st->dpy, st->fgc, st->gcv.background);
683 XFillRectangle(st->dpy, st->window, st->fgc, 0, 0, st->xgwa.width, st->xgwa.height);
684 XSetForeground(st->dpy, st->fgc, st->gcv.foreground);
687 return st->growth_delay;
692 substrate_reshape (Display *dpy, Window window, void *closure,
693 unsigned int w, unsigned int h)
698 substrate_event (Display *dpy, Window window, void *closure, XEvent *event)
704 substrate_free (Display *dpy, Window window, void *closure)
706 struct state *st = (struct state *) closure;
710 static const char *substrate_defaults[] = {
711 ".background: white",
712 ".foreground: black",
716 "*growthDelay: 18000",
720 "*circlePercent: 33",
724 static XrmOptionDescRec substrate_options[] = {
725 {"-background", ".background", XrmoptionSepArg, 0},
726 {"-foreground", ".foreground", XrmoptionSepArg, 0},
727 {"-wireframe", ".wireFrame", XrmoptionNoArg, "true"},
728 {"-max-cycles", ".maxCycles", XrmoptionSepArg, 0},
729 {"-growth-delay", ".growthDelay", XrmoptionSepArg, 0},
730 {"-initial-cracks", ".initialCracks", XrmoptionSepArg, 0},
731 {"-max-cracks", ".maxCracks", XrmoptionSepArg, 0},
732 {"-sand-grains", ".sandGrains", XrmoptionSepArg, 0},
733 {"-circle-percent", ".circlePercent", XrmoptionSepArg, 0},
737 XSCREENSAVER_MODULE ("Substrate", substrate)