http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[xscreensaver] / hacks / abstractile.c
1 /* 
2  * Copyright (c) 2004-2009 Steve Sundstrom
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 #include "screenhack.h"
14 #include "colors.h"
15 #include "hsv.h"
16 #include <stdio.h>
17 #include <math.h>
18 #include <sys/time.h>
19 /*#include <sys/utsname.h>*/
20
21 #define DEBUGFILE       "/tmp/abstractile.dbg"
22
23 #define MODE_CREATE             0   /* init, create, then finish sleep */
24 #define MODE_ERASE              1   /* erase, then reset colors */
25 #define MODE_DRAW               2
26
27 #define DIR_NONE                0
28 #define DIR_UP                  1
29 #define DIR_DOWN                2
30 #define DIR_LEFT                3
31 #define DIR_RIGHT               4
32
33 #define LINE_FORCE              1
34 #define LINE_NEW                2
35 #define LINE_BRIN               3
36 #define LINE_BROUT              4
37
38 #define PT_UL                   0
39 #define PT_MP                   1
40 #define PT_LR                   2
41 #define PT_NL                   3
42
43 #define D3D_NONE                0
44 #define D3D_BLOCK               1 
45 #define D3D_NEON                2
46 #define D3D_TILED               3
47
48 #define TILE_RANDOM             0
49 #define TILE_FLAT               1
50 #define TILE_THIN               2
51 #define TILE_OUTLINE            3
52 #define TILE_BLOCK              4
53 #define TILE_NEON               5
54 #define TILE_TILED              6
55
56 #define BASECOLORS              30
57 #define SHADES                  12
58 #define MAXCOLORS               40
59 #define LAYERS                  4
60 #define PATTERNS                40
61 #define SHAPES                  18
62 #define DRAWORDERS              40
63 #define COLORMAPS               20
64 #define WAVES                   6
65 #define STRETCHES               8
66
67 const double PI = 3.1415926535;
68
69 struct lineStruct {
70     unsigned int x, y, len, obj, color, ndol;
71     int deo;
72     Bool hv;
73 };
74
75 struct gridStruct {
76     unsigned int line, hl, hr, vu, vd, dhl, dhr, dvu, dvd;
77 };
78
79 /* basically the same as global variables, but used to keep them in a bucket 
80    and pass them around easier like the original C++ implementation */
81 struct state {
82   /* window values */
83   Display *display;
84   Window window;
85   XWindowAttributes xgwa;
86   GC fgc, bgc;
87   XColor colors[255];
88
89   /* memory values */
90   struct lineStruct *dline, *eline;
91   struct gridStruct *grid;
92   unsigned int *zlist, *fdol;
93   Bool *odi;
94     /* draw, erase, fill, init, line, object, z indexes */
95   unsigned int di, ei, fi, ii, bi, li, eli, oi, zi;
96   /* size variables */
97   unsigned int gridx, gridy, gridn;                     /* grid size */
98   int lwid, bwid, swid;/* line width, background width, shadow width */
99   int narray, max_wxh;
100   int elwid, elpu, egridx, egridy; /* for now */
101   /* fill variables */
102   int bnratio;                 /* ratio of branch lines to new lines */
103   int maxlen;                              /* maximum length of line */
104   int forcemax;                  /* make line be max possible length */
105   int olen;                           /* open length set by findopen */
106   int bln;          /* blocking line number set by findopen, -1=edge */
107   /* color variables */
108   int ncolors;                        /* number of colors for screen */
109   int shades;
110   int rco[MAXCOLORS];           /* random ordering of colors for deo */
111   int cmap;
112   int layers;
113   Bool newcols;      /* can we create new colormaps with each screen */
114   /* draw variables */
115   int dmap, emap;  /* pattern by which line draw order is determined */
116   int dvar, evar;             /* random number added to .deo to vary */
117   int ddir, edir;      /* draw/erase in forward direction or reverse */
118   int lpu;            /* lines drawn per update used to adjust speed */
119   int d3d;
120   int round;
121   int outline;
122   /* layered draw variables */
123   int pattern[LAYERS], shape[LAYERS], mix[LAYERS]; 
124   int csw[LAYERS], wsx[LAYERS], wsy[LAYERS], sec[LAYERS]; 
125   int cs1[LAYERS], cs2[LAYERS], cs3[LAYERS]; int cs4[LAYERS]; 
126   int wave[LAYERS], waveh[LAYERS], wavel[LAYERS]; 
127   int rx1[LAYERS], rx2[LAYERS], rx3[LAYERS];
128   int ry1[LAYERS], ry2[LAYERS], ry3[LAYERS];
129   /* misc variables */
130   int mode, sleep, speed, tile, dialog;
131   Bool grid_full, resized;
132   struct timeval time;
133 };
134
135 static int 
136 _min(int a, int b) 
137 {
138   if (a<=b)
139     return(a);
140   return(b);
141 }
142
143 static int 
144 _max(int a, int b) 
145 {
146   if (a>=b)
147     return(a);
148   return(b);
149 }
150
151 static int 
152 _dist(struct state *st, int x1, int x2, int y1, int y2, int s) 
153 {
154   double xd=x1-x2;
155   double yd=y1-y2;
156   switch(s) {
157     case 0:
158       return((int)sqrt(xd*xd+yd*yd));
159     case 1:
160       return((int)sqrt(xd*xd*st->cs1[0]*2+yd*yd));
161     case 2:
162       return((int)sqrt(xd*xd+yd*yd*st->cs2[0]*2));
163     default:
164       return((int)sqrt(xd*xd*st->cs1[0]/st->cs2[0]+yd*yd*st->cs3[0]/st->cs4[0]));
165   }
166 }
167
168 static int 
169 _wave(struct state *st, int x, int h, int l, int wave) 
170 {
171   l+=1; 
172   switch(wave) {
173     case 0:                                         /* cos wave*/
174       return((int)(cos((double)x*PI/l)*h));
175     case 1:                                      /* double wave*/
176     case 2:                                      /* double wave*/
177       return((int)(cos((double)x*PI/l)*h)+(int)(sin((double)x*PI/l/st->cs1[1])*h));
178     case 3:                                         /* zig zag */
179       return(abs((x%(l*2)-l))*h/l);
180     case 4:                                   /* giant zig zag */
181       return(abs((x%(l*4)-l*2))*h*3/l);
182     case 5:                                        /* sawtooth */
183       return((x%(l))*h/l); 
184     default:                                        /* no wave */
185       return(0);
186   } 
187 }
188
189 static int 
190 _triangle(struct state *st, int x, int y, int rx, int ry, int t) 
191 {
192   switch(t) {
193     case 1:
194       return(_min(_min(x+y+rx-(st->gridx/2),st->gridx-x+y),(st->gridy-y+(ry/2))*3/2));
195     case 2:
196       return(_min(_min(x-rx,y-ry),(rx+ry-x-y)*2/3)); 
197     case 3:
198       return(_min(_min(st->gridx-x-rx,y-ry),(rx+ry-st->gridx+x-y)*2/3));
199     case 4:
200       return(_min(_min(x-rx,st->gridy-y-ry),(rx+ry-x-st->gridy+y)*2/3));
201   } 
202   return(_min(_min(st->gridx-x-rx,st->gridy-y-ry),(rx+ry-st->gridx+x-st->gridy+y)*2/3));
203 }
204
205 static void 
206 _init_zlist(struct state *st) 
207 {
208   unsigned int tmp, y, z;
209
210   st->gridx=st->xgwa.width/st->lwid;
211   st->gridy=st->xgwa.height/st->lwid;
212   st->gridn=st->gridx*st->gridy;
213   /* clear grid */
214   for (z=0; z<st->gridn; z++) {
215     st->grid[z].line=st->grid[z].hl=st->grid[z].hr=st->grid[z].vu=st->grid[z].vd=st->grid[z].dhl=st->grid[z].dhr=st->grid[z].dvu=st->grid[z].dvd=0;
216     st->zlist[z]=z;
217   }
218   /* rather than pull x,y points randomly and wait to hit final empy cells a 
219      list of all points is created and mixed so empty cells do get hit last */
220   for (z=0; z<st->gridn; z++) {
221     y=random()%st->gridn;
222     tmp=st->zlist[y];
223     st->zlist[y]=st->zlist[z];
224     st->zlist[z]=tmp;
225   }
226 }
227
228 static void
229 make_color_ramp_rgb (Display *dpy, Colormap cmap,
230     int r1, int g1, int b1,  int r2, int g2, int b2,
231     XColor *colors, int *ncolorsP,
232     Bool closed_p, Bool allocate_p, Bool writable_p)
233 {
234     int h1, h2;
235     double s1, s2, v1, v2;
236     rgb_to_hsv(r1, g1, b1, &h1, &s1, &v1);
237     rgb_to_hsv(r2, g2, b2, &h2, &s2, &v2);
238     make_color_ramp(dpy, cmap, h1, s1, v1, h2, s2, v2,
239         colors, ncolorsP, False, allocate_p, writable_p);
240 }
241
242
243 static void 
244 _init_colors(struct state *st)
245 {
246   int col[BASECOLORS];
247   int c1, c2, c3, h1, h2, h3; 
248   int r1, g1, b1, r2, g2, b2, r3, g3, b3;
249   double s1, s2, s3, v1, v2, v3;
250   XColor tmp_col1[16], tmp_col2[16], tmp_col3[16];
251
252   unsigned short basecol[BASECOLORS][3]={
253     /* 0  dgray */    {0x3333,0x3333,0x3333},
254     /* 1  dbrown */   {0x6666,0x3333,0x0000},
255     /* 2  dred */     {0x9999,0x0000,0x0000},
256     /* 3  orange */   {0xFFFF,0x6666,0x0000},
257     /* 4  gold */     {0xFFFF,0xCCCC,0x0000},
258     /* 5  olive */    {0x6666,0x6666,0x0000},
259     /* 6  ivy */      {0x0000,0x6666,0x0000},
260     /* 7  dgreen */   {0x0000,0x9999,0x0000},
261     /* 8  bluegray */ {0x3333,0x6666,0x6666},
262     /* 9  dblue */    {0x0000,0x0000,0x9999},
263     /* 10 blue */     {0x3333,0x3333,0xFFFF},
264     /* 11 dpurple */  {0x6666,0x0000,0xCCCC},
265     /* 12 purple */   {0x6666,0x3333,0xFFFF},
266     /* 13 violet */   {0x9999,0x3333,0x9999},
267     /* 14 magenta */  {0xCCCC,0x3333,0xCCCC},
268     /* lights */
269     /* 15 gray */     {0x3333,0x3333,0x3333},
270     /* 16 brown */    {0x9999,0x6666,0x3333},
271     /* 17 tan */      {0xCCCC,0x9999,0x3333},
272     /* 18 red */      {0xFFFF,0x0000,0x0000},
273     /* 19 lorange */  {0xFFFF,0x9999,0x0000},
274     /* 20 yellow */   {0xFFFF,0xFFFF,0x0000},
275     /* 21 lolive */   {0x9999,0x9999,0x0000},
276     /* 22 green */    {0x3333,0xCCCC,0x0000},
277     /* 23 lgreen */   {0x3333,0xFFFF,0x3333},
278     /* 24 cyan */     {0x0000,0xCCCC,0xCCCC},
279     /* 25 sky */      {0x3333,0xFFFF,0xFFFF},
280     /* 26 marine */   {0x3333,0x6666,0xFFFF},
281     /* 27 lblue */    {0x3333,0xCCCC,0xFFFF},
282     /* 28 lpurple */  {0x9999,0x9999,0xFFFF},
283     /* 29 pink */     {0xFFFF,0x9999,0xFFFF}};
284
285   if (st->d3d) {
286     st->shades = (st->d3d==D3D_TILED) ? 5 : st->lwid/2+1;      
287     st->ncolors=4+random()%4;
288     if (st->cmap>0) {                      /* tint the basecolors a bit */
289       for (c1=0; c1<BASECOLORS; c1++)
290         for (c2=0; c2<2; c2++)
291           if (!basecol[c1][c2]) {
292             basecol[c1][c2]+=random()%16000;
293           } else if (basecol[c1][c2]==0xFFFF) {
294             basecol[c1][c2]-=random()%16000;
295           } else {
296             basecol[c1][c2]-=8000;
297             basecol[c1][c2]+=random()%16000;
298           }
299     }
300     switch(st->cmap%4) {
301       case 0:                                            /* all */
302         for (c1=0; c1<st->ncolors; c1++) 
303           col[c1]=random()%BASECOLORS;
304         break;
305       case 1:                                          /* darks */
306         for (c1=0; c1<st->ncolors; c1++) 
307           col[c1]=random()%15;
308         break;
309       case 2:                                   /* semi consecutive darks */
310         col[0]=random()%15;
311         for (c1=1; c1<st->ncolors; c1++) 
312           col[c1]=(col[c1-1]+1+random()%2)%15;
313         break;
314       case 3:                                   /* consecutive darks */
315         col[0]=random()%(15-st->ncolors);
316         for (c1=1; c1<st->ncolors; c1++) 
317           col[c1]=col[c1-1]+1;
318         break;
319     }
320     for (c1=0; c1<st->ncolors; c1++) {
321       /* adjust colors already set */
322       for (h1=c1*st->shades-1; h1>=0; h1--) 
323         st->colors[h1+st->shades]=st->colors[h1];
324       make_color_ramp_rgb(st->display, st->xgwa.colormap,
325         basecol[col[c1]][0], basecol[col[c1]][1], basecol[col[c1]][2], 
326         0xFFFF, 0xFFFF, 0xFFFF, st->colors, &st->shades, 
327         False, True, False);
328     }
329     return;
330   }
331   /* not 3d */
332   st->shades=1;
333   if (st->cmap%2) {                                  /* basecolors */
334     if (random()%3) {           
335       c1=random()%15;                
336       c2=(c1+3+(random()%5))%15;
337       c3=(c2+3+(random()%5))%15;
338     } else {
339       c1=random()%BASECOLORS;                
340       c2=(c1+5+(random()%10))%BASECOLORS;
341       c3=(c2+5+(random()%10))%BASECOLORS;
342     }
343     r1=basecol[c1][0];
344     g1=basecol[c1][1];
345     b1=basecol[c1][2];
346     r2=basecol[c2][0];
347     g2=basecol[c2][1];
348     b2=basecol[c2][2];
349     r3=basecol[c3][0];
350     g3=basecol[c3][1];
351     b3=basecol[c3][2];
352   } else {                                             /* random rgb's */
353     r1=random()%65535;
354     g1=random()%65535;
355     b1=random()%65535;
356     r2=(r1+16384+random()%32768)%65535;
357     g2=(g1+16384+random()%32768)%65535;
358     b2=(b1+16384+random()%32768)%65535;
359     r3=(r2+16384+random()%32768)%65535;
360     g3=(g2+16384+random()%32768)%65535;
361     b3=(b2+16384+random()%32768)%65535;
362  }
363  switch(st->cmap) {
364     case 0:                           /* make_color_ramp color->color */
365     case 1:
366     case 2:                           /* make_color_ramp color->white */
367     case 3:
368       st->ncolors=5+random()%5;
369       if (st->cmap>1)
370         r2=g2=b2=0xFFFF;
371       make_color_ramp_rgb(st->display, st->xgwa.colormap,
372         r1, g1, b1, r2, g2, b2, 
373         st->colors, &st->ncolors, random()%2, True, False);
374       break;
375     case 4:                                /* 3 color make_color_loop */
376     case 5:
377     case 6:
378     case 7:
379       st->ncolors=8+random()%12;
380       rgb_to_hsv(r1, g1, b1, &h1, &s1, &v1);
381       rgb_to_hsv(r2, g2, b2, &h2, &s2, &v2);
382       rgb_to_hsv(r3, g3, b3, &h3, &s3, &v3);
383
384       make_color_loop(st->display, st->xgwa.colormap,
385         h1, s1, v1, h2, s2, v2, h3, s3, v3,
386         st->colors, &st->ncolors, True, False);
387       break;
388     case 8:                                            /* random smooth */
389     case 9:
390       st->ncolors=(random()%4)*6+12;
391       make_smooth_colormap (st->display, st->xgwa.visual, 
392         st->xgwa.colormap, st->colors, &st->ncolors, 
393         True, False, True);
394       break;
395     case 10:                                                 /* rainbow */
396       st->ncolors=(random()%4)*6+12;
397       make_uniform_colormap (st->display, st->xgwa.visual, 
398         st->xgwa.colormap, st->colors, &st->ncolors, 
399         True, False, True);
400       break;
401     case 11:                                     /* dark to light blend */
402     case 12:
403     case 13:
404     case 14:
405       st->ncolors=7;
406       make_color_ramp_rgb(st->display, st->xgwa.colormap,
407         r1, g1, b1, 0xFFFF, 0xFFFF, 0xFFFF, 
408         tmp_col1, &st->ncolors, False, True, False);
409       make_color_ramp_rgb(st->display, st->xgwa.colormap,
410         r2, g2, b2, 0xFFFF, 0xFFFF, 0xFFFF, 
411         tmp_col2, &st->ncolors, False, True, False);
412       if (st->cmap<13) {
413         for(c1=0; c1<=4; c1++) {
414            st->colors[c1*2]=tmp_col1[c1];
415            st->colors[c1*2+1]=tmp_col2[c1];
416         }
417         st->ncolors=10;
418       } else {
419         make_color_ramp_rgb(st->display, st->xgwa.colormap,
420           r3, g3, b3, 0xFFFF, 0xFFFF, 0xFFFF, 
421           tmp_col3, &st->ncolors, False, True, False);
422         for(c1=0; c1<=4; c1++) {
423            st->colors[c1*3]=tmp_col1[c1];
424            st->colors[c1*3+1]=tmp_col2[c1];
425            st->colors[c1*3+2]=tmp_col3[c1];
426         }
427         st->ncolors=15;
428       }
429       break;
430     default:                                                  /* random */
431       st->ncolors=(random()%4)*6+12;
432       make_random_colormap (st->display, st->xgwa.visual, 
433         st->xgwa.colormap, st->colors, &st->ncolors, 
434         False, True, False, True);
435       break;
436   }
437
438   /* set random color order for drawing and erasing */
439   for (c1=0; c1<MAXCOLORS; c1++)
440     st->rco[c1]=c1;
441   for (c1=0; c1<MAXCOLORS; c1++) {
442     c3=random()%MAXCOLORS;
443     c2=st->rco[c1];
444     st->rco[c1]=st->rco[c3];
445     st->rco[c3]=c2;
446   }
447 }
448
449 static int _comparedeo(const void *i, const void *j)
450 {
451         struct lineStruct *h1, *h2;
452                                                                                 
453         h1=(struct lineStruct *)i;
454         h2=(struct lineStruct *)j;
455         if (h1->deo > h2->deo)
456                 return(1);
457         if (h1->deo < h2->deo)
458                 return(-1);
459         return(0);
460 }
461
462 static int 
463 _hv(struct state *st, int x, int y, int d1, int d2, int pn, Bool de)
464 {
465   int v1, v2, r;
466
467   switch (d1) {
468     case 0:
469       v1 = (de) ? st->egridx-x : st->gridx-x;
470       break;
471     case 1:
472       v1 = y;
473       break;
474     case 2:
475       v1 = x;
476       break;
477     default:
478       v1 = (de) ? st->egridy-y : st->gridy-y;
479       break;
480   }
481   switch (d2) {
482     case 0:
483       v2 = (de) ? st->egridx-x : st->gridx-x;
484       break;
485     case 1:
486       v2 = y;
487       break;
488     case 2:
489       v2 = x;
490       break;
491     default:
492       v2 = (de) ? st->egridy-y : st->gridy-y;
493       break;
494   }
495   r = (de) ? (st->dline[st->li].hv) ? (v1+10000)*pn : (v2+10000)*-pn :
496     (st->eline[st->li].hv) ? (v1+10000)*pn : (v2+10000)*-pn;
497   return(r);
498 }
499         
500 static int 
501 _getdeo(struct state *st, int x, int y, int map, int de)
502 {
503   int cr;
504   switch(map) {
505     case 0:                                        /* horizontal one side */
506       return(x);
507     case 1:                                          /* vertical one side */
508       return(y);
509     case 2:                                        /* horizontal two side */
510       return(_min(x,st->gridx-x)+1); 
511     case 3:                                          /* vertical two side */
512       return(_min(y,st->gridy-y)+1); 
513     case 4:                                                     /* square */
514       return(_max(abs(x-st->rx3[de]),abs(y-st->ry3[de]))+1);
515     case 5:                                                /* two squares */
516       return(_min(_max(abs(x-(st->rx3[de]/2)),abs(y-st->ry3[de])),_max(abs(x-(st->gridx-(st->rx2[de]/2))),abs(y-st->ry2[de])))+1); 
517     case 6:                                       /* horizontal rectangle */
518       return(_max(abs(x-st->rx3[de]),abs(y-(st->ry3[de]))*st->cs1[de])+1); 
519     case 7:                                         /* vertical rectangle */
520       return(_max(abs(x-st->rx3[de])*st->cs1[de],abs(y-(st->ry3[de])))+1); 
521     case 8:                                                    /* + cross */
522       return(_min(abs(x-st->rx3[de]),abs(y-(st->ry3[de])))+1); 
523     case 9:                                                   /* diagonal */
524       return((x*3/4+y)+1);
525     case 10:                                         /* opposite diagonal */
526       return((x*3/4+st->gridy-y)+1); 
527     case 11:                                                   /* diamond */
528       return((abs(x-st->rx3[de])+abs(y-st->ry3[de]))/2+1); 
529     case 12:                                              /* two diamonds */
530       return(_min(abs(x-(st->rx3[de]/2))+abs(y-st->ry3[de]),abs(x-(st->gridx-(st->rx2[de]/2)))+abs(y-st->ry2[de]))/2+1);
531     case 13:                                                    /* circle */
532       return(_dist(st,x,st->rx3[de],y,st->ry3[de],0)+1); 
533     case 14:                                        /* horizontal ellipse */
534       return(_dist(st,x,st->rx3[de],y,st->ry3[de],1)+1); 
535     case 15:                                          /* vertical ellipse */
536       return(_dist(st,x,st->rx3[de],y,st->ry3[de],2)+1); 
537     case 16:                                               /* two circles */
538       return(_min(_dist(st,x,st->rx3[de]/2,y,st->ry3[de],0),_dist(st,x,st->gridx-(st->rx2[de]/2),y,st->ry2[de],0))+1); 
539     case 17:                                  /* horizontal straight wave */
540       return(x+_wave(st,st->gridy+y,st->csw[0]*st->cs1[0],st->csw[0]*st->cs2[0],st->wave[de]));
541     case 18:                                    /* vertical straight wave */
542       return(y+_wave(st,st->gridx+x,st->csw[0]*st->cs1[0],st->csw[0]*st->cs2[0],st->wave[de])); 
543     case 19:                                     /* horizontal wavey wave */
544       return(x+_wave(st,st->gridy+y+((x/5)*st->edir),st->csw[de]*st->cs1[de],st->csw[de]*st->cs2[de],st->wave[de])+1);
545     case 20:                                       /* vertical wavey wave */
546       return(y+_wave(st,st->gridx+x+((y/5)*st->edir),st->csw[de]*st->cs1[de],st->csw[de]*st->cs2[de],st->wave[de])+1);
547 /* no d3d for 21,22 */
548     case 21:                                  /* simultaneous directional */
549       return(_hv(st,x,y,st->cs1[0]%2,st->cs2[0]%2,1,de));
550     case 22:                                       /* reverse directional */
551       return(_hv(st,x,y,st->cs1[0]%2,st->cs2[0]%2,-1,de)); 
552     case 23:                                                    /* length */
553       if (de) 
554         return(st->dline[st->li].len*1000+random()%5000); 
555       else 
556         return(st->eline[st->li].len*1000+random()%5000);
557     case 24:                                                    /* object */
558     case 25:                                                    
559     case 26:                                                    
560     case 27:                                                    
561       if (de) 
562         return(st->dline[st->li].obj*100); 
563       else 
564         return(st->eline[st->li].obj*100);
565     default:                                                     /* color */
566       cr = (de) ? st->dline[st->li].color : st->eline[st->li].color;
567       if (map<34) cr=st->rco[cr];
568       if ((map%6<4) || (de))  {                               /* by color */
569         cr*=1000;
570         cr+=random()%1000;
571       } else if (map%6==4) {                      /* by color horizontaly */
572         cr*=st->gridx;
573         cr+=(x+random()%(st->gridx/2));
574       } else {                                     /* by color vertically */
575         cr*=st->gridy;
576         cr+=(y+random()%(st->gridy/2));
577       }
578       return(cr);
579   }
580   return(1);
581 }
582
583 static void 
584 _init_screen(struct state *st)
585 {
586   int nstr, x;
587   struct lineStruct *tmp;
588
589   /* malloc memory in case of resize */
590   if (st->resized) {
591     st->max_wxh=st->xgwa.width*st->xgwa.height;
592     if (st->dline!=NULL)
593       free(st->dline);
594     if (st->eline!=NULL)
595       free(st->eline);
596     if (st->grid!=NULL)
597       free(st->grid);
598     if (st->zlist!=NULL)
599       free(st->zlist);
600     if (st->fdol!=NULL)
601       free(st->fdol);
602     if (st->odi!=NULL)
603       free(st->odi);
604     st->narray = (st->xgwa.width+1)*(st->xgwa.height+1)/4+1;
605     st->dline = calloc(st->narray, sizeof(struct lineStruct));
606     st->eline = calloc(st->narray, sizeof(struct lineStruct));
607     st->grid = calloc(st->narray, sizeof(struct gridStruct));
608     st->zlist = calloc(st->narray, sizeof(unsigned int));
609     st->fdol = calloc(st->narray, sizeof(unsigned int));
610     st->odi = calloc(st->narray, sizeof(Bool));
611     if ((st->dline == NULL) || (st->eline == NULL) || 
612       (st->grid == NULL) || (st->zlist == NULL) ||
613       (st->fdol == NULL) || (st->odi == NULL)) {
614       fprintf(stderr, "not enough memory\n");
615       exit(1);
616     }
617     st->dialog = (st->xgwa.width<500) ? 1 : 0;
618     st->resized=False;
619   }
620   if (st->ii) {
621     /* swap st->dline and st->eline pointers to resort and erase */
622     tmp=st->eline;
623     st->eline=st->dline;
624     st->dline=tmp;
625     st->eli=st->li;
626     st->elwid=st->lwid;
627     st->elpu=st->lpu;
628     st->egridx=st->gridx;
629     st->egridy=st->gridy;
630
631     /* create new erase order */
632     for (st->li=1; st->li<=st->eli; st->li++) 
633       st->eline[st->li].deo=(_getdeo(st,st->eline[st->li].x,st->eline[st->li].y,st->emap,0) + (random()%st->evar) + (random()%st->evar))*st->edir;
634     qsort(st->eline, st->eli+1, sizeof(struct lineStruct), _comparedeo);
635   }
636   st->ii++;
637
638   /* clear arrays and other counters */
639   st->di=st->ei=st->fi=st->li=st->oi=st->zi=0;
640   st->grid_full=False;
641   /* li starts from 1 */
642   st->dline[0].x=st->dline[0].y=st->dline[0].len=0; 
643   /* to keep it first after sorting so di is never null */
644   st->dline[0].deo=-999999999; 
645
646   /* set random screen variables */
647   st->lwid = (st->ii==1) ? 3 : 2+((random()%6)%4);
648   st->d3d = ((st->tile==TILE_FLAT) || (st->tile==TILE_THIN) ||
649     (st->tile==TILE_OUTLINE)) ? D3D_NONE :
650     (st->tile==TILE_BLOCK) ? D3D_BLOCK :
651     (st->tile==TILE_NEON) ? D3D_NEON :
652     (st->tile==TILE_TILED) ? D3D_TILED : 
653     /* force TILE_D3D on first screen to properly load all shades */
654     ((st->ii==1) && (!st->newcols)) ? D3D_TILED : (random()%5)%4; 
655 /* st->d3d=D3D_BLOCK; st->lwid=2; */
656   st->outline = (st->tile==TILE_OUTLINE) ? 1 :
657      ((st->tile!=TILE_RANDOM) || (random()%5)) ? 0 : 1; 
658   st->round = (st->d3d==D3D_NEON) ? 1 : 
659     ((st->d3d==D3D_BLOCK) || (st->outline) || (random()%6)) ? 0 : 1;
660   if ((st->d3d) || (st->outline) || (st->round))
661     st->lwid+=2;
662   if ((!st->d3d) && (!st->round) && (!st->outline) && (st->lwid>3))
663     st->lwid-=2;
664   if (st->d3d==D3D_TILED)
665     st->lwid++;
666   if (st->tile==TILE_THIN)
667     st->lwid=2;
668  
669   _init_zlist(st);
670
671   st->maxlen=(st->lwid>6) ? 2+(random()%4) :
672                 (st->lwid>4) ? 2+(random()%8)%6 :
673                 (st->lwid>2) ? 2+(random()%12)%8 : 2+(random()%15)%10;
674   st->bnratio = 4+(random()%4)+(random()%4);
675   st->forcemax = (random()%6) ? 0 : 1;
676
677   if ((st->ii==1) || (st->newcols))
678     _init_colors(st);
679
680   st->dmap = (st->emap+5+(random()%5))%DRAWORDERS;
681
682   st->dmap=20+random()%20;
683   
684   st->dvar = (st->dmap>22) ? 100 : 10+(st->csw[0]*(random()%5));
685   st->ddir= (random()%2) ? 1 : -1;
686   
687   st->emap = (st->dmap+10+(random()%10))%20;
688   st->evar = (st->emap>22) ? 100 : 10+(st->csw[0]*(random()%5));
689   st->edir= (random()%2) ? 1 : -1;
690
691   st->layers= (random()%2) ? 2 : (random()%2) ? 1 : (random()%2) ? 3 : 4;
692   st->cmap=(st->cmap+5+(random()%10))%COLORMAPS;
693
694   for (x=0; x<LAYERS; x++) {
695     st->pattern[x]=random()%PATTERNS;
696     st->shape[x]=random()%SHAPES;
697     st->mix[x]=random()%20;
698     nstr = (st->lwid==2) ? 20+random()%12 :
699       (st->lwid==3) ? 16+random()%8 : 
700       (st->lwid==4) ? 12+random()%6 : 
701       (st->lwid==5) ? 10+random()%5 : 
702       (st->lwid==6) ? 8+random()%4 : 
703         5+random()%5;
704     st->csw[x] = _max(5,st->gridy/nstr);
705     st->wsx[x] = (st->wsx[x]+3+(random()%3))%STRETCHES;
706     st->wsy[x] = (st->wsy[x]+3+(random()%3))%STRETCHES;
707     st->sec[x] = random()%5;
708     if ((!st->dialog) && (st->sec[x]<2)) st->csw[x]/=2;
709     st->cs1[x] = (st->dialog) ? 1+random()%3 : 2+random()%5;
710     st->cs2[x] = (st->dialog) ? 1+random()%3 : 2+random()%5;
711     st->cs3[x] = (st->dialog) ? 1+random()%3 : 2+random()%5;
712     st->cs4[x] = (st->dialog) ? 1+random()%3 : 2+random()%5;
713     st->wave[x]=random()%WAVES;
714     st->wavel[x]=st->csw[x]*(2+random()%6); 
715     st->waveh[x]=st->csw[x]*(1+random()%3);
716     st->rx1[x]=(st->gridx/10+random()%(st->gridx*8/10));
717     st->ry1[x]=(st->gridy/10+random()%(st->gridy*8/10));
718     st->rx2[x]=(st->gridx*2/10+random()%(st->gridx*6/10));
719     st->ry2[x]=(st->gridy*2/10+random()%(st->gridy*6/10));
720     st->rx3[x]=(st->gridx*3/10+random()%(st->gridx*4/10));
721     st->ry3[x]=(st->gridy*3/10+random()%(st->gridy*4/10));
722   }
723 }
724
725 static int 
726 _shape(struct state *st, int x, int y, int rx, int ry, int n)
727 {
728   switch(st->shape[n]) {
729     case 0:                                        /* square/rectangle */
730     case 1:
731     case 2:
732       return(1+_max(abs(x-rx)*st->cs1[n]/st->cs2[n],abs(y-ry)*st->cs3[n]/st->cs4[n]));
733     case 3:                                                 /* diamond */
734     case 4:
735       return(1+(abs(x-rx)*st->cs1[n]/st->cs2[n]+abs(y-ry)*st->cs3[n]/st->cs4[n]));
736     case 5:                                            /* 8 point star */
737       return(1+_min(_max(abs(x-rx),abs(y-ry))*3/2,abs(x-rx)+abs(y-ry)));
738     case 6:                                             /* circle/oval */
739     case 7:
740     case 8:
741       return(1+_dist(st,x,rx,y,ry,st->cs1[n]));
742     case 9:                                       /* black hole circle */
743       return(1+(st->gridx*st->gridy/(1+(_dist(st,x,rx,y,ry,st->cs2[n]))))); 
744     case 10:                                                   /* sun */
745       return(1+_min(abs(x-rx)*st->gridx/(abs(y-ry)+1),abs(y-ry)*st->gridx/(abs(x-rx)+1)));
746     case 11:                             /* 2 circles+inverted circle */
747       return(1+(_dist(st,x,rx,y,ry,st->cs1[n])*_dist(st,x,(rx*3)%st->gridx,y,(ry*5)%st->gridy,st->cs1[n])/(1+_dist(st,x,(rx*4)%st->gridx,y,(ry*7)%st->gridy,st->cs1[n])))); 
748     case 12:                                                   /* star */
749       return(1+(int)sqrt(abs((x-rx)*(y-ry))));
750     case 13:                                       /* centered ellipse */
751       return(1+_dist(st,x,rx,y,ry,0)+_dist(st,x,st->gridx-rx,y,st->gridy-ry,0));
752     default:                                               /* triangle */
753       return(1+_triangle(st,x,y,rx,ry,st->cs4[n]));
754   } 
755   return(1+_triangle(st,x,y,rx,ry,st->cs4[n]));
756 }
757
758 static int 
759 _pattern(struct state *st, int x, int y, int n)
760 {
761   int v=0, ox;
762   ox=x;
763   switch(st->wsx[n]) {
764     case 0:                                             /* slants */
765       x+=y/(1+st->cs4[n]);
766       break;
767     case 1:
768       x+=(st->gridy-y)/(1+st->cs4[n]);
769       break;
770     case 2:                                             /* curves */
771       x+=_wave(st,y,st->gridx/(1+st->cs1[n]),st->gridy,0);
772       break;
773     case 3:
774       x+=_wave(st,st->gridy-y,st->gridy/(1+st->cs1[n]),st->gridy,0);
775       break;
776     case 4:                                           /* U curves */
777       x+=_wave(st,y,st->cs1[n]*st->csw[n]/2,st->gridy*2/PI,0);
778       break;
779     case 5:
780       x-=_wave(st,y,st->cs1[n]*st->csw[n]/2,st->gridy*2/PI,0);
781       break;
782   }
783   switch(st->wsy[0]) {
784     case 0:                                          /* slants */
785       y+=ox/(1+st->cs1[n]);
786       break;
787     case 1:
788       y+=(st->gridx-ox)/(1+st->cs1[n]);
789       break;
790     case 2:                                           /* curves */
791       y+=_wave(st,ox,st->gridx/(1+st->cs1[n]),st->gridx,0);
792       break;
793     case 3:
794       y+=_wave(st,st->gridx-ox,st->gridx/(1+st->cs1[n]),st->gridx,0);
795       break;
796     case 4:                                         /* U curves */
797       y+=_wave(st,ox,st->cs1[n]*st->csw[n]/2,st->gridy*2/PI,0);
798       break;
799     case 5:
800       y-=_wave(st,ox,st->cs1[n]*st->csw[n]/2,st->gridy*2/PI,0);
801       break;
802   }
803   switch(st->pattern[n]) {
804     case 0:                                          /* horizontal stripes */
805       v=y;
806       break;
807     case 1:                                            /* vertical stripes */
808       v=x;
809       break;
810     case 2:                                            /* diagonal stripes */
811       v=(x+(y*st->cs1[n]/st->cs2[n]));
812       break;
813     case 3:                                    /* reverse diagonal stripes */
814       v=(x-(y*st->cs1[n]/st->cs2[n]));
815       break;
816     case 4:                                                /* checkerboard */
817       v=(y/st->csw[n]*3+x/st->csw[n])*st->csw[n];
818       break;
819     case 5:                                       /* diagonal checkerboard */
820       v=((x+y)/2/st->csw[n]+(x+st->gridy-y)/2/st->csw[n]*3)*st->csw[n];
821       break;
822     case 6:                                                     /* + cross */
823       v=st->gridx+(_min(abs(x-st->rx3[n]),abs(y-st->ry3[n]))*2);
824       break;
825     case 7:                                              /* double + cross */
826       v=_min(_min(abs(x-st->rx2[n]),abs(y-st->ry2[n])),_min(abs(x-st->rx1[n]),abs(y-st->ry1[n])))*2;
827       break;
828     case 8:                                                     /* X cross */
829       v=st->gridx+(_min(abs(x-st->rx3[n])*st->cs1[n]/st->cs2[n]+abs(y-st->ry2[n])*st->cs3[n]/st->cs4[n],abs(x-st->rx3[n])*st->cs1[n]/st->cs2[n]-abs(y-st->ry3[n])*st->cs3[n]/st->cs4[n])*2);
830       break;
831     case 9:                                              /* double X cross */
832       v=_min(_min(abs(x-st->rx2[n])+abs(y-st->ry2[n]),abs(x-st->rx2[n])-abs(y-st->ry2[n])),_min(abs(x-st->rx1[n])+abs(y-st->ry1[n]),abs(x-st->rx1[n])-abs(y-st->ry1[n])))*2;
833       break;
834     case 10:                                   /* horizontal stripes/waves */
835       v=st->gridy+(y+_wave(st,x,st->waveh[n],st->wavel[n],st->wave[n]));
836       break;
837     case 11:                                     /* vertical stripes/waves */
838       v=st->gridx+(x+_wave(st,y,st->waveh[n],st->wavel[n],st->wave[n]));
839       break;
840     case 12:                                     /* diagonal stripes/waves */
841       v=st->gridx+(x+(y*st->cs1[n]/st->cs2[n])+_wave(st,x,st->waveh[n],st->wavel[n],st->wave[n]));
842       break;
843     case 13:                                     /* diagonal stripes/waves */
844       v=st->gridx+(x-(y*st->cs1[n]/st->cs2[n])+_wave(st,y,st->waveh[n],st->wavel[n],st->wave[n]));
845       break;
846     case 14:                                    /* horizontal spikey waves */
847       v=y+(st->csw[n]*st->cs4[n]/st->cs3[n])+_wave(st,x+((y/st->cs3[n])*st->edir),st->csw[n]/2*st->cs1[n]/st->cs2[n],st->csw[n]/2*st->cs2[n]/st->cs1[n],st->wave[n]);
848       break;
849     case 15:                                      /* vertical spikey waves */
850       v=x+(st->csw[n]*st->cs1[n]/st->cs2[n])+_wave(st,y+((x/st->cs3[n])*st->edir),st->csw[n]/2*st->cs1[n]/st->cs2[n],st->csw[n]/2*st->cs3[n]/st->cs4[n],st->wave[n]);
851       break;
852     case 16:                                    /* big slanted hwaves */
853       v=st->gridy-y-(x*st->cs1[n]/st->cs3[n])+(st->csw[n]*st->cs1[n]*st->cs2[n]) +_wave(st,x,st->csw[n]/3*st->cs1[n]*st->cs2[n],st->csw[n]/3*st->cs3[n]*st->cs2[n],st->wave[n]);
854       break;
855     case 17:                                    /* big slanted vwaves */
856       v=x-(y*st->cs1[n]/st->cs3[n])+(st->csw[n]*st->cs1[n]*st->cs2[n]) +_wave(st,y, st->csw[n]/3*st->cs1[n]*st->cs2[n], st->csw[n]/3*st->cs3[n]*st->cs2[n], st->wave[n]);
857       break;
858     case 18:                                          /* double hwave */
859       v=y+(y+st->csw[n]*st->cs3[n])+_wave(st,x,st->csw[n]/3*st->cs3[n],st->csw[n]/3*st->cs2[n],st->wave[n])+_wave(st,x,st->csw[n]/3*st->cs4[n],st->csw[n]/3*st->cs1[n]*3/2,st->wave[n]);
860       break;
861     case 19:                                          /* double vwave */
862       v=x+(x+st->csw[n]*st->cs1[n])+_wave(st,y,st->csw[n]/3*st->cs1[n],st->csw[n]/3*st->cs3[n],st->wave[n])+_wave(st,y,st->csw[n]/3*st->cs2[n],st->csw[n]/3*st->cs4[n]*3/2,st->wave[n]); 
863       break;
864     case 20:                                                  /* one shape */
865     case 21:
866     case 22:
867       v=_shape(st,x, y, st->rx3[n], st->ry3[n], n);
868       break;
869     case 23:                                                 /* two shapes */
870     case 24:
871     case 25:
872       v=_min(_shape(st,x, y, st->rx1[n], st->ry1[n], n),_shape(st,x, y, st->rx2[n], st->ry2[n], n));
873       break;
874     case 26:                                       /* two shapes opposites */
875     case 27:
876       v=_min(_shape(st,x, y, st->rx2[n], st->ry2[n], n),_shape(st,x, y, st->gridx-st->rx2[n], st->gridy-st->rx2[n], n));
877       break;
878     case 28:                                     /* two shape checkerboard */
879     case 29:
880       v=((_shape(st,x, y, st->rx1[n], st->ry1[n], n)/st->csw[n])+(_shape(st,x, y, st->rx2[n], st->ry2[n], n)/st->csw[n]))*st->csw[n];
881       break;
882     case 30:                                             /* two shape blob */
883     case 31:
884       v=(_shape(st,x, y, st->rx1[n], st->ry1[n], n)+_shape(st,x, y, st->rx2[n], st->ry2[n], n))/2;
885       break;
886     case 32:                                    /* inverted two shape blob */
887     case 33:
888       v=(_shape(st,x, y, st->rx1[n], st->ry1[n], n)+_shape(st,st->gridx-x, st->gridy-y, st->rx1[n], st->ry1[n], n))/2;
889       break;
890     case 34:                                               /* three shapes */
891     case 35:
892       v=_min(_shape(st,x, y, st->rx3[n], st->ry3[n], n),_min(_shape(st,x, y, st->rx1[n], st->ry1[n], n),_shape(st,x, y, st->rx2[n], st->ry2[n], n)));
893       break;
894     case 36:                                           /* three shape blob */
895     case 37:
896       v=(_shape(st,x, y, st->rx1[n], st->ry1[n], n)+_shape(st,x, y, st->rx2[n], st->ry2[n], n)+_shape(st,x, y, st->rx3[n], st->ry3[n], n))/3;
897       break;
898     case 38:                                                  /* 4 shapes */    
899       v=(_min(_shape(st,x, y, st->rx2[n], st->ry2[n], n),_shape(st,x, y, st->gridx-st->rx2[n], st->gridy-st->ry2[n], n)),_min(_shape(st,x, y, st->gridx-st->rx2[n], st->ry2[n], n),_shape(st,x, y, st->rx2[n], st->gridy-st->ry2[n], n)));
900       break;
901     case 39:                                            /* four rainbows */
902       v=(_min(_shape(st,x, y, st->gridx-st->rx2[n]/2, st->csw[n], n),_shape(st,x, y, st->csw[n], st->ry2[n]/2, n)),_min(_shape(st,x, y, st->rx2[n]/2, st->gridy-st->csw[n], n),_shape(st,x, y, st->gridx-st->csw[n], st->gridy-(st->ry2[n]/2), n))); 
903       break;
904   }
905   /* stretch or contract stripe */
906   switch(st->sec[n]) {
907     case 0:
908       v=(int)sqrt((int)sqrt(abs(v)*st->gridx)*st->gridx);
909       break;
910     case 1:
911       v=((int)pow(v,2)/st->gridx);
912       break;
913   }
914   return (abs(v));
915 }
916
917 static int 
918 _getcolor(struct state *st, int x, int y)
919 {
920   int n, cv[LAYERS];
921   
922   for (n=0; n<st->layers; n++) {
923     cv[n]=_pattern(st,x,y,n);
924                   /* first wave/shape */
925     cv[0] = (!n) ? cv[0]/st->csw[0] : 
926                     /* checkerboard+1 */
927       (st->mix[n]<5) ? (cv[0]*st->csw[0]+cv[n])/st->csw[n] : 
928                /* checkerboard+ncol/2 */
929       (st->mix[n]<12) ? cv[0]+(cv[n]/st->csw[n]*st->ncolors/2) : 
930                            /* add mix */
931       (st->mix[n]<16) ? cv[0]+(cv[n]/st->csw[n]) : 
932                       /* subtract mix */
933       (st->mix[n]<18) ? cv[0]-(cv[n]/st->csw[n]) : 
934                   /* r to l morph mix */
935       (st->mix[n]==18) ? ((cv[0]*x)+(cv[n]*(st->gridx-x)/st->csw[n]))/st->gridx : 
936                   /* u to d morph mix */
937       ((cv[0]*y)+(cv[n]*(st->gridy-y)/st->csw[n]))/st->gridy; 
938   }
939   return(cv[0]);
940 }
941
942 /* return value=line direction
943    st->olen=open space to edge or next blocking line
944    st->bln=blocking line number or -1 if edge blocks */
945 static int 
946 _findopen(struct state *st, int x, int y, int z)
947 {
948   int dir, od[4], no=0;
949
950   if (((st->grid[z].hl) || (st->grid[z].hr)) && 
951     ((st->grid[z].vu) || (st->grid[z].vd))) 
952     return(DIR_NONE);
953   if ((z>st->gridx) && (!st->grid[z].hl) && (!st->grid[z].hr) && 
954     (!st->grid[z-st->gridx].line)) { 
955     od[no]=DIR_UP; 
956     no++; 
957   }
958   if ((z<st->gridn-st->gridx) && (!st->grid[z].hl) && 
959     (!st->grid[z].hr) && (!st->grid[z+st->gridx].line)) { 
960     od[no]=DIR_DOWN; 
961     no++; 
962   }
963   if ((x) && (!st->grid[z].hl) && (!st->grid[z].hr) && 
964     (!st->grid[z-1].line)) {
965     od[no]=DIR_LEFT; 
966     no++; 
967   }
968   if (((z+1)%st->gridx) && (!st->grid[z].hl) && (!st->grid[z].hr) && 
969     (!st->grid[z+1].line)) { 
970     od[no]=DIR_RIGHT; 
971     no++; 
972   }
973   if (!no) 
974     return(DIR_NONE);
975   dir=od[random()%no];
976   st->olen=st->bln=0;
977   while ((st->olen<=st->maxlen) && (!st->bln)) {
978     st->olen++;
979     if (dir==DIR_UP) 
980       st->bln = (y-st->olen<0) ? -1 : 
981         st->grid[z-(st->olen*st->gridx)].line;
982     if (dir==DIR_DOWN) 
983       st->bln = (y+st->olen>=st->gridy) ? -1 : 
984         st->grid[z+(st->olen*st->gridx)].line;
985     if (dir==DIR_LEFT) 
986       st->bln = (x-st->olen<0) ? -1 : 
987         st->grid[z-st->olen].line;
988     if (dir==DIR_RIGHT) 
989       st->bln = (x+st->olen>=st->gridx) ? -1 : 
990         st->grid[z+st->olen].line;
991   }
992   st->olen--; 
993   return(dir);
994 }
995
996 static void 
997 _fillgrid(struct state *st)
998 {
999   unsigned int gridc, n, add;
1000
1001   gridc=st->gridx*st->dline[st->li].y+st->dline[st->li].x;
1002   add = (st->dline[st->li].hv) ? 1 : st->gridx;
1003   for (n=0;  n<=st->dline[st->li].len; n++) {
1004     if (n)
1005       gridc+=add;
1006     if (!st->grid[gridc].line) {
1007       st->fi++;
1008       st->grid[gridc].line=st->li;
1009     }
1010     if (st->dline[st->li].hv) {
1011       if (n) 
1012         st->grid[gridc].hr=st->li;
1013       if (n<st->dline[st->li].len)
1014         st->grid[gridc].hl=st->li;
1015     } else {
1016       if (n) 
1017         st->grid[gridc].vd=st->li;
1018       if (n<st->dline[st->li].len) 
1019         st->grid[gridc].vu=st->li;
1020     }
1021     if (st->fi>=st->gridn) {
1022       st->grid_full=True;
1023       return;
1024     }
1025   }
1026 }
1027
1028 static void 
1029 _newline(struct state *st)
1030 {
1031   int bl, bz, dir, lt, x, y, z;
1032
1033   bl=0;
1034   z=st->zlist[st->zi];
1035   x=z%st->gridx;
1036   y=z/st->gridx;
1037   st->zi++;
1038   dir=_findopen(st,x,y,z);
1039
1040   if (!st->grid[z].line) { 
1041   /* this is an empty space, make a new line unless nothing is open around it */
1042     if (dir==DIR_NONE) {
1043       /* nothing is open, force a len 1 branch in any direction */
1044       lt=LINE_FORCE; 
1045       while ((dir==DIR_NONE) || 
1046         ((dir==DIR_UP) && (!y)) || 
1047         ((dir==DIR_DOWN) && (y+1==st->gridy)) ||
1048         ((dir==DIR_LEFT) && (!x)) || 
1049         ((dir==DIR_RIGHT) && (x+1==st->gridx))) {
1050           dir=random()%4;
1051       }
1052       bz = (dir==DIR_UP) ? z-st->gridx : (dir==DIR_DOWN) ? z+st->gridx : (dir==DIR_LEFT) ? z-1 : z+1;
1053       bl = st->grid[bz].line;
1054     } else if ((st->bnratio>1) && (st->bln>0) && 
1055       (st->olen<st->maxlen) && (random()%st->bnratio)) { 
1056       /* branch into blocking line */
1057       lt=LINE_BRIN; 
1058       bl = st->bln;
1059     } else { 
1060       /* make a new line and new object */
1061       lt=LINE_NEW; 
1062       st->oi++;
1063     }  
1064   } else { 
1065     /* this is a filled space, make a branch unless nothing is open around it */
1066     if (dir==DIR_NONE) 
1067       return;
1068     /* make a branch out of this line */
1069     lt=LINE_BROUT; 
1070     bl=st->grid[z].line;
1071   }
1072   st->li++;
1073   st->dline[st->li].len = (lt==LINE_FORCE) ? 1 :  (lt==LINE_BRIN) ? 
1074     st->olen+1 : (!st->forcemax) ? st->olen : 1+random()%st->olen;
1075   st->dline[st->li].x=x;
1076   if (dir==DIR_LEFT) 
1077     st->dline[st->li].x-=st->dline[st->li].len;
1078   st->dline[st->li].y=y;
1079   if (dir==DIR_UP) 
1080     st->dline[st->li].y-=st->dline[st->li].len;
1081   st->dline[st->li].hv = ((dir==DIR_LEFT) || (dir==DIR_RIGHT)) ? 
1082     True : False;
1083   st->dline[st->li].obj = (lt==LINE_NEW) ? st->oi : 
1084     st->dline[bl].obj;
1085   st->dline[st->li].color = (lt==LINE_NEW) ? 
1086     (_getcolor(st,x,y))%st->ncolors : st->dline[bl].color;
1087   st->dline[st->li].deo=(_getdeo(st,x,y,st->dmap,1) + 
1088     (random()%st->dvar) + (random()%st->dvar))*st->ddir;
1089   st->dline[st->li].ndol=0;
1090   _fillgrid(st);
1091 }
1092
1093 static void 
1094 _create_screen(struct state *st)
1095 {
1096   while(!st->grid_full)
1097     _newline(st);
1098   qsort(st->dline, st->li+1, sizeof(struct lineStruct), _comparedeo);
1099 /*st->lpu=st->li/20/((6-st->speed)*3);
1100   Used to use a computed lpu, lines per update to control draw speed
1101   draw 1/lpu of the lines before each XSync which takes a split second 
1102   the higher the lpu, the quicker the screen draws.  This worked somewhat
1103   after the 4->5 update, however with the Mac updating so much more slowly,
1104   values tuned for it draw the screen in a blink on Linux.  Therefore we
1105   draw 1/200th of the screen with each update and sleep, if necessary */
1106   st->lpu = (st->dialog) ? st->li/50 : st->li/200;   
1107   if (!st->lpu) st->lpu = 1;
1108   st->bi=1;
1109   st->mode=MODE_ERASE;
1110 }
1111
1112 static void 
1113 _fill_outline(struct state *st, int di)
1114 {
1115   int x, y, h, w;
1116
1117   if (!di)
1118     return;
1119   x=st->dline[di].x*st->lwid+1;
1120   y=st->dline[di].y*st->lwid+1;
1121   if (st->dline[di].hv) {
1122     w=(st->dline[di].len+1)*st->lwid-3;
1123     h=st->lwid-3;
1124   } else {
1125     w=st->lwid-3; 
1126     h=(st->dline[di].len+1)*st->lwid-3;
1127   }
1128   XFillRectangle (st->display, st->window, st->bgc, x, y, w, h);
1129 }
1130
1131 static void 
1132 _XFillRectangle(struct state *st, int di, int adj)
1133 {
1134   int a, b, x, y, w, h;
1135
1136   x=st->dline[di].x*st->lwid;
1137   y=st->dline[di].y*st->lwid;
1138   if (st->dline[di].hv) {
1139     w=(st->dline[di].len+1)*st->lwid-1;
1140     h=st->lwid-1;
1141   } else {
1142     w=st->lwid-1; 
1143     h=(st->dline[di].len+1)*st->lwid-1;
1144   }
1145   switch (st->d3d) {
1146     case D3D_NEON:
1147       x+=adj;
1148       y+=adj;
1149       w-=adj*2;
1150       h-=adj*2;
1151     break;
1152     case D3D_BLOCK:
1153       x+=adj;
1154       y+=adj;
1155       w-=st->lwid/2-1;
1156       h-=st->lwid/2-1;
1157     break;
1158   }
1159   if (!st->round) { 
1160     XFillRectangle(st->display, st->window, st->fgc, x, y, w, h);
1161   } else {
1162     if (h<st->lwid) {                                   /* horizontal */
1163       a=(h-1)/2;
1164       for (b=0; b<=a; b++)
1165         XFillRectangle(st->display, st->window, st->fgc, 
1166           x+b, y+a-b, w-b*2, h-((a-b)*2));
1167     } else {                                               /* vertical */
1168       a=(w-1)/2;
1169       for (b=0; b<=a; b++)
1170         XFillRectangle(st->display, st->window, st->fgc, 
1171           x+a-b, y+b, w-((a-b)*2), h-b*2);
1172     }
1173   }
1174 }
1175
1176 static void 
1177 _XFillTriangle(struct state *st, int color, int x1, int y1, int x2, int y2, 
1178   int x3, int y3)
1179 {
1180   XPoint points[3];
1181
1182   points[0].x=x1;
1183   points[0].y=y1;
1184   points[1].x=x2;
1185   points[1].y=y2;
1186   points[2].x=x3;
1187   points[2].y=y3;
1188   XSetForeground(st->display, st->fgc, st->colors[color].pixel);
1189   XFillPolygon (st->display, st->window, st->fgc, points, 3, Convex, 
1190       CoordModeOrigin);
1191 }
1192
1193 static void 
1194 _XFillPolygon4(struct state *st, int color, int x1, int y1, int x2, int y2, 
1195   int x3, int y3, int x4, int y4)
1196 {
1197   XPoint points[4];
1198
1199   points[0].x=x1;
1200   points[0].y=y1;
1201   points[1].x=x2;
1202   points[1].y=y2;
1203   points[2].x=x3;
1204   points[2].y=y3;
1205   points[3].x=x4;
1206   points[3].y=y4;
1207   XSetForeground(st->display, st->fgc, st->colors[color].pixel);
1208   XFillPolygon (st->display, st->window, st->fgc, points, 4, Convex, 
1209       CoordModeOrigin);
1210 }
1211
1212 static void 
1213 _draw_tiled(struct state *st, int color)
1214 {
1215   int a, c, d, x, y, z, m1, m2, lr, nl, w, h;
1216   a = (st->dline[st->di].hv) ? 1 : st->gridx;
1217   z = st->dline[st->di].y*st->gridx+st->dline[st->di].x;
1218   m1 = (st->lwid-1)/2;
1219   m2 = st->lwid/2;
1220   lr = st->lwid-1;
1221   nl = st->lwid;
1222
1223   /* draw tiles one grid cell at a time */
1224   for (c=0; c<=st->dline[st->di].len; c++) {
1225     if (st->dline[st->di].hv) {
1226       x = (st->dline[st->di].x+c)*st->lwid;
1227       y = st->dline[st->di].y*st->lwid;
1228       if (c) 
1229         st->grid[z].dhr=st->di;
1230       if (c<st->dline[st->di].len) 
1231         st->grid[z].dhl=st->di;
1232     } else {
1233       x = st->dline[st->di].x*st->lwid;
1234       y = (st->dline[st->di].y+c)*st->lwid;
1235       if (c) 
1236         st->grid[z].dvd=st->di;
1237       if (c<st->dline[st->di].len) 
1238         st->grid[z].dvu=st->di;
1239     }
1240     d=0;
1241     if (st->grid[z].dhl) 
1242       d+=8;
1243     if (st->grid[z].dhr) 
1244       d+=4;
1245     if (st->grid[z].dvu) 
1246       d+=2;
1247     if (st->grid[z].dvd) 
1248       d++;
1249     /* draw line base */
1250     switch (d) {
1251       case 1:
1252       case 2:                                    /* vertical */
1253       case 3:
1254       case 5:
1255       case 6:
1256       case 7:
1257       case 11:
1258       case 15:
1259         h = ((d==1) || (d==5)) ? lr : nl;
1260         XSetForeground(st->display, st->fgc, 
1261           st->colors[color].pixel);
1262         XFillRectangle (st->display, st->window, st->fgc, 
1263           x, y, m2, h);
1264         XSetForeground(st->display, st->fgc, 
1265            st->colors[color+3].pixel);
1266         XFillRectangle (st->display, st->window, st->fgc, 
1267           x+m2, y, m1, h);
1268         break;
1269       case 4:
1270       case 8:                                     /* horizontal */
1271       case 9:
1272       case 10:
1273       case 12:
1274       case 13:
1275       case 14:
1276         w = (d==4) ? lr : nl;
1277         XSetForeground(st->display, st->fgc, 
1278           st->colors[color+1].pixel);
1279         XFillRectangle (st->display, st->window, st->fgc, 
1280           x, y, w, m2);
1281         XSetForeground(st->display, st->fgc, 
1282            st->colors[color+2].pixel);
1283         XFillRectangle (st->display, st->window, st->fgc, 
1284           x, y+m2, w, m1);
1285         break;
1286     }
1287     /* draw angles */
1288     switch(d) {
1289       case 1:                                      /* bottom end ^ */
1290         _XFillTriangle(st,color+2, x, y+lr, x+lr, y+lr, x+m2, y+m2);
1291         break;
1292       case 2:                                       /* top end \/ */
1293         _XFillTriangle(st,color+1, x, y, x+lr, y, x+m2, y+m2);
1294         break;
1295       case 4:                                       /* right end < */
1296         _XFillTriangle(st,color+3, x+lr, y, x+lr, y+lr, x+m2, y+m2);
1297         break;
1298       case 5:                                        /* LR corner */
1299         _XFillTriangle(st,color+1, x, y+m2, x+m2, y+m2, x, y);
1300         _XFillPolygon4(st,color+2, x, y+m2, x+m2, y+m2, x+lr, y+lr, x, y+lr);
1301         break;
1302       case 6:                                        /* UR corner */
1303         _XFillPolygon4(st,color+1, x, y+m2, x+m2, y+m2, x+lr, y, x, y);
1304         _XFillTriangle(st,color+2, x, y+m2, x+m2, y+m2, x, y+lr);
1305         break;
1306       case 7:                                        /* T > into line */
1307         _XFillTriangle(st,color+1, x, y+m2, x+m2, y+m2, x, y);
1308         _XFillTriangle(st,color+2, x, y+m2, x+m2, y+m2, x, y+lr);
1309         break;
1310       case 8:                                       /* left end > */
1311         _XFillTriangle(st,color, x, y, x, y+lr, x+m2, y+m2);
1312         break;
1313       case 9:                                       /* LL corner */
1314         _XFillPolygon4(st,color, x+m2, y, x+m2, y+m2, x, y+lr, x, y);
1315         _XFillTriangle(st,color+3, x+m2, y, x+m2, y+m2, x+lr, y);
1316         break;
1317       case 10:                                      /* UL corner */
1318         _XFillPolygon4(st,color, x+m2, y+nl, x+m2, y+m2, x, y, x, y+nl);
1319         _XFillPolygon4(st,color+3, x+m2, y+nl, x+m2, y+m2, x+lr, y+lr, x+lr, y+nl);
1320         break;
1321       case 11:                                       /* T < into line */
1322         _XFillPolygon4(st,color+1, x+nl, y+m2, x+m2, y+m2, x+lr, y, x+nl, y);
1323         _XFillPolygon4(st,color+2, x+nl, y+m2, x+m2, y+m2, x+lr, y+lr, x+nl, y+lr);
1324         break;
1325       case 13:                                        /* T \/ into line */
1326         _XFillTriangle(st,color, x+m2, y, x+m2, y+m2, x, y);
1327         _XFillTriangle(st,color+3, x+m2, y, x+m2, y+m2, x+lr, y);
1328         break;
1329       case 14:                                        /* T ^ into line */
1330         _XFillPolygon4(st,color, x+m2, y+nl, x+m2, y+m2, x, y+lr, x, y+nl);
1331         _XFillPolygon4(st,color+3, x+m2, y+nl, x+m2, y+m2, x+lr, y+lr, x+lr, y+nl);
1332         break;
1333       case 15:                                        /* X intersection */
1334         _XFillTriangle(st,color+1, x, y+m2, x+m2, y+m2, x, y);
1335         _XFillTriangle(st,color+2, x, y+m2, x+m2, y+m2, x, y+lr);
1336         _XFillPolygon4(st,color+1, x+nl, y+m2, x+m2, y+m2, x+lr, y, x+nl, y);
1337         _XFillPolygon4(st,color+2, x+nl, y+m2, x+m2, y+m2, x+lr, y+lr, x+nl, y+lr);
1338         break;
1339     }
1340     z+=a;
1341   }
1342 }
1343
1344 static long 
1345 _mselapsed(struct state *st)
1346 {
1347   struct timeval t;
1348   gettimeofday(&t, NULL);
1349   t.tv_sec -= st->time.tv_sec;
1350   t.tv_usec -= st->time.tv_usec;
1351   return ((long)t.tv_sec*1000000+t.tv_usec);
1352 }
1353
1354 static void 
1355 _draw_lines(struct state *st)
1356 {
1357   int n, z, a, color, sh, di;
1358   if (st->bi==1)
1359     for (a=0; a<=st->oi; a++)
1360       st->fdol[a]=0;
1361
1362   for (st->di=st->bi; st->di<_min(st->li+1,st->bi+st->lpu); st->di++) {
1363     color=(st->dline[st->di].color%st->ncolors)*st->shades;
1364     XSetForeground(st->display, st->fgc, st->colors[color].pixel);
1365
1366     switch (st->d3d) {
1367       case D3D_NEON:
1368         st->dline[st->di].ndol=st->fdol[st->dline[st->di].obj];
1369         st->fdol[st->dline[st->di].obj]=st->di;
1370         for (sh=0; sh<st->lwid/2; sh++) {
1371           XSetForeground(st->display, st->fgc, 
1372             st->colors[color+sh].pixel);
1373           di=st->di;
1374           while(di>0) {
1375             _XFillRectangle(st,di,sh);
1376             di=st->dline[di].ndol;
1377           }
1378         }
1379         break;
1380       case D3D_BLOCK:
1381         st->dline[st->di].ndol=st->fdol[st->dline[st->di].obj];
1382         st->fdol[st->dline[st->di].obj]=st->di;
1383         for (sh=0; sh<st->lwid/2; sh++) {
1384           XSetForeground(st->display, st->fgc, 
1385             st->colors[color+(st->lwid/2)-sh-1].pixel);
1386           di=st->di;
1387           while(di>0) {
1388             _XFillRectangle(st,di,sh);
1389             di=st->dline[di].ndol;
1390           }
1391         }
1392         break;
1393       case D3D_TILED:
1394         _draw_tiled(st,color);
1395         break;
1396       default:               /* D3D_NONE */
1397         _XFillRectangle(st,st->di,0);
1398         if (st->outline) {
1399           _fill_outline(st, st->di);
1400           z=st->dline[st->di].y*st->gridx+st->dline[st->di].x;
1401           a = (st->dline[st->di].hv) ? 1 : st->gridx;
1402           for (n=0; n<=st->dline[st->di].len; n++) {
1403             _fill_outline(st, st->grid[z].dhl);
1404             _fill_outline(st, st->grid[z].dhr);
1405             _fill_outline(st, st->grid[z].dvu);
1406             _fill_outline(st, st->grid[z].dvd);
1407             if (st->dline[st->di].hv) {
1408               if (n) 
1409                 st->grid[z].dhr=st->di;
1410               if (n<st->dline[st->di].len) 
1411                 st->grid[z].dhl=st->di;
1412             } else {
1413               if (n) 
1414                 st->grid[z].dvd=st->di;
1415               if (n<st->dline[st->di].len) 
1416                 st->grid[z].dvu=st->di;
1417             }
1418             z+=a;
1419           }
1420         }
1421         break;
1422     }
1423   }
1424   if (st->di>st->li) {
1425       st->bi=1;
1426       st->mode=MODE_CREATE;
1427   } else {
1428       st->bi+=st->lpu;
1429   }
1430 }
1431
1432 static void 
1433 _erase_lines(struct state *st)
1434 {
1435   if (!st->ii)
1436     return;
1437   for (st->di=st->bi; st->di<_min(st->eli+1,st->bi+st->elpu); st->di++) {
1438     if (st->eline[st->di].hv) {
1439       XFillRectangle (st->display, st->window, st->bgc, 
1440       st->eline[st->di].x*st->elwid, 
1441       st->eline[st->di].y*st->elwid,
1442       (st->eline[st->di].len+1)*st->elwid, st->elwid);
1443     } else {
1444       XFillRectangle (st->display, st->window, st->bgc, 
1445       st->eline[st->di].x*st->elwid, 
1446       st->eline[st->di].y*st->elwid,
1447       st->elwid, (st->eline[st->di].len+1)*st->elwid);
1448     }
1449     if (st->di==st->eli) /* clear just in case */
1450       XFillRectangle(st->display, st->window, st->bgc, 0, 0, 
1451         st->xgwa.width, st->xgwa.height);
1452   }
1453   if (st->di>st->eli) {
1454       st->bi=1;
1455       if (st->resized) {
1456          st->mode=MODE_CREATE;
1457       } else {
1458          st->mode=MODE_DRAW;
1459       }
1460   } else {
1461       st->bi+=st->elpu;
1462   }
1463 }
1464
1465 static void *
1466 abstractile_init(Display *display, Window window)
1467 {
1468   struct state *st = (struct state *) calloc (1, sizeof(*st));
1469   XGCValues gcv;
1470 /*  struct utsname os;*/
1471
1472   char *tile = get_string_resource(display, "tile", "Tile");
1473   if      (tile && !strcmp(tile, "random")) st->tile = TILE_RANDOM;
1474   else if (tile && !strcmp(tile, "flat")) st->tile = TILE_FLAT;
1475   else if (tile && !strcmp(tile, "thin")) st->tile = TILE_THIN;
1476   else if (tile && !strcmp(tile, "outline")) st->tile = TILE_OUTLINE;
1477   else if (tile && !strcmp(tile, "block")) st->tile = TILE_BLOCK;
1478   else if (tile && !strcmp(tile, "neon")) st->tile = TILE_NEON;
1479   else if (tile && !strcmp(tile, "tiled")) st->tile = TILE_TILED;
1480   else {
1481     if (tile && *tile && !!strcmp(tile, "random"))
1482       fprintf(stderr, "%s: unknown tile option %s\n", progname, tile);
1483     st->tile = TILE_RANDOM;
1484   }
1485
1486   st->speed = get_integer_resource(display, "speed", "Integer");
1487   if (st->speed < 0) st->speed = 0;
1488   if (st->speed > 5) st->speed = 5;
1489   st->sleep = get_integer_resource(display, "sleep", "Integer");
1490   if (st->sleep < 0) st->sleep = 0;
1491   if (st->sleep > 60) st->sleep = 60;
1492
1493   st->display=display;
1494   st->window=window;
1495
1496   /* get screen size and create Graphics Contexts */
1497   XGetWindowAttributes (display, window, &st->xgwa);
1498   gcv.foreground = get_pixel_resource(display, st->xgwa.colormap, 
1499       "foreground", "Foreground");
1500   st->fgc = XCreateGC (display, window, GCForeground, &gcv);
1501   gcv.foreground = get_pixel_resource(display, st->xgwa.colormap,
1502       "background", "Background");
1503   st->bgc = XCreateGC (display, window, GCForeground, &gcv);
1504
1505 /* Um, no. This is obscene. -jwz.
1506   uname(&os);
1507   st->newcols=((!strcmp(os.sysname,"Linux")) || (!strcmp(os.sysname,"Darwin")))
1508       ? True : False;
1509 */
1510   st->newcols=False;
1511
1512   st->mode=MODE_CREATE;
1513   st->ii=0;
1514   st->resized=True;
1515   return st;
1516 }
1517
1518 static unsigned long 
1519 abstractile_draw (Display *dpy, Window window, void *closure)
1520 {
1521   struct state *st = (struct state *) closure;
1522   int mse, usleep;
1523  
1524   gettimeofday(&st->time, NULL);
1525
1526   /* If the window is too small, do nothing, sorry! */
1527   if (st->xgwa.width > 20 && st->xgwa.height > 20) {
1528     switch (st->mode) {
1529     case MODE_CREATE:
1530       _init_screen(st);
1531       _create_screen(st);
1532       break;
1533     case MODE_ERASE:
1534       _erase_lines(st);
1535       break;
1536     case MODE_DRAW:
1537       _draw_lines(st);
1538       break;
1539     }
1540   }
1541   mse=_mselapsed(st);
1542   usleep = ((!st->ii) && (st->mode==MODE_CREATE)) ?  0 :
1543       (st->mode==MODE_CREATE) ?  st->sleep*1000000-mse : 
1544       /* speed=0-5, goal is 10,8,6,4,2,0 sec normal and 5,4,3,2,1,0 dialog */
1545       (5-st->speed)*(2-st->dialog)*100000/st->lpu-mse; 
1546   if (usleep>=0)
1547       return usleep;
1548   return 0;
1549 }
1550
1551 static void
1552 abstractile_reshape (Display *dpy, Window window, void *closure,
1553                  unsigned int w, unsigned int h)
1554 {
1555   struct state *st = (struct state *) closure;
1556   st->xgwa.width = w;
1557   st->xgwa.height = h;
1558   if (w*h>st->max_wxh)
1559     st->resized=True;
1560 }
1561
1562 static Bool
1563 abstractile_event (Display *dpy, Window window, void *closure, XEvent *event)
1564 {
1565   return False;
1566 }
1567
1568 static void
1569 abstractile_free (Display *dpy, Window window, void *closure)
1570 {
1571   struct state *st = (struct state *) closure;
1572   free (st);
1573 }
1574
1575 static const char *abstractile_defaults [] = {
1576   ".background:    black",
1577   ".foreground:    white",
1578   "*fpsSolid:      true",
1579   "*sleep:             3",
1580   "*speed:             3",
1581   "*tile:         random",
1582   0
1583 };
1584
1585 static XrmOptionDescRec abstractile_options [] = {
1586   { "-sleep",  ".sleep",  XrmoptionSepArg, 0 },
1587   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
1588   { "-tile",   ".tile",   XrmoptionSepArg, 0 },
1589   { 0, 0, 0, 0 }
1590 };
1591
1592 XSCREENSAVER_MODULE ("Abstractile", abstractile)