http://packetstorm.tacticalflex.com/UNIX/admin/xscreensaver-3.27.tar.gz
[xscreensaver] / hacks / whirlwindwarp.c
1 /* xscreensaver, Copyright (c) 2000 Paul "Joey" Clark <pclark@bris.ac.uk>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  *
11  * 19971004: Johannes Keukelaar <johannes@nada.kth.se>: Use helix screen
12  *           eraser.
13  */
14
15 /* WhirlwindWarp: moving stars.  Ported from QBasic by Joey.
16    Version 1.2.  FF parameters now driven by a
17    dampened-walked velocity, making them smoother.
18
19    This code adapted from original program by jwz/jk above.
20    Freely distrubtable.  Please keep this tag with
21    this code, and add your own if you contribute.
22    I would be delighted to hear if have made use of this code.
23    If you find this code useful or have any queries, please
24    contact me: pclark@cs.bris.ac.uk / joeyclark@usa.net
25    Paul "Joey" Clark, hacking for humanity, Feb 99
26    www.cs.bris.ac.uk/~pclark | www.changetheworld.org.uk */
27
28 #include <math.h>
29
30 #include "screenhack.h"
31 #include "erase.h"
32 #include "hsv.h"
33
34 static GC draw_gc, erase_gc;
35 static unsigned int default_fg_pixel;
36
37 /* Maximum number of points, tails, and fields (hard-coded) */
38 #define maxps 1000
39 #define maxts 30
40 #define fs 10
41
42 /* Screen width and height */
43 static int scrwid,scrhei;
44
45 /* Current x,y of stars in realspace */
46 static float cx[maxps];
47 static float cy[maxps];
48 /* Previous x,y plots in pixelspace for removal later */
49 static int tx[maxps*maxts];
50 static int ty[maxps*maxts];
51 /* The force fields and their parameters */
52 static char *name[fs];
53 static int fon[fs];     /* Is field on or off? */
54 static float var[fs];   /* Current parameter */
55 static float op[fs];    /* Optimum (central/mean) value */
56 static float damp[fs];  /* Dampening (how much drawn between current and optimal) */
57 static float force[fs]; /* Amount of change per moment */
58 static float acc[fs];
59
60 /* Number of points and tails */
61 static int ps=500;
62 static int ts=5;
63
64 /* Show meters or not? */
65 static Bool meters;
66
67 static Bool init_whirlwindwarp(Display *dpy, Window window) {
68   XGCValues gcv;
69   Colormap cmap;
70   XWindowAttributes xgwa;
71   XGetWindowAttributes (dpy, window, &xgwa);
72   cmap = xgwa.colormap;
73   gcv.foreground = default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
74   draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
75   gcv.foreground = get_pixel_resource ("background", "Background", dpy, cmap);
76   erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
77
78   ps = get_integer_resource ("points", "Integer");
79   ts = get_integer_resource ("tails", "Integer");
80   meters = get_boolean_resource ("meters", "Show meters");
81   if (ps>maxps || ts>maxts)
82     return 0;
83   return 1;
84 }
85
86 static float myrnd() { /* between -1.0 and +1.0 */
87   return 2.0*((float)((random()%10000000)/10000000.0)-0.5);
88 }
89
90 float mysgn(float x) {
91   return ( x < 0 ? -1 :
92            x > 0 ? +1 :
93                     0 );
94 }
95
96 void stars_newp(int p) {
97   cx[p]=myrnd();
98   cy[p]=myrnd();
99 }
100
101 /* Adjust a variable var about optimum op,
102      with damp = dampening about op
103          force = force of random perturbation */
104 float stars_perturb(float var,float op,float damp,float force) {
105   var=op+damp*(var-op)+force*myrnd()/4.0;
106 /*  if (fabs(var-op)>0.1)  // (to keep within bounds)
107     var=op+0.1*mysgn(var-op);*/
108   return var;
109 }
110
111 /* Get pixel coordinates of a star */
112 int stars_scrpos_x(int p) {
113   return scrwid*(cx[p]+1.0)/2.0;
114 }
115 int stars_scrpos_y(int p) {
116   return scrhei*(cy[p]+1.0)/2.0;
117 }
118
119 /* Draw a meter of a forcefield's parameter */
120 void stars_draw_meter(Display *dpy,Window window,GC draw_gc,int f) {
121   int x,y,w,h;
122   x=scrwid/2;
123   y=f*10;
124   w=(var[f]-op[f])*scrwid*4;
125   h=7;
126   if (w<0) {
127     w=-w;
128     x=x-w;
129   }
130   if (fon[f])
131     XFillRectangle(dpy,window,draw_gc,x,y,w,h);
132   else
133     XDrawRectangle(dpy,window,draw_gc,x,y,w,h);
134 }
135
136 /* Move a star according to acting forcefields */
137 void stars_move(int p) {
138   float nx,ny;
139   float x=cx[p];
140   float y=cy[p];
141     if (fon[1]) {
142         x = x * var[1]; y = y * var[1];
143     }
144     if (fon[2]) {
145       nx=x*cos(var[2])+y*sin(var[2]);
146       ny=-x*sin(var[2])+y*cos(var[2]);
147       x=nx;
148       y=ny;
149     }
150     if (fon[3]) {
151       y=y*var[3];
152     }
153     if (fon[4]) {
154       x=(x-1.0)*var[3]+1.0;
155     }
156     if (fon[5]) {
157       x=x+var[5]*x;
158     }
159     if (fon[6]) {
160         x = mysgn(x) * pow(fabs(x),var[6]);
161         y = mysgn(y) * pow(fabs(y),var[6]);
162     }
163     if (fon[7]) {
164       if (fon[0]) {
165         if (fon[9]) {
166           x=x+var[7]*(-1.0+2.0*(float)(p%2));
167         } else {
168           x=x+var[7]*(-1.0+2.0*(float)((p%50)/49.0));
169         }
170       }
171     }
172     if (fon[8]) {
173       if (fon[0]) {
174         if (fon[9]) {
175           y=y+var[8]*(-1.0+2.0*(float)(p%2));
176         } else {
177           y=y+var[8]*(-1.0+2.0*(float)((p%50)/49.0));
178         }
179       }
180     }
181   cx[p]=x;
182   cy[p]=y;
183 }
184
185 static void do_whirlwindwarp(Display *dpy, Window window) {
186   Colormap cmap;
187   XWindowAttributes xgwa;
188   int got_color = 0;
189   XColor color[maxps];
190   XColor bgcolor;
191   int p,f,nt, sx,sy, resets,lastresets,cnt;
192
193   XClearWindow (dpy, window);
194   XGetWindowAttributes (dpy, window, &xgwa);
195   cmap = xgwa.colormap;
196   scrwid = xgwa.width;
197   scrhei = xgwa.height;
198
199   /* Setup colours */
200   hsv_to_rgb (0.0, 0.0, 0.0, &bgcolor.red, &bgcolor.green, &bgcolor.blue);
201   got_color = XAllocColor (dpy, cmap, &bgcolor);
202   for (p=0;p<ps;p++) {
203         if (!mono_p)
204           hsv_to_rgb (random()%360, .6+.4*myrnd(), .6+.4*myrnd(), &color[p].red, &color[p].green, &color[p].blue);
205           /* hsv_to_rgb (random()%360, 1.0, 1.0, &color[p].red, &color[p].green, &color[p].blue);   for stronger colours! */
206         if ((!mono_p) && (got_color = XAllocColor (dpy, cmap, &color[p]))) {
207         } else {
208           if (p>0)
209             color[p]=color[0];
210           else
211             color[p].pixel=default_fg_pixel;
212         }
213   }
214
215   /* Set up parameter movements for the different forcefields */
216   name[1] = "Velocity";
217   op[1] = 1; damp[1] = .999; force[1] = .002;
218   name[2] = "Rotation";
219   op[2] = 0; damp[2] = .999; force[2] = .002;
220   name[3] = "Drip";
221   op[3] = 1; damp[3] = .999; force[3] = .005;
222   name[4] = "Dribble";
223   op[4] = 1; damp[4] = .999; force[4] = .005;
224   name[5] = "Slide";
225   op[5] = 0; damp[5] = .999; force[5] = .002;
226   name[6] = "Accelerate";
227   op[6] = 1.0; damp[6] = .999; force[6] = .005;
228   name[7] = "xDisplace";
229   op[7] = 0; damp[7] = .999; force[7] = .005;
230   name[8] = "yDisplace";
231   op[8] = 0; damp[8] = .999; force[8] = .005;
232   /* 0 and 9 are options for splitting displacements [no var] */
233   name[0] = "Split";
234   op[0] = 0; damp[0] = 0; force[0] = 0;
235   name[9] = "2d/3d split";
236   op[9] = 0; damp[9] = 0; force[9] = 0;
237
238   /* Initialise parameters to optimum, all off */
239   for (f=0;f<fs;f++) {
240     var[f]=op[f];
241     fon[f]=0;
242     acc[f]=0;
243   }
244
245   /* Initialise stars */
246   for (p=0;p<ps;p++)
247     stars_newp(p);
248
249   /* tx[nt],ty[nt] remeber earlier screen plots (tails of stars)
250      which are deleted when nt comes round again */
251   nt=0;
252   resets=0;
253
254   while (1) {
255
256       /* Move current points */
257       lastresets=resets;
258       resets=0;
259       for (p=0;p<ps;p++) {
260         /* Erase old */
261         XSetForeground (dpy, draw_gc, bgcolor.pixel);
262         XDrawPoint(dpy,window,draw_gc,tx[nt],ty[nt]);
263
264         /* Move */
265         stars_move(p);
266         /* If moved off screen, create a new one */
267         if (cx[p]<-1.0 || cx[p]>+1.0 ||
268             cy[p]<-1.0 || cy[p]>+1.0 ||
269             fabs(cx[p])<.0001 || fabs(cy[p])<.0001) {
270           stars_newp(p);
271           resets++;
272         } else if (myrnd()>0.99) /* Reset at random */
273           stars_newp(p);
274
275         /* Draw point */
276         sx=stars_scrpos_x(p);
277         sy=stars_scrpos_y(p);
278         XSetForeground (dpy, draw_gc, color[p].pixel);
279         XDrawPoint(dpy,window,draw_gc,sx,sy);
280
281         /* Remember it for removal later */
282         tx[nt]=sx;
283         ty[nt]=sy;
284         nt=(nt+1)%(ps*ts);
285       }
286
287       /* Adjust force fields */
288       cnt=0;
289       for (f=0;f<fs;f++) {
290
291         if (meters) { /* Remove meter from display */
292           XSetForeground(dpy, draw_gc, bgcolor.pixel);
293           stars_draw_meter(dpy,window,draw_gc,f);
294         }
295
296         /* Adjust forcefield's parameter */
297         acc[f]=stars_perturb(acc[f],0,0.98,0.009);
298         var[f]=op[f]+(var[f]-op[f])*damp[f]+force[f]*acc[f];
299
300         if (myrnd()>0.998) /* Turn it on/off ? */
301           fon[f]=(myrnd()<0.0);
302           /* fon[f]=!fon[f]; */
303
304         if (meters) { /* Redraw the meter */
305           XSetForeground(dpy, draw_gc, color[f].pixel);
306           stars_draw_meter(dpy,window,draw_gc,f);
307         }
308
309         if (fon[f])
310           cnt++;
311       }
312       if (cnt==0) { /* Ensure at least one is on! */
313         f=random() % fs;
314         fon[f]=1;
315       }
316       if (meters) {
317         XSetForeground(dpy, draw_gc, bgcolor.pixel);
318         XDrawRectangle(dpy,window,draw_gc,0,0,lastresets*5,3);
319         XSetForeground(dpy, draw_gc, default_fg_pixel);
320         XDrawRectangle(dpy,window,draw_gc,0,0,resets*5,3);
321       }
322 /*      if (resets*5>scrwid) {
323        Turn one off a field if too many points are flying off screen */
324       /* This was a problem when one of the force-fields was acting wrong,
325          but not really needed any more unless we need to debug new ones ...! */
326         /* for (f=0;f<fs;f++)
327             if (fon[f])
328             printf("%i",f);
329           printf("\n");
330           XSync (dpy, False);
331           screenhack_handle_events (dpy);
332          sleep(1);
333         do { // In fact this bit might go wrong if
334           f=random() % fs; // we have not ensured at least one is on (above)
335         } while (!fon[f]);
336         fon[f]=0;
337       }            */
338
339       XSync (dpy, False);
340       screenhack_handle_events (dpy);
341
342   }
343
344 }
345
346
347 char *progclass = "WhirlwindWarp";
348
349 char *defaults [] = {
350   ".background: black",
351   ".foreground: white",
352   "*points:     400",
353   "*tails:      10",
354   "*meters:     false",
355   0
356 };
357
358 XrmOptionDescRec options [] = {
359   { "-points",  ".points",      XrmoptionSepArg, 0 },
360   { "-tails",   ".tails",       XrmoptionSepArg, 0 },
361   { "-meters",  ".meters",      XrmoptionNoArg, "true" },
362   { 0, 0, 0, 0 }
363 };
364
365 void screenhack(Display *dpy, Window window) {
366   if (init_whirlwindwarp(dpy, window))
367     do_whirlwindwarp(dpy, window);
368 }