ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-5.01.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   "*color_mode:      nice",
88   "*symmetry:          11",
89   "*ntrails:          100",
90   "*nsegments:          7",
91   "*narcs:              0",
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 static XrmOptionDescRec kaleidescope_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",        ".redrange",        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(GLOBAL *g, 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(GLOBAL *g, 
159              XColor *color,
160              unsigned short redstep,
161              unsigned short greenstep,
162              unsigned short bluestep) 
163 {
164   unsigned short red,green,blue;
165
166   if (! g->color_mode) {
167     XColor copy;
168     color->flags = DoRed|DoGreen|DoBlue;
169     color->red   = (red = color->red) - redstep;
170     color->green = (green = color->green) - greenstep;
171     color->blue  = (blue = color->blue)  - bluestep;
172     copy = *color;
173
174     if(!XAllocColor(g->dpy, g->cmap, color)) {
175       /* printf("couldn't alloc color...\n"); */
176       color->pixel = g->default_fg_pixel;
177     }
178     copy.pixel = color->pixel;
179     *color = copy;
180
181     color->red   = red   - redstep;
182     color->green = green- greenstep;
183     color->blue  = blue  - bluestep;
184     return;
185   } 
186 }
187
188
189 static Ksegment *
190 create_ksegment (GLOBAL *g)
191 {
192   Ksegment *seg, *prev;
193   XColor new_color;
194   int i;
195   unsigned short redstep,bluestep,greenstep;
196
197   krandom_color(g, &new_color);
198
199   redstep = new_color.red/(2 * g->ntrails);
200   greenstep = new_color.green/(2 * g->ntrails);
201   bluestep = new_color.blue/(2 * g->ntrails);
202
203   seg            = (Ksegment *) malloc(sizeof(Ksegment));
204   seg->xsegments = (XSegment  *) malloc(g->symmetry * sizeof(XSegment));
205
206   prev = seg;
207   for(i=0; i< (g->ntrails - 1); i++) {
208
209     kcycle_color(g, &new_color,redstep,greenstep,bluestep);
210
211     kcopy_color(&(prev->color), &new_color);
212
213     prev->next              = (Ksegment*)malloc(sizeof(Ksegment));
214     (prev->next)->xsegments = (XSegment*)malloc(g->symmetry * sizeof(XSegment));
215     prev->drawn             = 0;
216     prev = (prev->next);
217   } 
218
219   prev->drawn = 0;
220   prev->next = seg;
221   kcopy_color(&(prev->color), &new_color);
222
223   return seg;
224 }
225
226 static void
227 init_ksegment (GLOBAL *g, OBJECT *obj)
228 {
229
230   /* Give the segment some random values */
231   ((Ksegment *)obj->cur)->x1 = (g->xoff ? random() % g->xoff : 0);
232   ((Ksegment *)obj->cur)->y1 = (g->yoff ? random() % g->yoff : 0);
233   ((Ksegment *)obj->cur)->x2 = (g->xoff ? random() % g->xoff : 0);
234   ((Ksegment *)obj->cur)->y2 = (g->yoff ? random() % g->yoff : 0);
235 }
236
237
238 static void
239 draw_ksegment (GLOBAL *g, OBJECT *obj)
240 {
241   register short x1, y1, x2, y2;
242   int dx, dy;
243   int i;
244
245   g->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 (g, 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 propigate_ksegment(GLOBAL *g, 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 (GLOBAL *g)
332 {
333   int i;
334   for (i=0; i<g->nobjects; i++) {
335     (g->objects[i].init)(g, g->objects + i);
336   }
337 }
338
339 static void
340 create_objects (GLOBAL *g)
341 {
342   int i;
343
344   g->objects = (OBJECT *) malloc(g->nobjects * sizeof(OBJECT));
345
346   for (i=0; i< g->nsegments; i++) {
347     g->objects[i].cur = create_ksegment(g);
348     g->objects[i].type = 1;
349     g->objects[i].time = 0;
350     g->objects[i].propigate = propigate_ksegment;
351     g->objects[i].draw      = draw_ksegment;
352     g->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 propigate_objects (GLOBAL *g)
361 {
362   int i;
363
364   for(i=0; i<g->nobjects; i++) {
365     g->objects[i].propigate(g, g->objects + i);
366   }
367 }
368
369 static void
370 draw_objects (GLOBAL *g)
371 {
372   int i;
373
374   for(i=0; i<g->nobjects; i++) {
375     g->objects[i].draw(g, g->objects + i);
376   }
377 }
378
379 static void
380 init_g (GLOBAL *g)
381 {
382   XWindowAttributes xgwa;
383   XGCValues gcv;
384   char *color_mode_str;
385
386   g->symmetry        = get_integer_resource(g->dpy, "symmetry",         "Integer");
387   g->ntrails         = get_integer_resource(g->dpy, "ntrails"  ,        "Integer");
388   g->nsegments       = get_integer_resource(g->dpy, "nsegments",        "Integer");
389   g->narcs           = get_integer_resource(g->dpy, "narcs",            "Integer");
390   g->local_rotation  = get_integer_resource(g->dpy, "local_rotation",   "Integer");
391   g->global_rotation = get_integer_resource(g->dpy, "global_rotation",  "Integer");
392   g->spring_constant = get_integer_resource(g->dpy, "spring_constant", "Integer");
393   g->delay           = get_integer_resource(g->dpy, "delay", "Integer");
394   g->nobjects        = g->nsegments + g->narcs;
395
396   color_mode_str = get_string_resource(g->dpy, "color_mode", "color_mode");
397
398   /* make into an enum... */
399   if(!color_mode_str) {
400     g->color_mode = 0;
401   } else if (!strcmp(color_mode_str, "greedy")) {
402     g->color_mode = 0;
403   } else if (!strcmp(color_mode_str, "nice")) {
404     g->color_mode = 1;
405   } else {
406     g->color_mode = 2;
407   }
408
409   XGetWindowAttributes (g->dpy, g->window, &xgwa);
410   g->xmax     = xgwa.width;
411   g->ymax     = xgwa.height;  
412   g->xoff     = g->xmax/2;
413   g->yoff     = g->ymax/2;
414   g->costheta = cos(2*M_PI/g->symmetry);
415   g->sintheta  = sin(2*M_PI/g->symmetry);
416   g->cmap     = xgwa.colormap;
417
418   g->redmin     = get_integer_resource(g->dpy, "redmin",     "Integer");
419   g->redrange   = get_integer_resource(g->dpy, "redrange",   "Integer");
420   g->greenmin   = get_integer_resource(g->dpy, "greenmin",   "Integer");
421   g->greenrange = get_integer_resource(g->dpy, "greenrange", "Integer");
422   g->bluemin    = get_integer_resource(g->dpy, "bluemin",    "Integer");
423   g->bluerange  = get_integer_resource(g->dpy, "bluerange",  "Integer");
424
425   gcv.line_width = 1;
426   gcv.cap_style  = CapRound;
427   gcv.foreground = g->default_fg_pixel = get_pixel_resource (g->dpy, g->cmap, "foreground", "Foreground");
428   g->draw_gc      = XCreateGC (g->dpy, g->window, GCForeground|GCLineWidth|GCCapStyle, &gcv);
429
430   gcv.foreground = get_pixel_resource (g->dpy, g->cmap, "background", "Background");
431   g->erase_gc     = XCreateGC (g->dpy, g->window, GCForeground|GCLineWidth|GCCapStyle,&gcv);
432
433 # ifdef HAVE_COCOA
434   jwxyz_XSetAntiAliasing (g->dpy, g->draw_gc, False);
435   jwxyz_XSetAntiAliasing (g->dpy, g->erase_gc, False);
436 # endif
437
438
439 }
440
441 static void *
442 kaleidescope_init (Display *dpy, Window window)
443 {
444   GLOBAL *g = (GLOBAL *) calloc (1, sizeof(*g));
445   g->dpy = dpy;
446   g->window = window;
447   init_g (g);
448   create_objects(g);
449   init_objects (g);
450   return g;
451 }
452
453 static unsigned long
454 kaleidescope_draw (Display *dpy, Window window, void *closure)
455 {
456   GLOBAL *g = (GLOBAL *) closure;
457   if (g->done_once)
458     propigate_objects(g); 
459   else
460     g->done_once = 1;
461   draw_objects (g);
462   return g->delay;
463 }
464
465 static void
466 kaleidescope_reshape (Display *dpy, Window window, void *closure, 
467                  unsigned int w, unsigned int h)
468 {
469   GLOBAL *g = (GLOBAL *) closure;
470   g->xmax = w;
471   g->ymax = h;
472   g->xoff = g->xmax/2;
473   g->yoff = g->ymax/2;
474 }
475
476 static Bool
477 kaleidescope_event (Display *dpy, Window window, void *closure, XEvent *event)
478 {
479   return False;
480 }
481
482 static void
483 kaleidescope_free (Display *dpy, Window window, void *closure)
484 {
485   GLOBAL *g = (GLOBAL *) closure;
486   free (g);
487 }
488
489 XSCREENSAVER_MODULE ("Kaleidescope", kaleidescope)