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