8c3f80b978ae9b903f51a55ed07a90268d2835ec
[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   0
104 };
105
106 static XrmOptionDescRec kaleidescope_options [] = {
107   { "-color_mode",       ".color_mode",     XrmoptionSepArg, 0 },
108   { "-symmetry",        ".symmetry",        XrmoptionSepArg, 0 },
109   { "-nsegments",       ".nsegments",       XrmoptionSepArg, 0 },
110   { "-ntrails",         ".ntrails",         XrmoptionSepArg, 0 },
111   { "-local_rotation",  ".local_rotation",  XrmoptionSepArg, 0 },
112   { "-global_rotation", ".global_rotation", XrmoptionSepArg, 0 },
113   { "-delay",           ".delay",           XrmoptionSepArg, 0 },
114   { "-spring_constant", ".spring_constant", XrmoptionSepArg, 0 },
115   { "-redmin",          ".redmin",          XrmoptionSepArg, 0 },
116   { "-redrange",        ".redrange",        XrmoptionSepArg, 0 },
117   { "-bluemin",         ".bluemin",         XrmoptionSepArg, 0 },
118   { "-bluerange",       ".bluerange",       XrmoptionSepArg, 0 },
119   { "-greenmin",        ".greenmin",        XrmoptionSepArg, 0 },
120   { "-greenrange",      ".greenrange",      XrmoptionSepArg, 0 },
121   { 0, 0, 0, 0 }
122 };
123
124 /* END global variables */
125
126 static void
127 krandom_color(GLOBAL *g, XColor *color)
128 {
129   if((g->color_mode == 0) || (g->color_mode == 1)) {
130
131     color->blue  = (random() % g->bluerange)  + g->bluemin;
132     color->green = (random() % g->greenrange) + g->greenmin;
133     color->red   = (random() % g->redrange)   + g->redmin;
134
135     if(!XAllocColor(g->dpy, g->cmap, color)) {
136       color->pixel = g->default_fg_pixel;
137     } 
138     return;
139   } else {
140     color->pixel = g->default_fg_pixel;
141     return;
142   }
143 }
144
145
146 static void
147 kcopy_color(XColor *to, XColor *from)
148 {
149   to->red   = from->red;
150   to->green = from->green;
151   to->blue  = from->blue;
152   to->pixel = from->pixel;
153 }
154
155 static void
156 kcycle_color(GLOBAL *g, 
157              XColor *color,
158              unsigned short redstep,
159              unsigned short greenstep,
160              unsigned short bluestep) 
161 {
162   unsigned short red,green,blue;
163
164   if (! g->color_mode) {
165     XColor copy;
166     color->flags = DoRed|DoGreen|DoBlue;
167     color->red   = (red = color->red) - redstep;
168     color->green = (green = color->green) - greenstep;
169     color->blue  = (blue = color->blue)  - bluestep;
170     copy = *color;
171
172     if(!XAllocColor(g->dpy, g->cmap, color)) {
173       /* printf("couldn't alloc color...\n"); */
174       color->pixel = g->default_fg_pixel;
175     }
176     copy.pixel = color->pixel;
177     *color = copy;
178
179     color->red   = red   - redstep;
180     color->green = green- greenstep;
181     color->blue  = blue  - bluestep;
182     return;
183   } 
184 }
185
186
187 static Ksegment *
188 create_ksegment (GLOBAL *g)
189 {
190   Ksegment *seg, *prev;
191   XColor new_color;
192   int i;
193   unsigned short redstep,bluestep,greenstep;
194
195   krandom_color(g, &new_color);
196
197   redstep = new_color.red/(2 * g->ntrails);
198   greenstep = new_color.green/(2 * g->ntrails);
199   bluestep = new_color.blue/(2 * g->ntrails);
200
201   seg            = (Ksegment *) malloc(sizeof(Ksegment));
202   seg->xsegments = (XSegment  *) malloc(g->symmetry * sizeof(XSegment));
203
204   prev = seg;
205   for(i=0; i< (g->ntrails - 1); i++) {
206
207     kcycle_color(g, &new_color,redstep,greenstep,bluestep);
208
209     kcopy_color(&(prev->color), &new_color);
210
211     prev->next              = (Ksegment*)malloc(sizeof(Ksegment));
212     (prev->next)->xsegments = (XSegment*)malloc(g->symmetry * sizeof(XSegment));
213     prev->drawn             = 0;
214     prev = (prev->next);
215   } 
216
217   prev->drawn = 0;
218   prev->next = seg;
219   kcopy_color(&(prev->color), &new_color);
220
221   return seg;
222 }
223
224 static void
225 init_ksegment (GLOBAL *g, OBJECT *obj)
226 {
227
228   /* Give the segment some random values */
229   ((Ksegment *)obj->cur)->x1 = (g->xoff ? random() % g->xoff : 0);
230   ((Ksegment *)obj->cur)->y1 = (g->yoff ? random() % g->yoff : 0);
231   ((Ksegment *)obj->cur)->x2 = (g->xoff ? random() % g->xoff : 0);
232   ((Ksegment *)obj->cur)->y2 = (g->yoff ? random() % g->yoff : 0);
233 }
234
235
236 static void
237 draw_ksegment (GLOBAL *g, OBJECT *obj)
238 {
239   register short x1, y1, x2, y2;
240   int dx, dy;
241   int i;
242
243   g->counter++;
244
245   x1 = ((Ksegment *)obj->cur)->x1;   /* in the natural coordinate system */
246   y1 = ((Ksegment *)obj->cur)->y1;
247   x2 = ((Ksegment *)obj->cur)->x2;
248   y2 = ((Ksegment *)obj->cur)->y2;
249
250   dx = x2 - x1;
251   dy = y2 - y1;
252
253   /* maybe throw away values and start over */
254   if( ((dx*dx) + (dy * dy)) < 100) {
255     init_ksegment (g, obj);
256     x1 = ((Ksegment *)obj->cur)->x1;   /* in the natural coordinate system */
257     y1 = ((Ksegment *)obj->cur)->y1;
258     x2 = ((Ksegment *)obj->cur)->x2;
259     y2 = ((Ksegment *)obj->cur)->y2;
260   }
261
262   for (i=0; i<g->symmetry; i++) {
263     (((Ksegment *)obj->cur)->xsegments)[i].x1 = NEWX(x1,y1);
264     (((Ksegment *)obj->cur)->xsegments)[i].y1 = NEWY(x1,y1);
265     (((Ksegment *)obj->cur)->xsegments)[i].x2 = NEWX(x2,y2);
266     (((Ksegment *)obj->cur)->xsegments)[i].y2 = NEWY(x2,y2);
267
268     (((Ksegment *)obj->cur)->xsegments)[i].x1 = (x1 = (((Ksegment *)obj->cur)->xsegments)[i].x1) + g->xoff; 
269     (((Ksegment *)obj->cur)->xsegments)[i].y1 = (y1 = (((Ksegment *)obj->cur)->xsegments)[i].y1) + g->yoff;
270     (((Ksegment *)obj->cur)->xsegments)[i].x2 = (x2 = (((Ksegment *)obj->cur)->xsegments)[i].x2) + g->xoff;
271     (((Ksegment *)obj->cur)->xsegments)[i].y2 = (y2 = (((Ksegment *)obj->cur)->xsegments)[i].y2) + g->yoff;
272   }
273
274   XSetForeground(g->dpy, g->draw_gc, (((Ksegment *)obj->cur)->color).pixel);
275
276   XDrawSegments(g->dpy, g->window, g->draw_gc, ((Ksegment *)obj->cur)->xsegments, g->symmetry);
277   ((Ksegment *)obj->cur)->drawn = 1;
278
279   if (((((Ksegment *)obj->cur)->next)->drawn) != 0) {
280     XDrawSegments(g->dpy, g->window, g->erase_gc, ((Ksegment *)obj->cur)->next->xsegments, g->symmetry);
281   }
282 }
283
284 static void
285 propigate_ksegment(GLOBAL *g, OBJECT *obj)
286 {
287   int t;
288   short int x1,y1,x2,y2;
289   short int midx,midy,nmidx,nmidy;
290   float lsin, lcos, gsin, gcos;
291
292   lsin = sin((2*M_PI/10000)*g->local_rotation);
293   lcos = cos((2*M_PI/10000)*g->local_rotation);
294   gsin = sin((2*M_PI/10000)*g->global_rotation);
295   gcos = cos((2*M_PI/10000)*g->global_rotation);
296
297   t=obj->time;
298   obj->time = t + 1;
299
300   x1 = ((Ksegment *) obj->cur)->x1;
301   y1 = ((Ksegment *) obj->cur)->y1;
302   x2 = ((Ksegment *) obj->cur)->x2;
303   y2 = ((Ksegment *) obj->cur)->y2;
304
305   midx = (x1 + x2)/2;
306   midy = (y1 + y2)/2;
307
308   nmidx = midx*gcos + midy*gsin;
309   nmidy = midy*gcos - midx*gsin;
310
311   x1 = x1 - midx;
312   x2 = x2 - midx;
313   y1 = y1 - midy;
314   y2 = y2 - midy;
315
316
317   /* This is where we move to the next ksegment... */
318   obj->cur = ((Ksegment *)obj->cur)->next;
319
320   ((Ksegment *)obj->cur)->x1 = ((x1*lcos) + (y1*lsin)) + nmidx;
321   ((Ksegment *)obj->cur)->y1 = ((y1*lcos) - (x1*lsin)) + nmidy;
322   ((Ksegment *)obj->cur)->x2 = ((x2*lcos) + (y2*lsin)) + nmidx;
323   ((Ksegment *)obj->cur)->y2 = ((y2*lcos) - (x2*lsin)) + nmidy;
324
325   return ;
326 }
327
328 static void
329 init_objects (GLOBAL *g)
330 {
331   int i;
332   for (i=0; i<g->nobjects; i++) {
333     (g->objects[i].init)(g, g->objects + i);
334   }
335 }
336
337 static void
338 create_objects (GLOBAL *g)
339 {
340   int i;
341
342   g->objects = (OBJECT *) malloc(g->nobjects * sizeof(OBJECT));
343
344   for (i=0; i< g->nsegments; i++) {
345     g->objects[i].cur = create_ksegment(g);
346     g->objects[i].type = 1;
347     g->objects[i].time = 0;
348     g->objects[i].propigate = propigate_ksegment;
349     g->objects[i].draw      = draw_ksegment;
350     g->objects[i].init      = init_ksegment;
351   }
352
353   /* Here we can add creation functions for other object types. */
354 }
355
356
357 static void
358 propigate_objects (GLOBAL *g)
359 {
360   int i;
361
362   for(i=0; i<g->nobjects; i++) {
363     g->objects[i].propigate(g, g->objects + i);
364   }
365 }
366
367 static void
368 draw_objects (GLOBAL *g)
369 {
370   int i;
371
372   for(i=0; i<g->nobjects; i++) {
373     g->objects[i].draw(g, g->objects + i);
374   }
375 }
376
377 static void
378 init_g (GLOBAL *g)
379 {
380   XWindowAttributes xgwa;
381   XGCValues gcv;
382   char *color_mode_str;
383
384   g->symmetry        = get_integer_resource(g->dpy, "symmetry",         "Integer");
385   g->ntrails         = get_integer_resource(g->dpy, "ntrails"  ,        "Integer");
386   g->nsegments       = get_integer_resource(g->dpy, "nsegments",        "Integer");
387   g->narcs           = get_integer_resource(g->dpy, "narcs",            "Integer");
388   g->local_rotation  = get_integer_resource(g->dpy, "local_rotation",   "Integer");
389   g->global_rotation = get_integer_resource(g->dpy, "global_rotation",  "Integer");
390   g->spring_constant = get_integer_resource(g->dpy, "spring_constant", "Integer");
391   g->delay           = get_integer_resource(g->dpy, "delay", "Integer");
392   g->nobjects        = g->nsegments + g->narcs;
393
394   color_mode_str = get_string_resource(g->dpy, "color_mode", "color_mode");
395
396   /* make into an enum... */
397   if(!color_mode_str) {
398     g->color_mode = 0;
399   } else if (!strcmp(color_mode_str, "greedy")) {
400     g->color_mode = 0;
401   } else if (!strcmp(color_mode_str, "nice")) {
402     g->color_mode = 1;
403   } else {
404     g->color_mode = 2;
405   }
406
407   XGetWindowAttributes (g->dpy, g->window, &xgwa);
408   g->xmax     = xgwa.width;
409   g->ymax     = xgwa.height;  
410   g->xoff     = g->xmax/2;
411   g->yoff     = g->ymax/2;
412   g->costheta = cos(2*M_PI/g->symmetry);
413   g->sintheta  = sin(2*M_PI/g->symmetry);
414   g->cmap     = xgwa.colormap;
415
416   g->redmin     = get_integer_resource(g->dpy, "redmin",     "Integer");
417   g->redrange   = get_integer_resource(g->dpy, "redrange",   "Integer");
418   g->greenmin   = get_integer_resource(g->dpy, "greenmin",   "Integer");
419   g->greenrange = get_integer_resource(g->dpy, "greenrange", "Integer");
420   g->bluemin    = get_integer_resource(g->dpy, "bluemin",    "Integer");
421   g->bluerange  = get_integer_resource(g->dpy, "bluerange",  "Integer");
422
423   gcv.line_width = 1;
424   gcv.cap_style  = CapRound;
425   gcv.foreground = g->default_fg_pixel = get_pixel_resource (g->dpy, g->cmap, "foreground", "Foreground");
426   g->draw_gc      = XCreateGC (g->dpy, g->window, GCForeground|GCLineWidth|GCCapStyle, &gcv);
427
428   gcv.foreground = get_pixel_resource (g->dpy, g->cmap, "background", "Background");
429   g->erase_gc     = XCreateGC (g->dpy, g->window, GCForeground|GCLineWidth|GCCapStyle,&gcv);
430
431 # ifdef HAVE_COCOA
432   jwxyz_XSetAntiAliasing (g->dpy, g->draw_gc, False);
433   jwxyz_XSetAntiAliasing (g->dpy, g->erase_gc, False);
434 # endif
435
436
437 }
438
439 static void *
440 kaleidescope_init (Display *dpy, Window window)
441 {
442   GLOBAL *g = (GLOBAL *) calloc (1, sizeof(*g));
443   g->dpy = dpy;
444   g->window = window;
445   init_g (g);
446   create_objects(g);
447   init_objects (g);
448   return g;
449 }
450
451 static unsigned long
452 kaleidescope_draw (Display *dpy, Window window, void *closure)
453 {
454   GLOBAL *g = (GLOBAL *) closure;
455   if (g->done_once)
456     propigate_objects(g); 
457   else
458     g->done_once = 1;
459   draw_objects (g);
460   return g->delay;
461 }
462
463 static void
464 kaleidescope_reshape (Display *dpy, Window window, void *closure, 
465                  unsigned int w, unsigned int h)
466 {
467   GLOBAL *g = (GLOBAL *) closure;
468   g->xmax = w;
469   g->ymax = h;
470   g->xoff = g->xmax/2;
471   g->yoff = g->ymax/2;
472 }
473
474 static Bool
475 kaleidescope_event (Display *dpy, Window window, void *closure, XEvent *event)
476 {
477   return False;
478 }
479
480 static void
481 kaleidescope_free (Display *dpy, Window window, void *closure)
482 {
483   GLOBAL *g = (GLOBAL *) closure;
484   free (g);
485 }
486
487 XSCREENSAVER_MODULE ("Kaleidescope", kaleidescope)