From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / hacks / kaleidescope.c
1 /* kaleidescope, Copyright (c) 1997, 2006 Ron Tapia <tapia@nmia.com>
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
12 /*
13  * The above, for lack of a better copyright statement in easy reach
14  * was just lifted from the xscreensaver source.
15  *
16  * One of the odd things about this hack is that the radial motion of the
17  * segments depends on roundoff error alone. 
18  *
19  * I tried to make the source easy to add other shapes. So far, I've
20  * only messed with elipses and I couldn't do much with them that looked
21  * cool. A nice addition would be to add some sort of spline based shapes.
22  * Maybe rectangles would look nice.
23  *
24  */
25
26 #include <stdio.h>
27 #include <math.h>
28 #include <time.h>
29 #include "screenhack.h"
30 #include "spline.h"
31
32 #define NEWX(x,y) ((x*g->costheta) + (y*g->sintheta))
33 #define NEWY(x,y) ((y*g->costheta) - (x*g->sintheta))
34
35
36 typedef struct state GLOBAL;
37 typedef struct Obj OBJECT;
38 struct Obj {
39   int type;
40   int time;
41   void (*propigate) (GLOBAL *, OBJECT *);
42   void (*draw) (GLOBAL *, OBJECT *);
43   void (*init) (GLOBAL *, OBJECT *);
44   void *cur;
45 };
46
47 typedef struct KSEGMENT {
48   struct KSEGMENT *next;
49   XColor color;
50   int drawn;
51   short int x1,y1,x2,y2;  /* these are in the natural coordinate system */
52   XSegment  *xsegments;  /* these are in the X coordinate system */
53 } Ksegment;
54
55 struct state {
56   int  xoff, yoff;                    /* offset of origin xmax/2, ymax/2 */
57   int  xmax, ymax;                    /* width, height of window */
58   float costheta, sintheta;           
59   int symmetry;                       
60   int ntrails;          
61   int nsegments;
62   int narcs;
63   int nobjects;
64   int local_rotation;
65   int global_rotation;
66   int spring_constant;
67   Colormap cmap;
68   GC  draw_gc;
69   GC  erase_gc;
70   unsigned int default_fg_pixel;
71   Display *dpy;
72   Window window;
73   unsigned long delay;
74   unsigned short redmin,redrange,greenmin,greenrange,bluemin,bluerange;
75   int color_mode;
76
77   OBJECT *objects;
78   int counter;
79
80   int done_once;
81 };
82
83
84 static const char *kaleidescope_defaults [] = {
85   ".background:      black",
86   ".foreground:      white",
87   "*fpsSolid:        true",
88   "*color_mode:      nice",
89   "*symmetry:          11",
90   "*ntrails:          100",
91   "*nsegments:          7",
92   "*narcs:              0",
93   "*local_rotation:   -59",
94   "*global_rotation:    1",
95   "*spring_constant:    5",
96   "*delay:          20000",
97   "*redmin:         30000",
98   "*redrange:       20000",
99   "*greenmin:       30000",
100   "*greenrange:     20000",
101   "*bluemin:        30000",
102   "*bluerange:      20000",
103 #ifdef USE_IPHONE
104   "*ignoreRotation: True",
105 #endif
106   0
107 };
108
109 static XrmOptionDescRec kaleidescope_options [] = {
110   { "-color_mode",       ".color_mode",     XrmoptionSepArg, 0 },
111   { "-symmetry",        ".symmetry",        XrmoptionSepArg, 0 },
112   { "-nsegments",       ".nsegments",       XrmoptionSepArg, 0 },
113   { "-ntrails",         ".ntrails",         XrmoptionSepArg, 0 },
114   { "-local_rotation",  ".local_rotation",  XrmoptionSepArg, 0 },
115   { "-global_rotation", ".global_rotation", XrmoptionSepArg, 0 },
116   { "-delay",           ".delay",           XrmoptionSepArg, 0 },
117   { "-spring_constant", ".spring_constant", XrmoptionSepArg, 0 },
118   { "-redmin",          ".redmin",          XrmoptionSepArg, 0 },
119   { "-redrange",        ".redrange",        XrmoptionSepArg, 0 },
120   { "-bluemin",         ".bluemin",         XrmoptionSepArg, 0 },
121   { "-bluerange",       ".bluerange",       XrmoptionSepArg, 0 },
122   { "-greenmin",        ".greenmin",        XrmoptionSepArg, 0 },
123   { "-greenrange",      ".greenrange",      XrmoptionSepArg, 0 },
124   { 0, 0, 0, 0 }
125 };
126
127 /* END global variables */
128
129 static void
130 krandom_color(GLOBAL *g, XColor *color)
131 {
132   if((g->color_mode == 0) || (g->color_mode == 1)) {
133
134     color->blue  = (random() % g->bluerange)  + g->bluemin;
135     color->green = (random() % g->greenrange) + g->greenmin;
136     color->red   = (random() % g->redrange)   + g->redmin;
137
138     if(!XAllocColor(g->dpy, g->cmap, color)) {
139       color->pixel = g->default_fg_pixel;
140     } 
141     return;
142   } else {
143     color->pixel = g->default_fg_pixel;
144     return;
145   }
146 }
147
148
149 static void
150 kcopy_color(XColor *to, XColor *from)
151 {
152   to->red   = from->red;
153   to->green = from->green;
154   to->blue  = from->blue;
155   to->pixel = from->pixel;
156 }
157
158 static void
159 kcycle_color(GLOBAL *g, 
160              XColor *color,
161              unsigned short redstep,
162              unsigned short greenstep,
163              unsigned short bluestep) 
164 {
165   unsigned short red,green,blue;
166
167   if (! g->color_mode) {
168     XColor copy;
169     color->flags = DoRed|DoGreen|DoBlue;
170     color->red   = (red = color->red) - redstep;
171     color->green = (green = color->green) - greenstep;
172     color->blue  = (blue = color->blue)  - bluestep;
173     copy = *color;
174
175     if(!XAllocColor(g->dpy, g->cmap, color)) {
176       /* printf("couldn't alloc color...\n"); */
177       color->pixel = g->default_fg_pixel;
178     }
179     copy.pixel = color->pixel;
180     *color = copy;
181
182     color->red   = red   - redstep;
183     color->green = green- greenstep;
184     color->blue  = blue  - bluestep;
185     return;
186   } 
187 }
188
189
190 static Ksegment *
191 create_ksegment (GLOBAL *g)
192 {
193   Ksegment *seg, *prev;
194   XColor new_color;
195   int i;
196   unsigned short redstep,bluestep,greenstep;
197
198   krandom_color(g, &new_color);
199
200   redstep = new_color.red/(2 * g->ntrails);
201   greenstep = new_color.green/(2 * g->ntrails);
202   bluestep = new_color.blue/(2 * g->ntrails);
203
204   seg            = (Ksegment *) malloc(sizeof(Ksegment));
205   seg->xsegments = (XSegment  *) malloc(g->symmetry * sizeof(XSegment));
206
207   prev = seg;
208   for(i=0; i< (g->ntrails - 1); i++) {
209
210     kcycle_color(g, &new_color,redstep,greenstep,bluestep);
211
212     kcopy_color(&(prev->color), &new_color);
213
214     prev->next              = (Ksegment*)malloc(sizeof(Ksegment));
215     (prev->next)->xsegments = (XSegment*)malloc(g->symmetry * sizeof(XSegment));
216     prev->drawn             = 0;
217     prev = (prev->next);
218   } 
219
220   prev->drawn = 0;
221   prev->next = seg;
222   kcopy_color(&(prev->color), &new_color);
223
224   return seg;
225 }
226
227 static void
228 init_ksegment (GLOBAL *g, OBJECT *obj)
229 {
230
231   /* Give the segment some random values */
232   ((Ksegment *)obj->cur)->x1 = (g->xoff ? random() % g->xoff : 0);
233   ((Ksegment *)obj->cur)->y1 = (g->yoff ? random() % g->yoff : 0);
234   ((Ksegment *)obj->cur)->x2 = (g->xoff ? random() % g->xoff : 0);
235   ((Ksegment *)obj->cur)->y2 = (g->yoff ? random() % g->yoff : 0);
236 }
237
238
239 static void
240 draw_ksegment (GLOBAL *g, OBJECT *obj)
241 {
242   register short x1, y1, x2, y2;
243   int dx, dy;
244   int i;
245
246   g->counter++;
247
248   x1 = ((Ksegment *)obj->cur)->x1;   /* in the natural coordinate system */
249   y1 = ((Ksegment *)obj->cur)->y1;
250   x2 = ((Ksegment *)obj->cur)->x2;
251   y2 = ((Ksegment *)obj->cur)->y2;
252
253   dx = x2 - x1;
254   dy = y2 - y1;
255
256   /* maybe throw away values and start over */
257   if( ((dx*dx) + (dy * dy)) < 100) {
258     init_ksegment (g, obj);
259     x1 = ((Ksegment *)obj->cur)->x1;   /* in the natural coordinate system */
260     y1 = ((Ksegment *)obj->cur)->y1;
261     x2 = ((Ksegment *)obj->cur)->x2;
262     y2 = ((Ksegment *)obj->cur)->y2;
263   }
264
265   for (i=0; i<g->symmetry; i++) {
266     (((Ksegment *)obj->cur)->xsegments)[i].x1 = NEWX(x1,y1);
267     (((Ksegment *)obj->cur)->xsegments)[i].y1 = NEWY(x1,y1);
268     (((Ksegment *)obj->cur)->xsegments)[i].x2 = NEWX(x2,y2);
269     (((Ksegment *)obj->cur)->xsegments)[i].y2 = NEWY(x2,y2);
270
271     (((Ksegment *)obj->cur)->xsegments)[i].x1 = (x1 = (((Ksegment *)obj->cur)->xsegments)[i].x1) + g->xoff; 
272     (((Ksegment *)obj->cur)->xsegments)[i].y1 = (y1 = (((Ksegment *)obj->cur)->xsegments)[i].y1) + g->yoff;
273     (((Ksegment *)obj->cur)->xsegments)[i].x2 = (x2 = (((Ksegment *)obj->cur)->xsegments)[i].x2) + g->xoff;
274     (((Ksegment *)obj->cur)->xsegments)[i].y2 = (y2 = (((Ksegment *)obj->cur)->xsegments)[i].y2) + g->yoff;
275   }
276
277   XSetForeground(g->dpy, g->draw_gc, (((Ksegment *)obj->cur)->color).pixel);
278
279   XDrawSegments(g->dpy, g->window, g->draw_gc, ((Ksegment *)obj->cur)->xsegments, g->symmetry);
280   ((Ksegment *)obj->cur)->drawn = 1;
281
282   if (((((Ksegment *)obj->cur)->next)->drawn) != 0) {
283     XDrawSegments(g->dpy, g->window, g->erase_gc, ((Ksegment *)obj->cur)->next->xsegments, g->symmetry);
284   }
285 }
286
287 static void
288 propigate_ksegment(GLOBAL *g, OBJECT *obj)
289 {
290   int t;
291   short int x1,y1,x2,y2;
292   short int midx,midy,nmidx,nmidy;
293   float lsin, lcos, gsin, gcos;
294
295   lsin = sin((2*M_PI/10000)*g->local_rotation);
296   lcos = cos((2*M_PI/10000)*g->local_rotation);
297   gsin = sin((2*M_PI/10000)*g->global_rotation);
298   gcos = cos((2*M_PI/10000)*g->global_rotation);
299
300   t=obj->time;
301   obj->time = t + 1;
302
303   x1 = ((Ksegment *) obj->cur)->x1;
304   y1 = ((Ksegment *) obj->cur)->y1;
305   x2 = ((Ksegment *) obj->cur)->x2;
306   y2 = ((Ksegment *) obj->cur)->y2;
307
308   midx = (x1 + x2)/2;
309   midy = (y1 + y2)/2;
310
311   nmidx = midx*gcos + midy*gsin;
312   nmidy = midy*gcos - midx*gsin;
313
314   x1 = x1 - midx;
315   x2 = x2 - midx;
316   y1 = y1 - midy;
317   y2 = y2 - midy;
318
319
320   /* This is where we move to the next ksegment... */
321   obj->cur = ((Ksegment *)obj->cur)->next;
322
323   ((Ksegment *)obj->cur)->x1 = ((x1*lcos) + (y1*lsin)) + nmidx;
324   ((Ksegment *)obj->cur)->y1 = ((y1*lcos) - (x1*lsin)) + nmidy;
325   ((Ksegment *)obj->cur)->x2 = ((x2*lcos) + (y2*lsin)) + nmidx;
326   ((Ksegment *)obj->cur)->y2 = ((y2*lcos) - (x2*lsin)) + nmidy;
327
328   return ;
329 }
330
331 static void
332 init_objects (GLOBAL *g)
333 {
334   int i;
335   for (i=0; i<g->nobjects; i++) {
336     (g->objects[i].init)(g, g->objects + i);
337   }
338 }
339
340 static void
341 create_objects (GLOBAL *g)
342 {
343   int i;
344
345   g->objects = (OBJECT *) malloc(g->nobjects * sizeof(OBJECT));
346
347   for (i=0; i< g->nsegments; i++) {
348     g->objects[i].cur = create_ksegment(g);
349     g->objects[i].type = 1;
350     g->objects[i].time = 0;
351     g->objects[i].propigate = propigate_ksegment;
352     g->objects[i].draw      = draw_ksegment;
353     g->objects[i].init      = init_ksegment;
354   }
355
356   /* Here we can add creation functions for other object types. */
357 }
358
359
360 static void
361 propigate_objects (GLOBAL *g)
362 {
363   int i;
364
365   for(i=0; i<g->nobjects; i++) {
366     g->objects[i].propigate(g, g->objects + i);
367   }
368 }
369
370 static void
371 draw_objects (GLOBAL *g)
372 {
373   int i;
374
375   for(i=0; i<g->nobjects; i++) {
376     g->objects[i].draw(g, g->objects + i);
377   }
378 }
379
380 static void
381 init_g (GLOBAL *g)
382 {
383   XWindowAttributes xgwa;
384   XGCValues gcv;
385   char *color_mode_str;
386
387   g->symmetry        = get_integer_resource(g->dpy, "symmetry",         "Integer");
388   g->ntrails         = get_integer_resource(g->dpy, "ntrails"  ,        "Integer");
389   g->nsegments       = get_integer_resource(g->dpy, "nsegments",        "Integer");
390   g->narcs           = get_integer_resource(g->dpy, "narcs",            "Integer");
391   g->local_rotation  = get_integer_resource(g->dpy, "local_rotation",   "Integer");
392   g->global_rotation = get_integer_resource(g->dpy, "global_rotation",  "Integer");
393   g->spring_constant = get_integer_resource(g->dpy, "spring_constant", "Integer");
394   g->delay           = get_integer_resource(g->dpy, "delay", "Integer");
395   g->nobjects        = g->nsegments + g->narcs;
396
397   color_mode_str = get_string_resource(g->dpy, "color_mode", "color_mode");
398
399   /* make into an enum... */
400   if(!color_mode_str) {
401     g->color_mode = 0;
402   } else if (!strcmp(color_mode_str, "greedy")) {
403     g->color_mode = 0;
404   } else if (!strcmp(color_mode_str, "nice")) {
405     g->color_mode = 1;
406   } else {
407     g->color_mode = 2;
408   }
409
410   XGetWindowAttributes (g->dpy, g->window, &xgwa);
411   g->xmax     = xgwa.width;
412   g->ymax     = xgwa.height;  
413   g->xoff     = g->xmax/2;
414   g->yoff     = g->ymax/2;
415   g->costheta = cos(2*M_PI/g->symmetry);
416   g->sintheta  = sin(2*M_PI/g->symmetry);
417   g->cmap     = xgwa.colormap;
418
419   g->redmin     = get_integer_resource(g->dpy, "redmin",     "Integer");
420   g->redrange   = get_integer_resource(g->dpy, "redrange",   "Integer");
421   g->greenmin   = get_integer_resource(g->dpy, "greenmin",   "Integer");
422   g->greenrange = get_integer_resource(g->dpy, "greenrange", "Integer");
423   g->bluemin    = get_integer_resource(g->dpy, "bluemin",    "Integer");
424   g->bluerange  = get_integer_resource(g->dpy, "bluerange",  "Integer");
425
426   gcv.line_width = 1;
427   gcv.cap_style  = CapRound;
428   gcv.foreground = g->default_fg_pixel = get_pixel_resource (g->dpy, g->cmap, "foreground", "Foreground");
429   g->draw_gc      = XCreateGC (g->dpy, g->window, GCForeground|GCLineWidth|GCCapStyle, &gcv);
430
431   gcv.foreground = get_pixel_resource (g->dpy, g->cmap, "background", "Background");
432   g->erase_gc     = XCreateGC (g->dpy, g->window, GCForeground|GCLineWidth|GCCapStyle,&gcv);
433
434 # ifdef HAVE_COCOA
435   jwxyz_XSetAntiAliasing (g->dpy, g->draw_gc, False);
436   jwxyz_XSetAntiAliasing (g->dpy, g->erase_gc, False);
437 # endif
438
439
440 }
441
442 static void *
443 kaleidescope_init (Display *dpy, Window window)
444 {
445   GLOBAL *g = (GLOBAL *) calloc (1, sizeof(*g));
446   g->dpy = dpy;
447   g->window = window;
448   init_g (g);
449   create_objects(g);
450   init_objects (g);
451   return g;
452 }
453
454 static unsigned long
455 kaleidescope_draw (Display *dpy, Window window, void *closure)
456 {
457   GLOBAL *g = (GLOBAL *) closure;
458   if (g->done_once)
459     propigate_objects(g); 
460   else
461     g->done_once = 1;
462   draw_objects (g);
463   return g->delay;
464 }
465
466 static void
467 kaleidescope_reshape (Display *dpy, Window window, void *closure, 
468                  unsigned int w, unsigned int h)
469 {
470   GLOBAL *g = (GLOBAL *) closure;
471   g->xmax = w;
472   g->ymax = h;
473   g->xoff = g->xmax/2;
474   g->yoff = g->ymax/2;
475 }
476
477 static Bool
478 kaleidescope_event (Display *dpy, Window window, void *closure, XEvent *event)
479 {
480   return False;
481 }
482
483 static void
484 kaleidescope_free (Display *dpy, Window window, void *closure)
485 {
486   GLOBAL *g = (GLOBAL *) closure;
487   free (g);
488 }
489
490 XSCREENSAVER_MODULE ("Kaleidescope", kaleidescope)