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