]> git.hungrycats.org Git - xscreensaver/blob - hacks/xrayswarm.c
From https://www.jwz.org/xscreensaver/xscreensaver-6.09.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 "screenhack.h"
44
45 #include <math.h>
46 #include <sys/time.h>
47
48 #ifdef HAVE_JWXYZ
49 # define HAVE_GETTIMEOFDAY 1
50 #endif
51
52 /**********************************************************************
53  *                                                                     
54  * window crap 
55  *
56  **********************************************************************/
57
58 static const char *xrayswarm_defaults [] ={
59 /*      ".lowrez:               true", */
60         ".background:           black",
61         "*delay:                20000",
62         "*fpsSolid:             true",
63 #ifdef HAVE_MOBILE
64         "*ignoreRotation:       True",
65 #endif
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.line_width = 1;
301   if (xgwa.width > 2560 || xgwa.height > 2560)
302     xgcv.line_width = 3;  /* Retina displays */
303
304   xgcv.foreground=get_pixel_resource (st->dpy, cmap, "background", "Background");
305   st->fgc[0]=XCreateGC(st->dpy, st->win, GCForeground|GCFunction|GCLineWidth,
306                        &xgcv);
307 #ifdef HAVE_JWXYZ
308   jwxyz_XSetAntiAliasing (st->dpy, st->fgc[0], False);
309 #endif
310   
311   n=0;
312   if (mono_p) {
313     xgcv.foreground=get_pixel_resource (st->dpy, cmap, "foreground", "Foreground");
314     st->fgc[1]=XCreateGC(st->dpy,st->win,GCForeground|GCFunction|GCLineWidth,
315                          &xgcv);
316 #ifdef HAVE_JWXYZ
317     jwxyz_XSetAntiAliasing (st->dpy, st->fgc[1], False);
318 #endif
319     for (i=0;i<st->numColors;i+=2) st->fgc[i]=st->fgc[0];
320     for (i=1;i<st->numColors;i+=2) st->fgc[i]=st->fgc[1];
321   } else {
322     for (i = 0; i < st->numColors; i++) {
323       color.red=st->colors[n++]<<8;
324       color.green=st->colors[n++]<<8;
325       color.blue=st->colors[n++]<<8;
326       color.flags=DoRed|DoGreen|DoBlue;
327       XAllocColor(st->dpy,cmap,&color);
328       xgcv.foreground=color.pixel;
329       if (st->fgc[i]) XFreeGC (st->dpy, st->fgc[i]);
330       st->fgc[i] = XCreateGC(st->dpy, st->win,
331                              GCForeground|GCFunction|GCLineWidth, &xgcv);
332 #ifdef HAVE_JWXYZ
333       jwxyz_XSetAntiAliasing (st->dpy, st->fgc[i], False);
334 #endif
335     }
336   }
337
338   st->cgc = XCreateGC(st->dpy,st->win,GCForeground|GCFunction|GCLineWidth,
339                       &xgcv);
340   XSetGraphicsExposures(st->dpy,st->cgc,False);
341 #ifdef HAVE_JWXYZ
342   jwxyz_XSetAntiAliasing (st->dpy, st->cgc, False);
343 #endif
344
345   st->xsize = xgwa.width;
346   st->ysize = xgwa.height;
347   st->xc = st->xsize >> 1;
348   st->yc = st->ysize >> 1;
349
350   st->maxx = 1.0;
351   st->maxy = st->ysize/(float)st->xsize;
352
353   if (st->colorScheme < 0) st->colorScheme = random()%NUM_SCHEMES;
354
355   return True;
356 }
357
358 static void initBugs(struct state *st)
359 {
360   register bug *b;
361   int i;
362
363   st->head = st->tail = 0;
364
365   memset((char *)st->bugs, 0,MAX_BUGS*sizeof(bug));
366   memset((char *)st->targets, 0, MAX_TARGETS*sizeof(bug));
367
368   if (st->ntargets < 0) st->ntargets = (0.25+frand(0.75)*frand(1))*MAX_TARGETS;
369   if (st->ntargets < 1) st->ntargets = 1;
370
371   if (st->nbugs < 0) st->nbugs = (0.25+frand(0.75)*frand(1))*MAX_BUGS;
372   if (st->nbugs <= st->ntargets) st->nbugs = st->ntargets+1;
373
374   if (st->trailLen < 0) {
375     st->trailLen = (1.0 - frand(0.6)*frand(1))*MAX_TRAIL_LEN;
376   }
377
378   if (st->nbugs > MAX_BUGS) st->nbugs = MAX_BUGS;
379   if (st->ntargets > MAX_TARGETS) st->ntargets = MAX_TARGETS;
380   if (st->trailLen > MAX_TRAIL_LEN) st->trailLen = MAX_TRAIL_LEN;
381
382   b = st->bugs;
383   for (i = 0; i < st->nbugs; i++, b++) {
384     b->pos[0] = frand(st->maxx);
385     b->pos[1] = frand(st->maxy);
386     b->vel[0] = frand(st->maxVel/2);
387     b->vel[1] = frand(st->maxVel/2);
388
389     b->hist[st->head][0] = b->pos[0]*st->xsize;
390     b->hist[st->head][1] = b->pos[1]*st->xsize;
391     b->closest = &st->targets[random()%st->ntargets];
392   }
393
394   b = st->targets;
395   for (i = 0; i < st->ntargets; i++, b++) {
396     b->pos[0] = frand(st->maxx);
397     b->pos[1] = frand(st->maxy);
398
399     b->vel[0] = frand(st->targetVel/2);
400     b->vel[1] = frand(st->targetVel/2);
401
402     b->hist[st->head][0] = b->pos[0]*st->xsize;
403     b->hist[st->head][1] = b->pos[1]*st->xsize;
404   }
405 }
406
407 static void pickNewTargets(struct state *st)
408 {
409   register int i;
410   register bug *b;
411
412   b = st->bugs;
413   for (i = 0; i < st->nbugs; i++, b++) {
414     b->closest = &st->targets[random()%st->ntargets];
415   }
416 }
417
418 #if 0
419 static void addBugs(int numToAdd)
420 {
421   register bug *b;
422   int i;
423
424   if (numToAdd + st->nbugs > MAX_BUGS) numToAdd = MAX_BUGS-st->nbugs;
425   else if (numToAdd < 0) numToAdd = 0;
426
427   for (i = 0; i < numToAdd; i++) {
428     b = &st->bugs[random()%st->nbugs];
429     memcpy((char *)&st->bugs[st->nbugs+i], (char *)b, sizeof(bug));
430     b->closest = &st->targets[random()%st->ntargets];
431   }
432
433   st->nbugs += numToAdd;
434 }
435
436 static void addTargets(int numToAdd)
437 {
438   register bug *b;
439   int i;
440
441   if (numToAdd + st->ntargets > MAX_TARGETS) numToAdd = MAX_TARGETS-st->ntargets;
442   else if (numToAdd < 0) numToAdd = 0;
443
444   for (i = 0; i < numToAdd; i++) {
445     b = &st->targets[random()%st->ntargets];
446     memcpy((char *)&st->targets[st->ntargets+i], (char *)b, sizeof(bug));
447     b->closest = &st->targets[random()%st->ntargets];
448   }
449
450   st->ntargets += numToAdd;
451 }
452 #endif
453
454 static void computeConstants(struct state *st)
455 {
456   st->halfDtSq = st->dt*st->dt*0.5;
457   st->dtInv = 1.0/st->dt;
458   st->targetVelSq = st->targetVel*st->targetVel;
459   st->maxVelSq = st->maxVel*st->maxVel;
460   st->minVel = st->maxVel*st->minVelMultiplier;
461   st->minVelSq = st->minVel*st->minVel;
462 }
463
464 static void computeColorIndices(struct state *st)
465 {
466   int i;
467   int schizoLength;
468
469   /* note: colors are used in *reverse* order! */
470
471   /* grayscale */
472   for (i = 0; i < st->trailLen; i++) {
473     st->grayIndex[st->trailLen-1-i] = 4 + i*16.0/st->trailLen + 0.5;
474     if (st->grayIndex[st->trailLen-1-i] > 19) st->grayIndex[st->trailLen-1-i] = 19;
475   }
476
477   /* red */
478   for (i = 0; i < st->trailLen; i++) {
479     st->redIndex[st->trailLen-1-i] = 20 + i*16.0/st->trailLen + 0.5;
480     if (st->redIndex[st->trailLen-1-i] > 35) st->redIndex[st->trailLen-1-i] = 35;
481   }
482
483   /* blue */
484   for (i = 0; i < st->trailLen; i++) {
485     st->blueIndex[st->trailLen-1-i] = 36 + i*16.0/st->trailLen + 0.5;
486     if (st->blueIndex[st->trailLen-1-i] > 51) st->blueIndex[st->trailLen-1-i] = 51;
487   }
488
489   /* gray schizo - same as gray*/
490   for (i = 0; i < st->trailLen; i++) {
491     st->graySIndex[st->trailLen-1-i] = 4 + i*16.0/st->trailLen + 0.5;
492     if (st->graySIndex[st->trailLen-1-i] > 19) st->graySIndex[st->trailLen-1-i] = 19;
493   }
494
495   /*schizoLength = st->trailLen/4;
496   if (schizoLength < 3) schizoLength = 3;*/
497   /* red schizo */
498   for (i = 0; i < st->trailLen; i++) {
499     /*    redSIndex[trailLen-1-i] = 
500           20 + 16.0*(i%schizoLength)/(schizoLength-1.0) + 0.5;*/
501     st->redSIndex[st->trailLen-1-i] = 20 + i*16.0/st->trailLen + 0.5;
502     if (st->redSIndex[st->trailLen-1-i] > 35) st->redSIndex[st->trailLen-1-i] = 35;
503   }
504
505   schizoLength = st->trailLen/2;
506   if (schizoLength < 3) schizoLength = 3;
507   /* blue schizo is next */
508   for (i = 0; i < st->trailLen; i++) {
509     st->blueSIndex[st->trailLen-1-i] = 
510       36 + 16.0*(i%schizoLength)/(schizoLength-1.0) + 0.5;
511     if (st->blueSIndex[st->trailLen-1-i] > 51) st->blueSIndex[st->trailLen-1-i] = 51;
512   }
513
514   /* random is next */
515   for (i = 0; i < st->trailLen; i++) {
516     st->randomIndex[i] = 52 + random()%(st->numRandomColors);
517   }
518 }
519
520 #if 0
521 static void setParams(bugParams *p)
522 {
523   st->dt = p->dt;
524   st->targetVel = p->targetVel;
525   st->targetAcc = p->targetAcc;
526   st->maxVel = p->maxVel;
527   st->maxAcc = p->maxAcc;
528   st->noise = p->noise;
529
530   st->nbugs = p->nbugs;
531   st->ntargets = p->ntargets;
532   st->trailLen = p->trailLen;
533   st->colorScheme = p->colorScheme;
534   st->changeProb = p->changeProb;
535   computeConstants();
536   computeColorIndices();
537 }
538 #endif
539
540 static void drawBugs(struct state *st, int *tColorIdx, int tci0, int tnc,
541                      int *colorIdx, int ci0, int nc)
542 {
543   register bug *b;
544   register int i, j;
545   int temp;
546
547   if (((st->head+1)%st->trailLen) == st->tail) {
548     /* first, erase last segment of bugs if necessary */
549     temp = (st->tail+1) % st->trailLen;
550     
551     b = st->bugs;
552     for (i = 0; i < st->nbugs; 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   
558     b = st->targets;
559     for (i = 0; i < st->ntargets; i++, b++) {
560       XDrawLine(st->dpy, st->win, st->fgc[0],
561                 b->hist[st->tail][0], b->hist[st->tail][1],
562                 b->hist[temp][0], b->hist[temp][1]);
563     }
564     st->tail = (st->tail+1)%st->trailLen;
565   }
566   
567   for (j = st->tail; j != st->head; j = temp) {
568     temp = (j+1)%st->trailLen;
569
570     b = st->bugs;
571     for (i = 0; i < st->nbugs; i++, b++) {
572       XDrawLine(st->dpy, st->win, st->fgc[colorIdx[ci0]],
573                 b->hist[j][0], b->hist[j][1],
574                 b->hist[temp][0], b->hist[temp][1]);
575     }
576     
577     b = st->targets;
578     for (i = 0; i < st->ntargets; i++, b++) {
579       XDrawLine(st->dpy, st->win, st->fgc[tColorIdx[tci0]],
580                 b->hist[j][0], b->hist[j][1],
581                 b->hist[temp][0], b->hist[temp][1]);
582     }
583     ci0 = (ci0+1)%nc;  
584     tci0 = (tci0+1)%tnc;  
585   }
586 }
587
588 static void clearBugs(struct state *st)
589 {
590   register bug *b;
591   register int i, j;
592   int temp;
593
594   st->tail = st->tail-1;
595   if (st->tail < 0) st->tail = st->trailLen-1;
596
597   if (((st->head+1)%st->trailLen) == st->tail) {
598     /* first, erase last segment of bugs if necessary */
599     temp = (st->tail+1) % st->trailLen;
600     
601     b = st->bugs;
602     for (i = 0; i < st->nbugs; 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   
608     b = st->targets;
609     for (i = 0; i < st->ntargets; i++, b++) {
610       XDrawLine(st->dpy, st->win, st->fgc[0],
611                 b->hist[st->tail][0], b->hist[st->tail][1],
612                 b->hist[temp][0], b->hist[temp][1]);
613     }
614     st->tail = (st->tail+1)%st->trailLen;
615   }
616   
617   for (j = st->tail; j != st->head; j = temp) {
618     temp = (j+1)%st->trailLen;
619
620     b = st->bugs;
621     for (i = 0; i < st->nbugs; 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     b = st->targets;
628     for (i = 0; i < st->ntargets; i++, b++) {
629       XDrawLine(st->dpy, st->win, st->fgc[0],
630                 b->hist[j][0], b->hist[j][1],
631                 b->hist[temp][0], b->hist[temp][1]);
632     }
633   }
634 }
635
636 static void updateState(struct state *st)
637 {
638   register int i;
639   register bug *b;
640   register float ax, ay, temp;
641   float theta;
642   bug *b2;
643   int j;
644
645   st->head = (st->head+1)%st->trailLen;
646   
647   for (j = 0; j < 5; j++) {
648     /* update closets bug for the bug indicated by checkIndex */
649     st->checkIndex = (st->checkIndex+1)%st->nbugs;
650     b = &st->bugs[st->checkIndex];
651
652     ax = b->closest->pos[0] - b->pos[0];
653     ay = b->closest->pos[1] - b->pos[1];
654     temp = ax*ax + ay*ay;
655     for (i = 0; i < st->ntargets; i++) {
656       b2 = &st->targets[i];
657       if (b2 == b->closest) continue;
658       ax = b2->pos[0] - b->pos[0];
659       ay = b2->pos[1] - b->pos[1];
660       theta = ax*ax + ay*ay;
661       if (theta < temp*2) {
662         b->closest = b2;
663         temp = theta;
664       }
665     }
666   }
667   
668   /* update target state */
669
670   b = st->targets;
671   for (i = 0; i < st->ntargets; i++, b++) {
672     theta = frand(6.28);
673     ax = st->targetAcc*cos(theta);
674     ay = st->targetAcc*sin(theta);
675
676     b->vel[0] += ax*st->dt;
677     b->vel[1] += ay*st->dt;
678
679     /* check velocity */
680     temp = sq(b->vel[0]) + sq(b->vel[1]);
681     if (temp > st->targetVelSq) {
682       temp = st->targetVel/sqrt(temp);
683       /* save old vel for acc computation */
684       ax = b->vel[0];
685       ay = b->vel[1];
686
687       /* compute new velocity */
688       b->vel[0] *= temp;
689       b->vel[1] *= temp;
690       
691       /* update acceleration */
692       ax = (b->vel[0]-ax)*st->dtInv;
693       ay = (b->vel[1]-ay)*st->dtInv;
694     }
695
696     /* update position */
697     b->pos[0] += b->vel[0]*st->dt + ax*st->halfDtSq;
698     b->pos[1] += b->vel[1]*st->dt + ay*st->halfDtSq;
699
700     /* check limits on targets */
701     if (b->pos[0] < 0) {
702       /* bounce */
703       b->pos[0] = -b->pos[0];
704       b->vel[0] = -b->vel[0];
705     } else if (b->pos[0] >= st->maxx) {
706       /* bounce */
707       b->pos[0] = 2*st->maxx-b->pos[0];
708       b->vel[0] = -b->vel[0];
709     }
710     if (b->pos[1] < 0) {
711       /* bounce */
712       b->pos[1] = -b->pos[1];
713       b->vel[1] = -b->vel[1];
714     } else if (b->pos[1] >= st->maxy) {
715       /* bounce */
716       b->pos[1] = 2*st->maxy-b->pos[1];
717       b->vel[1] = -b->vel[1];
718     }
719
720     b->hist[st->head][0] = b->pos[0]*st->xsize;
721     b->hist[st->head][1] = b->pos[1]*st->xsize;
722   }
723
724   /* update bug state */
725   b = st->bugs;
726   for (i = 0; i < st->nbugs; i++, b++) {
727     theta = atan2(b->closest->pos[1] - b->pos[1] + frand(st->noise),
728                   b->closest->pos[0] - b->pos[0] + frand(st->noise));
729     ax = st->maxAcc*cos(theta);
730     ay = st->maxAcc*sin(theta);
731
732     b->vel[0] += ax*st->dt;
733     b->vel[1] += ay*st->dt;
734
735     /* check velocity */
736     temp = sq(b->vel[0]) + sq(b->vel[1]);
737     if (temp > st->maxVelSq) {
738       temp = st->maxVel/sqrt(temp);
739
740       /* save old vel for acc computation */
741       ax = b->vel[0];
742       ay = b->vel[1];
743
744       /* compute new velocity */
745       b->vel[0] *= temp;
746       b->vel[1] *= temp;
747       
748       /* update acceleration */
749       ax = (b->vel[0]-ax)*st->dtInv;
750       ay = (b->vel[1]-ay)*st->dtInv;
751     } else if (temp < st->minVelSq) {
752       temp = st->minVel/sqrt(temp);
753
754       /* save old vel for acc computation */
755       ax = b->vel[0];
756       ay = b->vel[1];
757
758       /* compute new velocity */
759       b->vel[0] *= temp;
760       b->vel[1] *= temp;
761       
762       /* update acceleration */
763       ax = (b->vel[0]-ax)*st->dtInv;
764       ay = (b->vel[1]-ay)*st->dtInv;
765     }
766
767     /* update position */
768     b->pos[0] += b->vel[0]*st->dt + ax*st->halfDtSq;
769     b->pos[1] += b->vel[1]*st->dt + ay*st->halfDtSq;
770
771     /* check limits on targets */
772     if (b->pos[0] < 0) {
773       /* bounce */
774       b->pos[0] = -b->pos[0];
775       b->vel[0] = -b->vel[0];
776     } else if (b->pos[0] >= st->maxx) {
777       /* bounce */
778       b->pos[0] = 2*st->maxx-b->pos[0];
779       b->vel[0] = -b->vel[0];
780     }
781     if (b->pos[1] < 0) {
782       /* bounce */
783       b->pos[1] = -b->pos[1];
784       b->vel[1] = -b->vel[1];
785     } else if (b->pos[1] >= st->maxy) {
786       /* bounce */
787       b->pos[1] = 2*st->maxy-b->pos[1];
788       b->vel[1] = -b->vel[1];
789     }
790
791     b->hist[st->head][0] = b->pos[0]*st->xsize;
792     b->hist[st->head][1] = b->pos[1]*st->xsize;
793   }
794 }
795
796 static void mutateBug(struct state *st, int which)
797 {
798   int i, j;
799
800   if (which == 0) {
801     /* turn bug into target */
802     if (st->ntargets < MAX_TARGETS-1 && st->nbugs > 1) {
803       i = random() % st->nbugs;
804       memcpy((char *)&st->targets[st->ntargets], (char *)&st->bugs[i], sizeof(bug));
805       memcpy((char *)&st->bugs[i], (char *)&st->bugs[st->nbugs-1], sizeof(bug));
806       st->targets[st->ntargets].pos[0] = frand(st->maxx);
807       st->targets[st->ntargets].pos[1] = frand(st->maxy);
808       st->nbugs--;
809       st->ntargets++;
810
811       for (i = 0; i < st->nbugs; i += st->ntargets) {
812         st->bugs[i].closest = &st->targets[st->ntargets-1];
813       }
814     }
815   } else {
816     /* turn target into bug */
817     if (st->ntargets > 1 && st->nbugs < MAX_BUGS-1) {
818       /* pick a target */
819       i = random() % st->ntargets;
820
821       /* copy state into a new bug */
822       memcpy((char *)&st->bugs[st->nbugs], (char *)&st->targets[i], sizeof(bug));
823       st->ntargets--;
824
825       /* pick a target for the new bug */
826       st->bugs[st->nbugs].closest = &st->targets[random()%st->ntargets];
827
828       for (j = 0; j < st->nbugs; j++) {
829         if (st->bugs[j].closest == &st->targets[st->ntargets]) {
830           st->bugs[j].closest = &st->targets[i];
831         } else if (st->bugs[j].closest == &st->targets[i]) {
832           st->bugs[j].closest = &st->targets[random()%st->ntargets];
833         }
834       }
835       st->nbugs++;
836       
837       /* copy the last ntarget into the one we just deleted */
838       memcpy(&st->targets[i], (char *)&st->targets[st->ntargets], sizeof(bug));
839     }
840   }
841 }
842
843 static void mutateParam(float *param)
844 {
845   *param *= 0.75+frand(0.5);
846 }
847
848 static void randomSmallChange(struct state *st)
849 {
850   int whichCase = 0;
851
852   whichCase = random()%11;
853
854   if (++st->rsc_callDepth > 10) {
855     st->rsc_callDepth--;
856     return;
857   }
858   
859   switch(whichCase) {
860   case 0:
861     /* acceleration */
862     mutateParam(&st->maxAcc);
863     break;
864
865   case 1:
866     /* target acceleration */
867     mutateParam(&st->targetAcc);
868     break;
869
870   case 2:
871     /* velocity */
872     mutateParam(&st->maxVel);
873     break;
874
875   case 3: 
876     /* target velocity */
877     mutateParam(&st->targetVel);
878     break;
879
880   case 4:
881     /* noise */
882     mutateParam(&st->noise);
883     break;
884
885   case 5:
886     /* minVelMultiplier */
887     mutateParam(&st->minVelMultiplier);
888     break;
889     
890   case 6:
891   case 7:
892     /* target to bug */
893     if (st->ntargets < 2) break;
894     mutateBug(st, 1);
895     break;
896
897   case 8:
898     /* bug to target */
899     if (st->nbugs < 2) break;
900     mutateBug(st, 0);
901     if (st->nbugs < 2) break;
902     mutateBug(st, 0);
903     break;
904
905   case 9:
906     /* color scheme */
907     st->colorScheme = random()%NUM_SCHEMES;
908     if (st->colorScheme == RANDOM_SCHIZO || st->colorScheme == COLOR_SCHIZO) {
909       /* don't use these quite as much */
910       st->colorScheme = random()%NUM_SCHEMES;
911     }
912     break;
913
914   default:
915     randomSmallChange(st);
916     randomSmallChange(st);
917     randomSmallChange(st);
918     randomSmallChange(st);
919   }
920
921   if (st->minVelMultiplier < 0.3) st->minVelMultiplier = 0.3;
922   else if (st->minVelMultiplier > 0.9) st->minVelMultiplier = 0.9;
923   if (st->noise < 0.01) st->noise = 0.01;
924   if (st->maxVel < 0.02) st->maxVel = 0.02;
925   if (st->targetVel < 0.02) st->targetVel = 0.02;
926   if (st->targetAcc > st->targetVel*0.7) st->targetAcc = st->targetVel*0.7;
927   if (st->maxAcc > st->maxVel*0.7) st->maxAcc = st->maxVel*0.7;
928   if (st->targetAcc > st->targetVel*0.7) st->targetAcc = st->targetVel*0.7;
929   if (st->maxAcc < 0.01) st->maxAcc = 0.01;
930   if (st->targetAcc < 0.005) st->targetAcc = 0.005;
931
932   computeConstants(st);
933   st->rsc_callDepth--;
934 }
935
936 static void randomBigChange(struct state *st)
937 {
938   int whichCase = 0;
939   int temp;
940
941   whichCase = random()%4;
942   
943   if (++st->rbc_callDepth > 3) {
944     st->rbc_callDepth--;
945     return;
946   }
947
948   switch(whichCase) {
949   case 0:
950     /* trail length */
951     temp = (random()%(MAX_TRAIL_LEN-25)) + 25;
952     clearBugs(st);
953     st->trailLen = temp;
954     computeColorIndices(st);
955     initBugs(st);
956     break;
957
958   case 1:  
959     /* Whee! */
960     randomSmallChange(st);
961     randomSmallChange(st);
962     randomSmallChange(st);
963     randomSmallChange(st);
964     randomSmallChange(st);
965     randomSmallChange(st);
966     randomSmallChange(st);
967     randomSmallChange(st);
968     break;
969
970   case 2:
971     clearBugs(st);
972     initBugs(st);
973     break;
974     
975   case 3:
976     pickNewTargets(st);
977     break;
978     
979   default:
980     temp = random()%st->ntargets;
981     st->targets[temp].pos[0] += frand(st->maxx/4)-st->maxx/8;
982     st->targets[temp].pos[1] += frand(st->maxy/4)-st->maxy/8;
983     /* updateState() will fix bounds */
984     break;
985   }
986
987   st->rbc_callDepth--;
988 }
989
990 static void updateColorIndex(struct state *st, 
991                              int **tColorIdx, int *tci0, int *tnc,
992                       int **colorIdx, int *ci0, int *nc)
993 {
994   switch(st->colorScheme) {
995   case COLOR_TRAILS:
996     *tColorIdx = st->redIndex;
997     *tci0 = 0;
998     *tnc = st->trailLen;
999     *colorIdx = st->blueIndex;
1000     *ci0 = 0;
1001     *nc = st->trailLen;
1002     break;
1003
1004   case GRAY_SCHIZO:
1005     *tColorIdx = st->graySIndex;
1006     *tci0 = st->head;
1007     *tnc = st->trailLen;
1008     *colorIdx = st->graySIndex;
1009     *ci0 = st->head;
1010     *nc = st->trailLen;
1011     break;
1012
1013   case COLOR_SCHIZO:
1014     *tColorIdx = st->redSIndex;
1015     *tci0 = st->head;
1016     *tnc = st->trailLen;
1017     *colorIdx = st->blueSIndex;
1018     *ci0 = st->head;
1019     *nc = st->trailLen;
1020     break;
1021
1022   case GRAY_TRAILS:
1023     *tColorIdx = st->grayIndex;
1024     *tci0 = 0;
1025     *tnc = st->trailLen;
1026     *colorIdx = st->grayIndex;
1027     *ci0 = 0;
1028     *nc = st->trailLen;
1029     break;
1030
1031   case RANDOM_TRAILS:
1032     *tColorIdx = st->redIndex;
1033     *tci0 = 0;
1034     *tnc = st->trailLen;
1035     *colorIdx = st->randomIndex;
1036     *ci0 = 0;
1037     *nc = st->trailLen;
1038     break;
1039
1040   case RANDOM_SCHIZO:
1041     *tColorIdx = st->redIndex;
1042     *tci0 = st->head;
1043     *tnc = st->trailLen;
1044     *colorIdx = st->randomIndex;
1045     *ci0 = st->head;
1046     *nc = st->trailLen;
1047     break;
1048   }
1049 }
1050
1051 #if HAVE_GETTIMEOFDAY
1052 static void initTime(struct state *st)
1053 {
1054 #if GETTIMEOFDAY_TWO_ARGS
1055   gettimeofday(&st->startupTime, NULL);
1056 #else
1057   gettimeofday(&st->startupTime);
1058 #endif
1059 }
1060
1061 static double getTime(struct state *st)
1062 {
1063   struct timeval t;
1064   float f;
1065 #if GETTIMEOFDAY_TWO_ARGS
1066   gettimeofday(&t, NULL);
1067 #else
1068   gettimeofday(&t);
1069 #endif
1070   t.tv_sec -= st->startupTime.tv_sec;
1071   f = ((double)t.tv_sec) + t.tv_usec*1e-6;
1072   return f;
1073 }
1074 #endif
1075
1076 static void *
1077 xrayswarm_init (Display *d, Window w)
1078 {
1079   struct state *st = (struct state *) calloc (1, sizeof(*st));
1080   int i;
1081
1082   st->dpy = d;
1083   st->win = w;
1084
1085   st->dt = 0.3;
1086   st->targetVel = 0.03;
1087   st->targetAcc = 0.02;
1088   st->maxVel = 0.05;
1089   st->maxAcc = 0.03;
1090   st->noise = 0.01;
1091   st->minVelMultiplier = 0.5;
1092
1093   st->nbugs = -1;
1094   st->ntargets = -1;
1095   st->trailLen = -1;
1096
1097   st->colorScheme = /* -1 */  2;
1098   st->changeProb = 0.08;
1099
1100   if (!initGraphics(st)) abort();
1101
1102   computeConstants(st);
1103   initBugs(st);
1104   initTime(st);
1105   computeColorIndices(st);
1106
1107   if (st->changeProb > 0) {
1108     for (i = random()%5+5; i >= 0; i--) {
1109       randomSmallChange(st);
1110     }
1111   }
1112
1113   return st;
1114 }
1115
1116 static unsigned long
1117 xrayswarm_draw (Display *d, Window w, void *closure)
1118 {
1119   struct state *st = (struct state *) closure;
1120   unsigned long this_delay = st->delay;
1121
1122 #if HAVE_GETTIMEOFDAY
1123   st->draw_start = getTime(st);
1124 #endif
1125
1126     if (st->delay > 0) {
1127       st->draw_cnt = 2;
1128       st->dt = DESIRED_DT/2;
1129     } else {
1130       st->draw_cnt = 1;
1131       st->dt = DESIRED_DT;
1132     }
1133
1134     for (; st->draw_cnt > 0; st->draw_cnt--) {
1135       updateState(st);
1136       updateColorIndex(st, &st->draw_targetColorIndex, &st->draw_targetStartColor, &st->draw_targetNumColors,
1137                        &st->draw_colorIndex, &st->draw_startColor, &st->draw_numColors);
1138       drawBugs(st, st->draw_targetColorIndex, st->draw_targetStartColor, st->draw_targetNumColors,
1139                st->draw_colorIndex, st->draw_startColor, st->draw_numColors);
1140     }
1141 #if HAVE_GETTIMEOFDAY
1142     st->draw_end = getTime(st);
1143     st->draw_nframes++;
1144
1145     if (st->draw_end > st->draw_start+0.5) {
1146       if (frand(1.0) < st->changeProb) randomSmallChange(st);
1147       if (frand(1.0) < st->changeProb*0.3) randomBigChange(st);
1148       st->draw_elapsed = st->draw_end-st->draw_start;
1149         
1150       st->draw_timePerFrame = st->draw_elapsed/st->draw_nframes - st->delay*1e-6;
1151       st->draw_fps = st->draw_nframes/st->draw_elapsed;
1152       /*
1153       printf("elapsed: %.3f\n", elapsed);
1154       printf("fps: %.1f  secs per frame: %.3f  delay: %f\n", 
1155              fps, timePerFrame, delay);
1156       */
1157
1158       if (st->draw_fps > MAX_FPS) {
1159         st->delay = (1.0/MAX_FPS - (st->draw_timePerFrame + st->delay*1e-6))*1e6;
1160       } else if (st->dt*st->draw_fps < MIN_FPS*DESIRED_DT) {
1161         /* need to speed things up somehow */
1162         if (0 && st->nbugs > 10) {
1163           /*printf("reducing bugs to improve speed.\n");*/
1164           clearBugs(st);
1165           st->nbugs *= st->draw_fps/MIN_FPS;
1166           if (st->ntargets >= st->nbugs/2) mutateBug(st, 1);
1167         } else if (0 && st->dt < 0.3) {
1168           /*printf("increasing dt to improve speed.\n");*/
1169           st->dt *= MIN_FPS/st->draw_fps;
1170           computeConstants(st);
1171         } else if (st->trailLen > 10) {
1172           /*printf("reducing trail length to improve speed.\n");*/
1173           clearBugs(st);
1174           st->trailLen = st->trailLen * (st->draw_fps/MIN_FPS);
1175           if (st->trailLen < 10) st->trailLen = 10;
1176           computeColorIndices(st);
1177           initBugs(st);
1178         }
1179       }
1180
1181       st->draw_start = getTime(st);
1182       st->draw_nframes = 0;
1183     }
1184 #else
1185     if (frand(1) < st->changeProb*2/100.0) randomSmallChange(st);
1186     if (frand(1) < st->changeProb*0.3/100.0) randomBigChange(st);
1187 #endif
1188     
1189     if (st->delay <= 10000) {
1190       st->draw_delayAccum += st->delay;
1191       if (st->draw_delayAccum > 10000) {
1192         this_delay = st->draw_delayAccum;
1193         st->draw_delayAccum = 0;
1194         st->draw_sleepCount = 0;
1195       }
1196       if (++st->draw_sleepCount > 2) {
1197         st->draw_sleepCount = 0;
1198         this_delay = 10000;
1199       }
1200     }
1201
1202     return this_delay;
1203 }
1204
1205 static void
1206 xrayswarm_reshape (Display *dpy, Window window, void *closure, 
1207                  unsigned int w, unsigned int h)
1208 {
1209   struct state *st = (struct state *) closure;
1210   st->xsize = w;
1211   st->ysize = h;
1212   st->xc = st->xsize >> 1;
1213   st->yc = st->ysize >> 1;
1214   st->maxy = st->ysize/(float)st->xsize;
1215 }
1216
1217 static Bool
1218 xrayswarm_event (Display *dpy, Window window, void *closure, XEvent *event)
1219 {
1220   return False;
1221 }
1222
1223 static void
1224 xrayswarm_free (Display *dpy, Window window, void *closure)
1225 {
1226   struct state *st = (struct state *) closure;
1227   int i;
1228   for (i = 0; i < 256; i++)
1229     if (st->fgc[i]) XFreeGC (dpy, (st->fgc[i]));
1230   XFreeGC (dpy, st->cgc);
1231   free (st);
1232 }
1233
1234 XSCREENSAVER_MODULE ("XRaySwarm", xrayswarm)