From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / hacks / xrayswarm.c
1 /*
2  * Copyright (c) 2000 by Chris Leger (xrayjones@users.sourceforge.net)
3  *
4  * xrayswarm - a shameless ripoff of the 'swarm' screensaver on SGI
5  *   boxes.
6  *
7  * Version 1.0 - initial release.  doesn't read any special command-line
8  *   options, and only supports the variable 'delay' via Xresources.
9  *   (the delay resouces is most useful on systems w/o gettimeofday, in
10  *   which case automagical level-of-detail for FPS maintainance can't
11  *   be used.)
12  *
13  *   The code isn't commented, but isn't too ugly. It should be pretty
14  *   easy to understand, with the exception of the colormap stuff.
15  *
16  */
17 /*
18 Permission is hereby granted, free of charge, to any person obtaining
19 a copy of this software and associated documentation files (the
20 "Software"), to deal in the Software without restriction, including
21 without limitation the rights to use, copy, modify, merge, publish,
22 distribute, sublicense, and/or sell copies of the Software, and to
23 permit persons to whom the Software is furnished to do so, subject to
24 the following conditions:
25
26 The above copyright notice and this permission notice shall be included
27 in all copies or substantial portions of the Software.
28
29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
30 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
32 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
33 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
34 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
35 OTHER DEALINGS IN THE SOFTWARE.
36
37 Except as contained in this notice, the name of the X Consortium shall
38 not be used in advertising or otherwise to promote the sale, use or
39 other dealings in this Software without prior written authorization
40 from the X Consortium.
41 */
42
43 #include <math.h>
44
45 #ifdef HAVE_CONFIG_H
46 # include "config.h"
47 #endif
48
49 #include <sys/time.h>
50 #include "screenhack.h"
51
52 #ifdef HAVE_JWXYZ
53 # define HAVE_GETTIMEOFDAY 1
54 #endif
55
56 /**********************************************************************
57  *                                                                     
58  * window crap 
59  *
60  **********************************************************************/
61
62 static const char *xrayswarm_defaults [] ={
63         ".lowrez:               true",
64         ".background:           black",
65         "*delay:                20000",
66         "*fpsSolid:             true",
67 #ifdef HAVE_MOBILE
68         "*ignoreRotation:       True",
69 #endif
70         0
71 };
72
73 static XrmOptionDescRec xrayswarm_options [] = {
74         {"-delay",".delay",XrmoptionSepArg,0},
75         {0,0,0,0}
76 };
77
78
79 /**********************************************************************
80  *                                                                     
81  * bug structs & variables
82  *
83  **********************************************************************/
84 #define MAX_TRAIL_LEN 60
85 #define MAX_BUGS 100
86 #define MAX_TARGETS 10
87 #define sq(x) ((x)*(x))
88
89 #define MAX_FPS 150
90 #define MIN_FPS 16
91 #define DESIRED_DT 0.2
92
93 typedef struct _sbug {
94   float pos[3];
95   int hist[MAX_TRAIL_LEN][2];
96   float vel[2];
97   struct _sbug *closest;
98 } bug;
99
100 #define GRAY_TRAILS 0
101 #define GRAY_SCHIZO 1
102 #define COLOR_TRAILS 2
103 #define RANDOM_TRAILS 3
104 #define RANDOM_SCHIZO 4
105 #define COLOR_SCHIZO 5
106 #define NUM_SCHEMES 6 /* too many schizos; don't use last 2 */  
107
108
109
110 struct state {
111   Display *dpy;
112   Window win;
113
114   unsigned char colors[768];
115
116   GC fgc[256];
117   GC cgc;
118   int xsize, ysize;
119   int xc, yc;
120   unsigned long delay;
121   float maxx, maxy;
122
123   float dt;
124   float targetVel;
125   float targetAcc;
126   float maxVel;
127   float maxAcc;
128   float noise;
129   float minVelMultiplier;
130
131   int nbugs;
132   int ntargets;
133   int trailLen;
134
135   float dtInv;
136   float halfDtSq;
137   float targetVelSq;
138   float maxVelSq;
139   float minVelSq;
140   float minVel;
141
142   bug bugs[MAX_BUGS];
143   bug targets[MAX_TARGETS];
144   int head;
145   int tail;
146   int colorScheme;
147   float changeProb;
148
149   int grayIndex[MAX_TRAIL_LEN];
150   int redIndex[MAX_TRAIL_LEN];
151   int blueIndex[MAX_TRAIL_LEN];
152   int graySIndex[MAX_TRAIL_LEN];
153   int redSIndex[MAX_TRAIL_LEN];
154   int blueSIndex[MAX_TRAIL_LEN];
155   int randomIndex[MAX_TRAIL_LEN];
156   int numColors;
157   int numRandomColors;
158
159   int checkIndex;
160   int rsc_callDepth;
161   int rbc_callDepth;
162
163   float draw_fps;
164   float draw_timePerFrame, draw_elapsed;
165   int *draw_targetColorIndex, *draw_colorIndex;
166   int draw_targetStartColor, draw_targetNumColors;
167   int draw_startColor, draw_numColors;
168   double draw_start, draw_end;
169   int draw_cnt;
170   int draw_sleepCount;
171   int draw_delayAccum;
172   int draw_nframes;
173
174   struct timeval startupTime;
175 };
176
177
178 typedef struct {
179   float dt;
180   float targetVel;
181   float targetAcc;
182   float maxVel;
183   float maxAcc;
184   float noise;
185
186   int nbugs;
187   int ntargets;
188   int trailLen;
189   int colorScheme;
190   int changeProb;
191 } bugParams;
192
193 #if 0
194 static const bugParams good1 = {
195   0.3,  /* dt */
196   0.03,  /* targetVel */
197   0.02,  /* targetAcc */
198   0.05,  /* maxVel */
199   0.03,  /* maxAcc */
200   0.01,  /* noise */
201   -1,    /* nbugs */
202   -1,     /* ntargets */
203   60,    /* trailLen */
204   2,     /* colorScheme */
205   0.15  /* changeProb */
206 };
207
208 static const bugParams *goodParams[] = { 
209 &good1
210 };
211
212 static int numParamSets = 1;
213 #endif
214
215 static void initCMap(struct state *st)
216 {
217   int i, n;
218   int temp;
219
220   n = 0;
221
222   /* color 0 is black */
223   st->colors[n++] = 0;
224   st->colors[n++] = 0;
225   st->colors[n++] = 0;
226
227   /* color 1 is red */
228   st->colors[n++] = 255;
229   st->colors[n++] = 0;
230   st->colors[n++] = 0;
231
232   /* color 2 is green */
233   st->colors[n++] = 255;
234   st->colors[n++] = 0;
235   st->colors[n++] = 0;
236
237   /* color 3 is blue */
238   st->colors[n++] = 255;
239   st->colors[n++] = 0;
240   st->colors[n++] = 0;
241
242   /* start greyscale colors at 4; 16 levels */
243   for (i = 0; i < 16; i++) {
244     temp = i*16;
245     if (temp > 255) temp = 255;
246     st->colors[n++] = 255 - temp;
247     st->colors[n++] = 255 - temp;
248     st->colors[n++] = 255 - temp;
249   }
250
251   /* start red fade at 20; 16 levels */
252   for (i = 0; i < 16; i++) {
253     temp = i*16;
254     if (temp > 255) temp = 255;
255     st->colors[n++] = 255 - temp;
256     st->colors[n++] = 255 - pow(i/16.0+0.001, 0.3)*255;
257     st->colors[n++] = 65 - temp/4;
258   }
259
260   /* start blue fade at 36; 16 levels */
261   for (i = 0; i < 16; i++) {
262     temp = i*16;
263     if (temp > 255) temp = 255;
264     st->colors[n++] = 32 - temp/8;
265     st->colors[n++] = 180 - pow(i/16.0+0.001, 0.3)*180;
266     st->colors[n++] = 255 - temp;
267   }
268
269   /* random colors start at 52 */
270   st->numRandomColors = MAX_TRAIL_LEN;
271
272   st->colors[n] = random()&255; n++;
273   st->colors[n] = random()&255; n++;
274   st->colors[n] = st->colors[n-2]/2 + st->colors[n-3]/2; n++;
275
276   for (i = 0; i < st->numRandomColors; i++) {
277     st->colors[n] = (st->colors[n-3] + (random()&31) - 16)&255; n++;
278     st->colors[n] = (st->colors[n-3] + (random()&31) - 16)&255; n++;
279     st->colors[n] = st->colors[n-2]/(float)(i+2) + st->colors[n-3]/(float)(i+2); n++;
280   }
281   
282   st->numColors = n/3 + 1;
283 }
284
285 static int initGraphics(struct state *st)
286 {
287   XGCValues xgcv;
288   XWindowAttributes xgwa;
289 /*  XSetWindowAttributes xswa;*/
290   Colormap cmap;
291   XColor color;
292   int n, i;
293   
294   initCMap(st);
295
296   XGetWindowAttributes(st->dpy,st->win,&xgwa);
297   cmap=xgwa.colormap;
298 /*  xswa.backing_store=Always;
299   XChangeWindowAttributes(st->dpy,st->win,CWBackingStore,&xswa);*/
300   xgcv.function=GXcopy;
301
302   st->delay = get_integer_resource(st->dpy, "delay","Integer");
303   
304   xgcv.foreground=get_pixel_resource (st->dpy, cmap, "background", "Background");
305   st->fgc[0]=XCreateGC(st->dpy, st->win, GCForeground|GCFunction,&xgcv);
306 #ifdef HAVE_JWXYZ
307   jwxyz_XSetAntiAliasing (st->dpy, st->fgc[0], False);
308 #endif
309   
310   n=0;
311   if (mono_p) {
312     xgcv.foreground=get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
313     st->fgc[1]=XCreateGC(st->dpy,st->win,GCForeground|GCFunction,&xgcv);
314 #ifdef HAVE_JWXYZ
315     jwxyz_XSetAntiAliasing (st->dpy, st->fgc[1], False);
316 #endif
317     for (i=0;i<st->numColors;i+=2) st->fgc[i]=st->fgc[0];
318     for (i=1;i<st->numColors;i+=2) st->fgc[i]=st->fgc[1];
319   } else {
320     for (i = 0; i < st->numColors; i++) {
321       color.red=st->colors[n++]<<8;
322       color.green=st->colors[n++]<<8;
323       color.blue=st->colors[n++]<<8;
324       color.flags=DoRed|DoGreen|DoBlue;
325       XAllocColor(st->dpy,cmap,&color);
326       xgcv.foreground=color.pixel;
327       st->fgc[i] = XCreateGC(st->dpy, st->win, GCForeground | GCFunction,&xgcv);
328 #ifdef HAVE_JWXYZ
329       jwxyz_XSetAntiAliasing (st->dpy, st->fgc[i], False);
330 #endif
331     }
332   }
333   st->cgc = XCreateGC(st->dpy,st->win,GCForeground|GCFunction,&xgcv);
334   XSetGraphicsExposures(st->dpy,st->cgc,False);
335 #ifdef HAVE_JWXYZ
336   jwxyz_XSetAntiAliasing (st->dpy, st->cgc, False);
337 #endif
338
339   st->xsize = xgwa.width;
340   st->ysize = xgwa.height;
341   st->xc = st->xsize >> 1;
342   st->yc = st->ysize >> 1;
343
344   st->maxx = 1.0;
345   st->maxy = st->ysize/(float)st->xsize;
346
347   if (st->colorScheme < 0) st->colorScheme = random()%NUM_SCHEMES;
348
349   return True;
350 }
351
352 static void initBugs(struct state *st)
353 {
354   register bug *b;
355   int i;
356
357   st->head = st->tail = 0;
358
359   memset((char *)st->bugs, 0,MAX_BUGS*sizeof(bug));
360   memset((char *)st->targets, 0, MAX_TARGETS*sizeof(bug));
361
362   if (st->ntargets < 0) st->ntargets = (0.25+frand(0.75)*frand(1))*MAX_TARGETS;
363   if (st->ntargets < 1) st->ntargets = 1;
364
365   if (st->nbugs < 0) st->nbugs = (0.25+frand(0.75)*frand(1))*MAX_BUGS;
366   if (st->nbugs <= st->ntargets) st->nbugs = st->ntargets+1;
367
368   if (st->trailLen < 0) {
369     st->trailLen = (1.0 - frand(0.6)*frand(1))*MAX_TRAIL_LEN;
370   }
371
372   if (st->nbugs > MAX_BUGS) st->nbugs = MAX_BUGS;
373   if (st->ntargets > MAX_TARGETS) st->ntargets = MAX_TARGETS;
374   if (st->trailLen > MAX_TRAIL_LEN) st->trailLen = MAX_TRAIL_LEN;
375
376   b = st->bugs;
377   for (i = 0; i < st->nbugs; i++, b++) {
378     b->pos[0] = frand(st->maxx);
379     b->pos[1] = frand(st->maxy);
380     b->vel[0] = frand(st->maxVel/2);
381     b->vel[1] = frand(st->maxVel/2);
382
383     b->hist[st->head][0] = b->pos[0]*st->xsize;
384     b->hist[st->head][1] = b->pos[1]*st->xsize;
385     b->closest = &st->targets[random()%st->ntargets];
386   }
387
388   b = st->targets;
389   for (i = 0; i < st->ntargets; i++, b++) {
390     b->pos[0] = frand(st->maxx);
391     b->pos[1] = frand(st->maxy);
392
393     b->vel[0] = frand(st->targetVel/2);
394     b->vel[1] = frand(st->targetVel/2);
395
396     b->hist[st->head][0] = b->pos[0]*st->xsize;
397     b->hist[st->head][1] = b->pos[1]*st->xsize;
398   }
399 }
400
401 static void pickNewTargets(struct state *st)
402 {
403   register int i;
404   register bug *b;
405
406   b = st->bugs;
407   for (i = 0; i < st->nbugs; i++, b++) {
408     b->closest = &st->targets[random()%st->ntargets];
409   }
410 }
411
412 #if 0
413 static void addBugs(int numToAdd)
414 {
415   register bug *b;
416   int i;
417
418   if (numToAdd + st->nbugs > MAX_BUGS) numToAdd = MAX_BUGS-st->nbugs;
419   else if (numToAdd < 0) numToAdd = 0;
420
421   for (i = 0; i < numToAdd; i++) {
422     b = &st->bugs[random()%st->nbugs];
423     memcpy((char *)&st->bugs[st->nbugs+i], (char *)b, sizeof(bug));
424     b->closest = &st->targets[random()%st->ntargets];
425   }
426
427   st->nbugs += numToAdd;
428 }
429
430 static void addTargets(int numToAdd)
431 {
432   register bug *b;
433   int i;
434
435   if (numToAdd + st->ntargets > MAX_TARGETS) numToAdd = MAX_TARGETS-st->ntargets;
436   else if (numToAdd < 0) numToAdd = 0;
437
438   for (i = 0; i < numToAdd; i++) {
439     b = &st->targets[random()%st->ntargets];
440     memcpy((char *)&st->targets[st->ntargets+i], (char *)b, sizeof(bug));
441     b->closest = &st->targets[random()%st->ntargets];
442   }
443
444   st->ntargets += numToAdd;
445 }
446 #endif
447
448 static void computeConstants(struct state *st)
449 {
450   st->halfDtSq = st->dt*st->dt*0.5;
451   st->dtInv = 1.0/st->dt;
452   st->targetVelSq = st->targetVel*st->targetVel;
453   st->maxVelSq = st->maxVel*st->maxVel;
454   st->minVel = st->maxVel*st->minVelMultiplier;
455   st->minVelSq = st->minVel*st->minVel;
456 }
457
458 static void computeColorIndices(struct state *st)
459 {
460   int i;
461   int schizoLength;
462
463   /* note: colors are used in *reverse* order! */
464
465   /* grayscale */
466   for (i = 0; i < st->trailLen; i++) {
467     st->grayIndex[st->trailLen-1-i] = 4 + i*16.0/st->trailLen + 0.5;
468     if (st->grayIndex[st->trailLen-1-i] > 19) st->grayIndex[st->trailLen-1-i] = 19;
469   }
470
471   /* red */
472   for (i = 0; i < st->trailLen; i++) {
473     st->redIndex[st->trailLen-1-i] = 20 + i*16.0/st->trailLen + 0.5;
474     if (st->redIndex[st->trailLen-1-i] > 35) st->redIndex[st->trailLen-1-i] = 35;
475   }
476
477   /* blue */
478   for (i = 0; i < st->trailLen; i++) {
479     st->blueIndex[st->trailLen-1-i] = 36 + i*16.0/st->trailLen + 0.5;
480     if (st->blueIndex[st->trailLen-1-i] > 51) st->blueIndex[st->trailLen-1-i] = 51;
481   }
482
483   /* gray schizo - same as gray*/
484   for (i = 0; i < st->trailLen; i++) {
485     st->graySIndex[st->trailLen-1-i] = 4 + i*16.0/st->trailLen + 0.5;
486     if (st->graySIndex[st->trailLen-1-i] > 19) st->graySIndex[st->trailLen-1-i] = 19;
487   }
488
489   /*schizoLength = st->trailLen/4;
490   if (schizoLength < 3) schizoLength = 3;*/
491   /* red schizo */
492   for (i = 0; i < st->trailLen; i++) {
493     /*    redSIndex[trailLen-1-i] = 
494           20 + 16.0*(i%schizoLength)/(schizoLength-1.0) + 0.5;*/
495     st->redSIndex[st->trailLen-1-i] = 20 + i*16.0/st->trailLen + 0.5;
496     if (st->redSIndex[st->trailLen-1-i] > 35) st->redSIndex[st->trailLen-1-i] = 35;
497   }
498
499   schizoLength = st->trailLen/2;
500   if (schizoLength < 3) schizoLength = 3;
501   /* blue schizo is next */
502   for (i = 0; i < st->trailLen; i++) {
503     st->blueSIndex[st->trailLen-1-i] = 
504       36 + 16.0*(i%schizoLength)/(schizoLength-1.0) + 0.5;
505     if (st->blueSIndex[st->trailLen-1-i] > 51) st->blueSIndex[st->trailLen-1-i] = 51;
506   }
507
508   /* random is next */
509   for (i = 0; i < st->trailLen; i++) {
510     st->randomIndex[i] = 52 + random()%(st->numRandomColors);
511   }
512 }
513
514 #if 0
515 static void setParams(bugParams *p)
516 {
517   st->dt = p->dt;
518   st->targetVel = p->targetVel;
519   st->targetAcc = p->targetAcc;
520   st->maxVel = p->maxVel;
521   st->maxAcc = p->maxAcc;
522   st->noise = p->noise;
523
524   st->nbugs = p->nbugs;
525   st->ntargets = p->ntargets;
526   st->trailLen = p->trailLen;
527   st->colorScheme = p->colorScheme;
528   st->changeProb = p->changeProb;
529   computeConstants();
530   computeColorIndices();
531 }
532 #endif
533
534 static void drawBugs(struct state *st, int *tColorIdx, int tci0, int tnc,
535                      int *colorIdx, int ci0, int nc)
536 {
537   register bug *b;
538   register int i, j;
539   int temp;
540
541   if (((st->head+1)%st->trailLen) == st->tail) {
542     /* first, erase last segment of bugs if necessary */
543     temp = (st->tail+1) % st->trailLen;
544     
545     b = st->bugs;
546     for (i = 0; i < st->nbugs; i++, b++) {
547       XDrawLine(st->dpy, st->win, st->fgc[0],
548                 b->hist[st->tail][0], b->hist[st->tail][1],
549                 b->hist[temp][0], b->hist[temp][1]);
550     }
551   
552     b = st->targets;
553     for (i = 0; i < st->ntargets; i++, b++) {
554       XDrawLine(st->dpy, st->win, st->fgc[0],
555                 b->hist[st->tail][0], b->hist[st->tail][1],
556                 b->hist[temp][0], b->hist[temp][1]);
557     }
558     st->tail = (st->tail+1)%st->trailLen;
559   }
560   
561   for (j = st->tail; j != st->head; j = temp) {
562     temp = (j+1)%st->trailLen;
563
564     b = st->bugs;
565     for (i = 0; i < st->nbugs; i++, b++) {
566       XDrawLine(st->dpy, st->win, st->fgc[colorIdx[ci0]],
567                 b->hist[j][0], b->hist[j][1],
568                 b->hist[temp][0], b->hist[temp][1]);
569     }
570     
571     b = st->targets;
572     for (i = 0; i < st->ntargets; i++, b++) {
573       XDrawLine(st->dpy, st->win, st->fgc[tColorIdx[tci0]],
574                 b->hist[j][0], b->hist[j][1],
575                 b->hist[temp][0], b->hist[temp][1]);
576     }
577     ci0 = (ci0+1)%nc;  
578     tci0 = (tci0+1)%tnc;  
579   }
580 }
581
582 static void clearBugs(struct state *st)
583 {
584   register bug *b;
585   register int i, j;
586   int temp;
587
588   st->tail = st->tail-1;
589   if (st->tail < 0) st->tail = st->trailLen-1;
590
591   if (((st->head+1)%st->trailLen) == st->tail) {
592     /* first, erase last segment of bugs if necessary */
593     temp = (st->tail+1) % st->trailLen;
594     
595     b = st->bugs;
596     for (i = 0; i < st->nbugs; i++, b++) {
597       XDrawLine(st->dpy, st->win, st->fgc[0],
598                 b->hist[st->tail][0], b->hist[st->tail][1],
599                 b->hist[temp][0], b->hist[temp][1]);
600     }
601   
602     b = st->targets;
603     for (i = 0; i < st->ntargets; i++, b++) {
604       XDrawLine(st->dpy, st->win, st->fgc[0],
605                 b->hist[st->tail][0], b->hist[st->tail][1],
606                 b->hist[temp][0], b->hist[temp][1]);
607     }
608     st->tail = (st->tail+1)%st->trailLen;
609   }
610   
611   for (j = st->tail; j != st->head; j = temp) {
612     temp = (j+1)%st->trailLen;
613
614     b = st->bugs;
615     for (i = 0; i < st->nbugs; i++, b++) {
616       XDrawLine(st->dpy, st->win, st->fgc[0],
617                 b->hist[j][0], b->hist[j][1],
618                 b->hist[temp][0], b->hist[temp][1]);
619     }
620     
621     b = st->targets;
622     for (i = 0; i < st->ntargets; i++, b++) {
623       XDrawLine(st->dpy, st->win, st->fgc[0],
624                 b->hist[j][0], b->hist[j][1],
625                 b->hist[temp][0], b->hist[temp][1]);
626     }
627   }
628 }
629
630 static void updateState(struct state *st)
631 {
632   register int i;
633   register bug *b;
634   register float ax, ay, temp;
635   float theta;
636   bug *b2;
637   int j;
638
639   st->head = (st->head+1)%st->trailLen;
640   
641   for (j = 0; j < 5; j++) {
642     /* update closets bug for the bug indicated by checkIndex */
643     st->checkIndex = (st->checkIndex+1)%st->nbugs;
644     b = &st->bugs[st->checkIndex];
645
646     ax = b->closest->pos[0] - b->pos[0];
647     ay = b->closest->pos[1] - b->pos[1];
648     temp = ax*ax + ay*ay;
649     for (i = 0; i < st->ntargets; i++) {
650       b2 = &st->targets[i];
651       if (b2 == b->closest) continue;
652       ax = b2->pos[0] - b->pos[0];
653       ay = b2->pos[1] - b->pos[1];
654       theta = ax*ax + ay*ay;
655       if (theta < temp*2) {
656         b->closest = b2;
657         temp = theta;
658       }
659     }
660   }
661   
662   /* update target state */
663
664   b = st->targets;
665   for (i = 0; i < st->ntargets; i++, b++) {
666     theta = frand(6.28);
667     ax = st->targetAcc*cos(theta);
668     ay = st->targetAcc*sin(theta);
669
670     b->vel[0] += ax*st->dt;
671     b->vel[1] += ay*st->dt;
672
673     /* check velocity */
674     temp = sq(b->vel[0]) + sq(b->vel[1]);
675     if (temp > st->targetVelSq) {
676       temp = st->targetVel/sqrt(temp);
677       /* save old vel for acc computation */
678       ax = b->vel[0];
679       ay = b->vel[1];
680
681       /* compute new velocity */
682       b->vel[0] *= temp;
683       b->vel[1] *= temp;
684       
685       /* update acceleration */
686       ax = (b->vel[0]-ax)*st->dtInv;
687       ay = (b->vel[1]-ay)*st->dtInv;
688     }
689
690     /* update position */
691     b->pos[0] += b->vel[0]*st->dt + ax*st->halfDtSq;
692     b->pos[1] += b->vel[1]*st->dt + ay*st->halfDtSq;
693
694     /* check limits on targets */
695     if (b->pos[0] < 0) {
696       /* bounce */
697       b->pos[0] = -b->pos[0];
698       b->vel[0] = -b->vel[0];
699     } else if (b->pos[0] >= st->maxx) {
700       /* bounce */
701       b->pos[0] = 2*st->maxx-b->pos[0];
702       b->vel[0] = -b->vel[0];
703     }
704     if (b->pos[1] < 0) {
705       /* bounce */
706       b->pos[1] = -b->pos[1];
707       b->vel[1] = -b->vel[1];
708     } else if (b->pos[1] >= st->maxy) {
709       /* bounce */
710       b->pos[1] = 2*st->maxy-b->pos[1];
711       b->vel[1] = -b->vel[1];
712     }
713
714     b->hist[st->head][0] = b->pos[0]*st->xsize;
715     b->hist[st->head][1] = b->pos[1]*st->xsize;
716   }
717
718   /* update bug state */
719   b = st->bugs;
720   for (i = 0; i < st->nbugs; i++, b++) {
721     theta = atan2(b->closest->pos[1] - b->pos[1] + frand(st->noise),
722                   b->closest->pos[0] - b->pos[0] + frand(st->noise));
723     ax = st->maxAcc*cos(theta);
724     ay = st->maxAcc*sin(theta);
725
726     b->vel[0] += ax*st->dt;
727     b->vel[1] += ay*st->dt;
728
729     /* check velocity */
730     temp = sq(b->vel[0]) + sq(b->vel[1]);
731     if (temp > st->maxVelSq) {
732       temp = st->maxVel/sqrt(temp);
733
734       /* save old vel for acc computation */
735       ax = b->vel[0];
736       ay = b->vel[1];
737
738       /* compute new velocity */
739       b->vel[0] *= temp;
740       b->vel[1] *= temp;
741       
742       /* update acceleration */
743       ax = (b->vel[0]-ax)*st->dtInv;
744       ay = (b->vel[1]-ay)*st->dtInv;
745     } else if (temp < st->minVelSq) {
746       temp = st->minVel/sqrt(temp);
747
748       /* save old vel for acc computation */
749       ax = b->vel[0];
750       ay = b->vel[1];
751
752       /* compute new velocity */
753       b->vel[0] *= temp;
754       b->vel[1] *= temp;
755       
756       /* update acceleration */
757       ax = (b->vel[0]-ax)*st->dtInv;
758       ay = (b->vel[1]-ay)*st->dtInv;
759     }
760
761     /* update position */
762     b->pos[0] += b->vel[0]*st->dt + ax*st->halfDtSq;
763     b->pos[1] += b->vel[1]*st->dt + ay*st->halfDtSq;
764
765     /* check limits on targets */
766     if (b->pos[0] < 0) {
767       /* bounce */
768       b->pos[0] = -b->pos[0];
769       b->vel[0] = -b->vel[0];
770     } else if (b->pos[0] >= st->maxx) {
771       /* bounce */
772       b->pos[0] = 2*st->maxx-b->pos[0];
773       b->vel[0] = -b->vel[0];
774     }
775     if (b->pos[1] < 0) {
776       /* bounce */
777       b->pos[1] = -b->pos[1];
778       b->vel[1] = -b->vel[1];
779     } else if (b->pos[1] >= st->maxy) {
780       /* bounce */
781       b->pos[1] = 2*st->maxy-b->pos[1];
782       b->vel[1] = -b->vel[1];
783     }
784
785     b->hist[st->head][0] = b->pos[0]*st->xsize;
786     b->hist[st->head][1] = b->pos[1]*st->xsize;
787   }
788 }
789
790 static void mutateBug(struct state *st, int which)
791 {
792   int i, j;
793
794   if (which == 0) {
795     /* turn bug into target */
796     if (st->ntargets < MAX_TARGETS-1 && st->nbugs > 1) {
797       i = random() % st->nbugs;
798       memcpy((char *)&st->targets[st->ntargets], (char *)&st->bugs[i], sizeof(bug));
799       memcpy((char *)&st->bugs[i], (char *)&st->bugs[st->nbugs-1], sizeof(bug));
800       st->targets[st->ntargets].pos[0] = frand(st->maxx);
801       st->targets[st->ntargets].pos[1] = frand(st->maxy);
802       st->nbugs--;
803       st->ntargets++;
804
805       for (i = 0; i < st->nbugs; i += st->ntargets) {
806         st->bugs[i].closest = &st->targets[st->ntargets-1];
807       }
808     }
809   } else {
810     /* turn target into bug */
811     if (st->ntargets > 1 && st->nbugs < MAX_BUGS-1) {
812       /* pick a target */
813       i = random() % st->ntargets;
814
815       /* copy state into a new bug */
816       memcpy((char *)&st->bugs[st->nbugs], (char *)&st->targets[i], sizeof(bug));
817       st->ntargets--;
818
819       /* pick a target for the new bug */
820       st->bugs[st->nbugs].closest = &st->targets[random()%st->ntargets];
821
822       for (j = 0; j < st->nbugs; j++) {
823         if (st->bugs[j].closest == &st->targets[st->ntargets]) {
824           st->bugs[j].closest = &st->targets[i];
825         } else if (st->bugs[j].closest == &st->targets[i]) {
826           st->bugs[j].closest = &st->targets[random()%st->ntargets];
827         }
828       }
829       st->nbugs++;
830       
831       /* copy the last ntarget into the one we just deleted */
832       memcpy(&st->targets[i], (char *)&st->targets[st->ntargets], sizeof(bug));
833     }
834   }
835 }
836
837 static void mutateParam(float *param)
838 {
839   *param *= 0.75+frand(0.5);
840 }
841
842 static void randomSmallChange(struct state *st)
843 {
844   int whichCase = 0;
845
846   whichCase = random()%11;
847
848   if (++st->rsc_callDepth > 10) {
849     st->rsc_callDepth--;
850     return;
851   }
852   
853   switch(whichCase) {
854   case 0:
855     /* acceleration */
856     mutateParam(&st->maxAcc);
857     break;
858
859   case 1:
860     /* target acceleration */
861     mutateParam(&st->targetAcc);
862     break;
863
864   case 2:
865     /* velocity */
866     mutateParam(&st->maxVel);
867     break;
868
869   case 3: 
870     /* target velocity */
871     mutateParam(&st->targetVel);
872     break;
873
874   case 4:
875     /* noise */
876     mutateParam(&st->noise);
877     break;
878
879   case 5:
880     /* minVelMultiplier */
881     mutateParam(&st->minVelMultiplier);
882     break;
883     
884   case 6:
885   case 7:
886     /* target to bug */
887     if (st->ntargets < 2) break;
888     mutateBug(st, 1);
889     break;
890
891   case 8:
892     /* bug to target */
893     if (st->nbugs < 2) break;
894     mutateBug(st, 0);
895     if (st->nbugs < 2) break;
896     mutateBug(st, 0);
897     break;
898
899   case 9:
900     /* color scheme */
901     st->colorScheme = random()%NUM_SCHEMES;
902     if (st->colorScheme == RANDOM_SCHIZO || st->colorScheme == COLOR_SCHIZO) {
903       /* don't use these quite as much */
904       st->colorScheme = random()%NUM_SCHEMES;
905     }
906     break;
907
908   default:
909     randomSmallChange(st);
910     randomSmallChange(st);
911     randomSmallChange(st);
912     randomSmallChange(st);
913   }
914
915   if (st->minVelMultiplier < 0.3) st->minVelMultiplier = 0.3;
916   else if (st->minVelMultiplier > 0.9) st->minVelMultiplier = 0.9;
917   if (st->noise < 0.01) st->noise = 0.01;
918   if (st->maxVel < 0.02) st->maxVel = 0.02;
919   if (st->targetVel < 0.02) st->targetVel = 0.02;
920   if (st->targetAcc > st->targetVel*0.7) st->targetAcc = st->targetVel*0.7;
921   if (st->maxAcc > st->maxVel*0.7) st->maxAcc = st->maxVel*0.7;
922   if (st->targetAcc > st->targetVel*0.7) st->targetAcc = st->targetVel*0.7;
923   if (st->maxAcc < 0.01) st->maxAcc = 0.01;
924   if (st->targetAcc < 0.005) st->targetAcc = 0.005;
925
926   computeConstants(st);
927   st->rsc_callDepth--;
928 }
929
930 static void randomBigChange(struct state *st)
931 {
932   int whichCase = 0;
933   int temp;
934
935   whichCase = random()%4;
936   
937   if (++st->rbc_callDepth > 3) {
938     st->rbc_callDepth--;
939     return;
940   }
941
942   switch(whichCase) {
943   case 0:
944     /* trail length */
945     temp = (random()%(MAX_TRAIL_LEN-25)) + 25;
946     clearBugs(st);
947     st->trailLen = temp;
948     computeColorIndices(st);
949     initBugs(st);
950     break;
951
952   case 1:  
953     /* Whee! */
954     randomSmallChange(st);
955     randomSmallChange(st);
956     randomSmallChange(st);
957     randomSmallChange(st);
958     randomSmallChange(st);
959     randomSmallChange(st);
960     randomSmallChange(st);
961     randomSmallChange(st);
962     break;
963
964   case 2:
965     clearBugs(st);
966     initBugs(st);
967     break;
968     
969   case 3:
970     pickNewTargets(st);
971     break;
972     
973   default:
974     temp = random()%st->ntargets;
975     st->targets[temp].pos[0] += frand(st->maxx/4)-st->maxx/8;
976     st->targets[temp].pos[1] += frand(st->maxy/4)-st->maxy/8;
977     /* updateState() will fix bounds */
978     break;
979   }
980
981   st->rbc_callDepth--;
982 }
983
984 static void updateColorIndex(struct state *st, 
985                              int **tColorIdx, int *tci0, int *tnc,
986                       int **colorIdx, int *ci0, int *nc)
987 {
988   switch(st->colorScheme) {
989   case COLOR_TRAILS:
990     *tColorIdx = st->redIndex;
991     *tci0 = 0;
992     *tnc = st->trailLen;
993     *colorIdx = st->blueIndex;
994     *ci0 = 0;
995     *nc = st->trailLen;
996     break;
997
998   case GRAY_SCHIZO:
999     *tColorIdx = st->graySIndex;
1000     *tci0 = st->head;
1001     *tnc = st->trailLen;
1002     *colorIdx = st->graySIndex;
1003     *ci0 = st->head;
1004     *nc = st->trailLen;
1005     break;
1006
1007   case COLOR_SCHIZO:
1008     *tColorIdx = st->redSIndex;
1009     *tci0 = st->head;
1010     *tnc = st->trailLen;
1011     *colorIdx = st->blueSIndex;
1012     *ci0 = st->head;
1013     *nc = st->trailLen;
1014     break;
1015
1016   case GRAY_TRAILS:
1017     *tColorIdx = st->grayIndex;
1018     *tci0 = 0;
1019     *tnc = st->trailLen;
1020     *colorIdx = st->grayIndex;
1021     *ci0 = 0;
1022     *nc = st->trailLen;
1023     break;
1024
1025   case RANDOM_TRAILS:
1026     *tColorIdx = st->redIndex;
1027     *tci0 = 0;
1028     *tnc = st->trailLen;
1029     *colorIdx = st->randomIndex;
1030     *ci0 = 0;
1031     *nc = st->trailLen;
1032     break;
1033
1034   case RANDOM_SCHIZO:
1035     *tColorIdx = st->redIndex;
1036     *tci0 = st->head;
1037     *tnc = st->trailLen;
1038     *colorIdx = st->randomIndex;
1039     *ci0 = st->head;
1040     *nc = st->trailLen;
1041     break;
1042   }
1043 }
1044
1045 #if HAVE_GETTIMEOFDAY
1046 static void initTime(struct state *st)
1047 {
1048 #if GETTIMEOFDAY_TWO_ARGS
1049   gettimeofday(&st->startupTime, NULL);
1050 #else
1051   gettimeofday(&st->startupTime);
1052 #endif
1053 }
1054
1055 static double getTime(struct state *st)
1056 {
1057   struct timeval t;
1058   float f;
1059 #if GETTIMEOFDAY_TWO_ARGS
1060   gettimeofday(&t, NULL);
1061 #else
1062   gettimeofday(&t);
1063 #endif
1064   t.tv_sec -= st->startupTime.tv_sec;
1065   f = ((double)t.tv_sec) + t.tv_usec*1e-6;
1066   return f;
1067 }
1068 #endif
1069
1070 static void *
1071 xrayswarm_init (Display *d, Window w)
1072 {
1073   struct state *st = (struct state *) calloc (1, sizeof(*st));
1074   int i;
1075
1076   st->dpy = d;
1077   st->win = w;
1078
1079   st->dt = 0.3;
1080   st->targetVel = 0.03;
1081   st->targetAcc = 0.02;
1082   st->maxVel = 0.05;
1083   st->maxAcc = 0.03;
1084   st->noise = 0.01;
1085   st->minVelMultiplier = 0.5;
1086
1087   st->nbugs = -1;
1088   st->ntargets = -1;
1089   st->trailLen = -1;
1090
1091   st->colorScheme = /* -1 */  2;
1092   st->changeProb = 0.08;
1093
1094   if (!initGraphics(st)) abort();
1095
1096   computeConstants(st);
1097   initBugs(st);
1098   initTime(st);
1099   computeColorIndices(st);
1100
1101   if (st->changeProb > 0) {
1102     for (i = random()%5+5; i >= 0; i--) {
1103       randomSmallChange(st);
1104     }
1105   }
1106
1107   return st;
1108 }
1109
1110 static unsigned long
1111 xrayswarm_draw (Display *d, Window w, void *closure)
1112 {
1113   struct state *st = (struct state *) closure;
1114   unsigned long this_delay = st->delay;
1115
1116 #if HAVE_GETTIMEOFDAY
1117   st->draw_start = getTime(st);
1118 #endif
1119
1120     if (st->delay > 0) {
1121       st->draw_cnt = 2;
1122       st->dt = DESIRED_DT/2;
1123     } else {
1124       st->draw_cnt = 1;
1125       st->dt = DESIRED_DT;
1126     }
1127
1128     for (; st->draw_cnt > 0; st->draw_cnt--) {
1129       updateState(st);
1130       updateColorIndex(st, &st->draw_targetColorIndex, &st->draw_targetStartColor, &st->draw_targetNumColors,
1131                        &st->draw_colorIndex, &st->draw_startColor, &st->draw_numColors);
1132       drawBugs(st, st->draw_targetColorIndex, st->draw_targetStartColor, st->draw_targetNumColors,
1133                st->draw_colorIndex, st->draw_startColor, st->draw_numColors);
1134     }
1135 #if HAVE_GETTIMEOFDAY
1136     st->draw_end = getTime(st);
1137     st->draw_nframes++;
1138
1139     if (st->draw_end > st->draw_start+0.5) {
1140       if (frand(1.0) < st->changeProb) randomSmallChange(st);
1141       if (frand(1.0) < st->changeProb*0.3) randomBigChange(st);
1142       st->draw_elapsed = st->draw_end-st->draw_start;
1143         
1144       st->draw_timePerFrame = st->draw_elapsed/st->draw_nframes - st->delay*1e-6;
1145       st->draw_fps = st->draw_nframes/st->draw_elapsed;
1146       /*
1147       printf("elapsed: %.3f\n", elapsed);
1148       printf("fps: %.1f  secs per frame: %.3f  delay: %f\n", 
1149              fps, timePerFrame, delay);
1150       */
1151
1152       if (st->draw_fps > MAX_FPS) {
1153         st->delay = (1.0/MAX_FPS - (st->draw_timePerFrame + st->delay*1e-6))*1e6;
1154       } else if (st->dt*st->draw_fps < MIN_FPS*DESIRED_DT) {
1155         /* need to speed things up somehow */
1156         if (0 && st->nbugs > 10) {
1157           /*printf("reducing bugs to improve speed.\n");*/
1158           clearBugs(st);
1159           st->nbugs *= st->draw_fps/MIN_FPS;
1160           if (st->ntargets >= st->nbugs/2) mutateBug(st, 1);
1161         } else if (0 && st->dt < 0.3) {
1162           /*printf("increasing dt to improve speed.\n");*/
1163           st->dt *= MIN_FPS/st->draw_fps;
1164           computeConstants(st);
1165         } else if (st->trailLen > 10) {
1166           /*printf("reducing trail length to improve speed.\n");*/
1167           clearBugs(st);
1168           st->trailLen = st->trailLen * (st->draw_fps/MIN_FPS);
1169           if (st->trailLen < 10) st->trailLen = 10;
1170           computeColorIndices(st);
1171           initBugs(st);
1172         }
1173       }
1174
1175       st->draw_start = getTime(st);
1176       st->draw_nframes = 0;
1177     }
1178 #else
1179     if (frand(1) < st->changeProb*2/100.0) randomSmallChange(st);
1180     if (frand(1) < st->changeProb*0.3/100.0) randomBigChange(st);
1181 #endif
1182     
1183     if (st->delay <= 10000) {
1184       st->draw_delayAccum += st->delay;
1185       if (st->draw_delayAccum > 10000) {
1186         this_delay = st->draw_delayAccum;
1187         st->draw_delayAccum = 0;
1188         st->draw_sleepCount = 0;
1189       }
1190       if (++st->draw_sleepCount > 2) {
1191         st->draw_sleepCount = 0;
1192         this_delay = 10000;
1193       }
1194     }
1195
1196     return this_delay;
1197 }
1198
1199 static void
1200 xrayswarm_reshape (Display *dpy, Window window, void *closure, 
1201                  unsigned int w, unsigned int h)
1202 {
1203   struct state *st = (struct state *) closure;
1204   st->xsize = w;
1205   st->ysize = h;
1206   st->xc = st->xsize >> 1;
1207   st->yc = st->ysize >> 1;
1208   st->maxy = st->ysize/(float)st->xsize;
1209 }
1210
1211 static Bool
1212 xrayswarm_event (Display *dpy, Window window, void *closure, XEvent *event)
1213 {
1214   return False;
1215 }
1216
1217 static void
1218 xrayswarm_free (Display *dpy, Window window, void *closure)
1219 {
1220   struct state *st = (struct state *) closure;
1221   free (st);
1222 }
1223
1224 XSCREENSAVER_MODULE ("XRaySwarm", xrayswarm)