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