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(void) { /* 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 | } |
---|