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