16836543b7b2e97a809610f7b7ea27cc7f77c745
[xscreensaver] / hacks / truchet.c
1 /* truchet --- curved and straight tilings
2  * Copyright (c) 1998 Adrian Likins <adrian@gimp.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* This screensaver draws two varieties of truchet patterns, a curved one and
14    a straight one. There are lots and lots of command line options to play
15    with.
16
17    If your running remotely or on a slow machine or slow xserver, some of the
18    settings will be way too much. The default settings should be okay though. 
19
20    This screensaver doesnt use anything bizarre or special at all, just a few
21    standard xlib calls.
22
23    A few suggested commandline combos..All these were tested on a k6-200
24    running XFree86 3.3 on a ark2000, so your mileage may vary...
25
26       truchet -delay 200 -no-curves
27       truchet -delay 500 -no-curves -square -no-erase
28       truchet -delay 500 -no-erase -square -erase-count 5
29       truchet -scroll
30       truchet -scroll -no-erase -anim-step-size 9
31       truchet -delay 200 -no-angles -min-width 36 -max-width 36
32       truchet -delay 200 -no-curves -min-width 12 -max-width 12
33       truchet -delay 200 -no-curves -min-width 36 -max-width 36 -no-erase
34       truchet -delay 100 -min-width 256 -max-width 512 -no-erase \
35               -min-linewidth 96 -root
36       truchet -min-width 64 -max-width 128 -no-erase -max-linewidth 4 \
37               -root -no-angles
38       truchet -min-width 64 -max-width 128 -no-erase -max-linewidth 4 \
39               -root -no-curves -delay 25
40  */
41
42 #include "screenhack.h"
43
44 #define MAXRATIO 2
45
46 static const char *truchet_defaults [] = {
47   "*minWidth:                 40",
48   "*minHeight:                40",
49   "*max-Width:                150",
50   "*max-Height:               150",
51   "*maxLineWidth:             25",
52   "*minLineWidth:             2",
53   "*erase:                    True",
54   "*eraseCount:               25",        
55   "*square:                   True",
56   "*delay:                    400000",
57   "*curves:                   True",
58   "*angles:                   True",
59   "*scroll:                   False",
60   "*scroll-overlap:           400",
61   "*anim-delay:               100",
62   "*anim-step-size:           3",
63   "*randomize:                true",
64    0
65 };
66
67 /* options passed to this program */
68 static XrmOptionDescRec truchet_options [] = {
69   { "-min-width",      ".minWidth",       XrmoptionSepArg, 0 },
70   { "-max-height",     ".max-Height",      XrmoptionSepArg, 0 },
71   { "-max-width",      ".max-Width",       XrmoptionSepArg, 0 },
72   { "-min-height",     ".minHeight",      XrmoptionSepArg, 0 },
73   { "-max-linewidth",  ".maxLineWidth",   XrmoptionSepArg, 0 },
74   { "-min-linewidth",  ".minLineWidth",   XrmoptionSepArg, 0 },
75   { "-erase",          ".erase",          XrmoptionNoArg, "True" },
76   { "-no-erase",       ".erase",          XrmoptionNoArg, "False" },
77   { "-erase-count",    ".eraseCount",     XrmoptionSepArg, 0 },
78   { "-square",         ".square",         XrmoptionNoArg, "True" },
79   { "-not-square",     ".square",         XrmoptionNoArg, "False" },
80   { "-curves",         ".curves",         XrmoptionNoArg, "True" },
81   { "-angles",         ".angles",         XrmoptionNoArg,  "True" },
82   { "-no-angles",      ".angles",         XrmoptionNoArg,  "False" },
83   { "-no-curves",      ".curves",         XrmoptionNoArg, "False" },
84   { "-delay",          ".delay",          XrmoptionSepArg, 0 },
85   { "-scroll",         ".scroll",         XrmoptionNoArg, "True" },
86   { "-scroll-overlap", ".scroll-overlap", XrmoptionSepArg, 0 },
87   { "-anim-delay",     ".anim-delay",     XrmoptionSepArg, 0 },
88   { "-anim-step-size",  ".anim-step-size", XrmoptionSepArg, 0 },
89   { "-randomize",       ".randomize",     XrmoptionNoArg, "True" },
90   { 0, 0, 0, 0 }
91 };
92
93 struct state {
94   Display *dpy;
95   Window window;
96
97   XGCValues gcv;
98   GC agc, bgc;
99   int linewidth;
100   int width, height;
101   XWindowAttributes xgwa;
102   Pixmap frame;
103   int overlap;
104
105   int maxlinewidth;
106   int minlinewidth;
107   int minwidth;
108   int minheight;
109   int max_height; 
110   int max_width; 
111   int delay;
112   int count;
113   int anim_delay;
114   int anim_step_size;
115
116   Colormap cmap;
117   XColor fgc;
118   Bool curves;
119   Bool square;
120   Bool angles;
121   Bool erase;
122   Bool eraseCount;
123   Bool scroll;
124   int scrolling;
125 };
126
127 static void draw_truchet(struct state *st);
128 static void draw_angles(struct state *st);
129 static void scroll_area(struct state *st, int step_size);
130
131 static void draw_angles(struct state *st)
132 {
133   int cx = 0, cy = 0;
134   
135   while((st->xgwa.height+st->overlap) > cy*st->height)
136         {
137           while((st->xgwa.width+st->overlap) > cx*st->width)
138             {
139               if(random()%2)
140               {
141                 /* block1 */
142                 XDrawLine(st->dpy,st->frame,st->agc,
143                           (cx*st->width)+(st->width/2),
144                           (cy*st->height), 
145                           (cx*st->width)+(st->width),
146                           (cy*st->height)+(st->height/2));
147                 XDrawLine(st->dpy,st->frame,st->agc,
148                           (cx*st->width), 
149                           (cy*st->height)+(st->height/2),
150                           (cx*st->width)+(st->width/2),
151                           (cy*st->height)+(st->height));
152               }
153             else
154               {
155                 /* block 2 */
156                 XDrawLine(st->dpy,st->frame,st->agc, 
157                           (cx*st->width)+(st->width/2),
158                           (cy*st->height),
159                           (cx*st->width),
160                           (cy*st->height)+(st->height/2));
161                 XDrawLine(st->dpy,st->frame,st->agc,
162                           (cx*st->width)+(st->width),
163                           (cy*st->height)+(st->height/2),
164                           (cx*st->width)+(st->width/2),
165                           (cy*st->height)+(st->height)); 
166               }
167               cx++;
168             }
169           cy++;
170           cx=0;
171         }
172 }
173   
174
175 static void draw_truchet(struct state *st)
176 {
177   int cx = 0, cy = 0;
178
179   while(st->xgwa.height+st->overlap > cy*st->height)
180         {
181           while(st->xgwa.width+st->overlap > cx*st->width)
182             {
183               if(random()%2)
184               {
185                 /* block1 */
186                 XDrawArc(st->dpy, st->frame, st->agc,
187                          ((cx*st->width)-(st->width/2)),
188                          ((cy*st->height)-(st->height/2)),
189                          st->width,
190                          st->height,
191                          0, -5760);
192                 XDrawArc(st->dpy,st->frame, st->agc,
193                          ((cx*st->width)+(st->width/2)),
194                          ((cy*st->height)+(st->height/2)),
195                          st->width,
196                          st->height,
197                          11520,
198                          -5760);
199               }
200             else
201               {
202                 /* block 2 */
203                 XDrawArc(st->dpy,st->frame,st->agc,
204                          ((cx*st->width)+(st->width/2)),
205                          ((cy*st->height)-(st->height/2)),
206                          st->width,
207                          st->height,
208                          17280,
209                          -5760);
210                 XDrawArc(st->dpy,st->frame,st->agc,
211                          ((cx*st->width)-(st->width/2)),
212                          ((cy*st->height)+(st->height/2)),
213                          st->width,
214                          st->height,
215                          0,
216                          5760);
217               }
218               cx++;
219             }
220           cy++;
221           cx=0;
222         }
223 }
224
225
226 static void scroll_area(struct state *st, int step_size)
227 {
228   int scrollcount_x;
229   int scrollcount_y;
230   int offset;
231   int scroll;
232   int direction;
233   int progress;
234
235   offset=st->overlap/2;
236   scroll=st->overlap/4;
237   
238   /* This runs in a loop, starting with
239    *  st->scrolling = (scroll / st->anim_step_size) * 4 - 1;
240    * and going all the way down to st->scrolling = 0.
241    */
242
243   /* if anyone knows a good way to generate
244    * a more random scrolling motion... */
245
246   direction = st->scrolling / (scroll / st->anim_step_size);
247   progress = (st->scrolling % (scroll / st->anim_step_size)) * st->anim_step_size;
248
249   if (direction & 1) {
250     scrollcount_x = progress - scroll;
251     scrollcount_y = progress;
252   } else {
253     scrollcount_x = -progress;
254     scrollcount_y = progress - scroll;
255   }
256
257   if (direction & 2) {
258     scrollcount_x = -scrollcount_x;
259     scrollcount_y = -scrollcount_y;
260   }
261
262   XCopyArea(st->dpy, st->frame, st->window, st->agc,scrollcount_x+offset,scrollcount_y+offset, st->xgwa.width, st->xgwa.height, 0,0);
263 }
264
265
266 static void *
267 truchet_init (Display *dpy, Window window)
268 {
269   struct state *st = (struct state *) calloc (1, sizeof(*st));
270
271   st->dpy = dpy;
272   st->window = window;
273
274   st->maxlinewidth = get_integer_resource (st->dpy, "maxLineWidth", "Integer");
275   st->minlinewidth = get_integer_resource (st->dpy, "minLineWidth", "Integer");
276   st->minwidth = get_integer_resource (st->dpy, "minWidth", "Integer");
277   st->minheight = get_integer_resource (st->dpy, "minHeight", "Integer");
278   st->max_width = get_integer_resource (st->dpy, "max-Width", "Integer"); 
279   st->max_height = get_integer_resource (st->dpy, "max-Height", "Integer" ); 
280   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
281   st->eraseCount = get_integer_resource (st->dpy, "eraseCount", "Integer");
282   st->square = get_boolean_resource (st->dpy, "square", "Boolean");
283   st->curves = get_boolean_resource (st->dpy, "curves", "Boolean");
284   st->angles = get_boolean_resource (st->dpy, "angles", "Boolean");
285   st->erase = get_boolean_resource (st->dpy, "erase", "Boolean");
286   st->scroll = get_boolean_resource (st->dpy, "scroll", "Boolean");
287   st->overlap = get_integer_resource (st->dpy, "scroll-overlap", "Integer");
288   st->anim_delay = get_integer_resource (st->dpy, "anim-delay", "Integer");
289   st->anim_step_size = get_integer_resource (st->dpy, "anim-step-size", "Integer");
290
291   if (get_boolean_resource(st->dpy, "randomize", "Randomize"))
292     {
293       int i = (random() % 12);
294       switch(i) {
295       case 0:
296         break;
297       case 1:
298         st->curves = False;
299         break;
300       case 2:
301         st->curves = False;
302         st->square = True;
303         st->erase = False;
304         break;
305       case 3:
306         st->square = True;
307         st->erase = False;
308         st->eraseCount = 5;
309         break;
310       case 4:
311         st->scroll = True;
312         break;
313       case 5:
314         st->scroll = True;
315         st->erase = False;
316         st->anim_step_size = 9;
317         break;
318       case 6:
319         st->angles = False;
320         st->minwidth = st->max_width = 36;
321         break;
322       case 7:
323         st->curves = False;
324         st->minwidth = st->max_width = 12;
325         break;
326       case 8:
327         st->curves = False;
328         st->erase = False;
329         st->minwidth = st->max_width = 36;
330         break;
331       case 9:
332         st->erase = False;
333         st->minwidth = 256;
334         st->max_width = 512;
335         st->minlinewidth = 96;
336         break;
337       case 10:
338         st->angles = False;
339         st->minwidth = 64;
340         st->max_width = 128;
341         st->maxlinewidth = 4;
342         break;
343       case 11:
344         st->curves = False;
345         st->minwidth = 64;
346         st->max_width = 128;
347         st->maxlinewidth = 4;
348         break;
349       default:
350         abort();
351         break;
352       }
353     }
354
355   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
356   st->gcv.foreground = BlackPixel(st->dpy,0);
357   st->gcv.background = WhitePixel(st->dpy,0);
358   st->gcv.line_width = 25;
359   st->cmap = st->xgwa.colormap;
360
361   st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap,
362                                       "background", "Background");
363
364   st->bgc = XCreateGC (st->dpy, st->window, GCForeground, &st->gcv);
365   st->agc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv);
366
367   XFillRectangle(st->dpy, st->window, st->bgc, 0, 0, st->xgwa.width, st->xgwa.height);
368
369  
370   st->width=60;
371   st->height=60;
372   st->linewidth=1;
373   st->count=0;
374   XSetForeground(st->dpy, st->agc, st->gcv.background);
375   
376   
377   st->frame = XCreatePixmap(st->dpy,st->window, st->xgwa.width+st->overlap, st->xgwa.height+st->overlap, st->xgwa.depth); 
378   XFillRectangle(st->dpy, st->frame, st->bgc, 0, 0, 
379                  st->xgwa.width + st->overlap, 
380                  st->xgwa.height + st->overlap);
381   
382   return st;
383 }
384
385 static unsigned long
386 truchet_draw (Display *dpy, Window window, void *closure)
387 {
388   struct state *st = (struct state *) closure;
389
390   if (st->scrolling)
391     {
392       st->scrolling--;
393       scroll_area(st, st->anim_step_size);
394       return st->anim_delay*1000;
395     }
396
397   if (!mono_p)
398     {
399       /* XXX there are probably bugs with this. */
400       /* could be...I just borrowed this code from munch */
401
402       st->fgc.red = random() % 65535;
403       st->fgc.green = random() % 65535;
404       st->fgc.blue = random() % 65535;
405         
406       if (XAllocColor(st->dpy, st->cmap, &st->fgc)) 
407         {
408           XSetForeground(st->dpy, st->agc, st->fgc.pixel);
409         }
410       else
411         {
412           /* use white if all else fails  */
413           XSetForeground(st->dpy,st->agc, st->gcv.background);
414         }
415     }
416
417       
418       
419
420   /* generate a random line width */
421   st->linewidth=(random()% st->maxlinewidth);
422
423   /* check for lower bound */
424   if(st->linewidth < st->minlinewidth)
425     st->linewidth = st->minlinewidth;
426
427   /* try to get an odd linewidth as it seem to work a little better */
428   if(st->linewidth%2)
429     st->linewidth++;
430
431   /* grab a random height and width */ 
432   st->width=(random()%st->max_width);
433   st->height=(random()%st->max_height);
434
435   /* make sure we dont get a 0 height or width */
436   if(st->width == 0 || st->height == 0)
437     {
438       st->height=st->max_height;
439       st->width=st->max_width;
440     }
441
442
443   /* check for min height and width */
444   if(st->height < st->minheight)
445     {
446       st->height=st->minheight;
447     }
448   if(st->width < st->minwidth)
449     {
450       st->width=st->minwidth;
451     }
452
453   /* if tiles need to be square, fix it... */
454   if(st->square)
455     st->height=st->width;
456
457   /* check for sane aspect ratios */
458   if((st->width/st->height) > MAXRATIO) 
459     st->height=st->width;
460   if((st->height/st->width) > MAXRATIO)
461     st->width=st->height;
462       
463   /* to avoid linewidths of zero */
464   if(st->linewidth == 0 || st->linewidth < st->minlinewidth)
465     st->linewidth = st->minlinewidth;
466
467   /* try to keep from getting line widths that would be too big */
468   if(st->linewidth > 0 && st->linewidth >= (st->height/5))
469     st->linewidth = st->height/5;
470   
471   XSetLineAttributes(st->dpy, st->agc, st->linewidth, LineSolid, CapRound, JoinRound);
472
473   if(st->erase || (st->count >= st->eraseCount))
474     {
475       /*  XClearWindow(dpy,window); */
476       XFillRectangle(st->dpy, st->frame, st->bgc, 0, 0, st->xgwa.width+st->overlap, st->xgwa.height+st->overlap);
477       st->count=0;
478     }
479             
480   if(!st->scroll)
481     st->overlap=0;
482             
483   /* do the fun stuff...*/
484   if(st->curves && st->angles)
485     {
486       if(random()%2)
487         draw_truchet(st);
488       else
489         draw_angles(st);
490     }
491   else if(st->curves && !st->angles)
492     draw_truchet(st);
493   else if(!st->curves && st->angles)
494     draw_angles(st);
495
496    
497   st->count++;
498
499   if(st->scroll)
500     {
501       st->scrolling = ((st->overlap / 4) / st->anim_step_size) * 4;
502       return 0;
503     }
504
505   XCopyArea(st->dpy,st->frame,st->window,st->agc,0,0,st->xgwa.width,st->xgwa.height,0,0);
506
507   return st->delay;
508 }
509
510 static void
511 truchet_reshape (Display *dpy, Window window, void *closure, 
512                  unsigned int w, unsigned int h)
513 {
514   struct state *st = (struct state *) closure;
515   st->width = w;
516   st->height = h;
517   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
518 }
519
520 static Bool
521 truchet_event (Display *dpy, Window window, void *closure, XEvent *event)
522 {
523   return False;
524 }
525
526 static void
527 truchet_free (Display *dpy, Window window, void *closure)
528 {
529 }
530
531
532 XSCREENSAVER_MODULE ("Truchet", truchet)
533