http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.20.tar.gz
[xscreensaver] / hacks / fiberlamp.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* fiberlamp --- A Fiber Optic Lamp */
3
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)fiberlamp.c   5.00 2000/11/01 xlockmore";
6
7 #endif
8
9 /*-
10  * Copyright (c) 2005 by Tim Auckland <Tim.Auckland@Procket.com>
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * "fiberlamp" shows Fiber Optic Lamp.  Since there is no closed-form
25  * solution to the large-amplitude cantilever equation, the flexible
26  * fiber is modeled as a set of descrete nodes.
27  *
28  * Revision History:
29  * 13-Jan-2005: Initial development.
30  */
31
32 #ifdef STANDALONE
33 #define MODE_fiberlamp
34 #define PROGCLASS "Fiberlamp"
35 #define HACK_INIT init_fiberlamp
36 #define HACK_DRAW draw_fiberlamp
37 #define HACK_RESHAPE reshape_fiberlamp
38 #define _no_HACK_FREE release_fiberlamp
39 #define fiberlamp_opts xlockmore_opts
40 #define DEFAULTS "*delay: 10000 \n" \
41  "*count: 500 \n" \
42  "*cycles: 10000 \n" \
43  "*ncolors: 64 \n"
44 #define UNIFORM_COLORS
45 #include "xlockmore.h"          /* in xscreensaver distribution */
46 # ifndef MI_DEPTH
47 #  define MI_DEPTH MI_WIN_DEPTH
48 # endif
49 #else /* STANDALONE */
50 #include "xlock.h"              /* in xlockmore distribution */
51 #endif /* STANDALONE */
52
53 #ifdef MODE_fiberlamp
54
55 ModeSpecOpt fiberlamp_opts =
56 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
57
58 #ifdef USE_MODULES
59 ModStruct   fiberlamp_description =
60 {"fiberlamp", "init_fiberlamp", "draw_fiberlamp", "release_fiberlamp",
61  "draw_fiberlamp", "change_fiberlamp", (char *) NULL, &fiberlamp_opts,
62  1000, 500, 10000, 0, 64, 1.0, "", "Shows a Fiber Optic Lamp", 0, NULL};
63
64 #endif
65
66 #define SPREAD (30.0) /* Angular spread at the base */
67 #define SCALE (MI_WIDTH(mi)/2) /* Screen size */
68 #define NODES (20) /* Number of nodes in a fiber.  Variable with range
69                                           10 .. 30, if desired.  High values have
70                                           stability problems unless you use small DT */
71
72 /* Physics parameters.  Tune carefully to keep realism and avoid instability*/
73 #define DT (0.5) /* Time increment: Low is slow, High is less stable. */
74 #define PY (0.12) /* Rigidity: Low droops, High is stiff. */
75 #define DAMPING (0.055) /* Damping: Low allows oscillations, High is boring. */
76
77 #undef PLAN /* Plan view (for debugging) */
78 #undef CHECKCOLORWHEEL /* Plan view with no spread */
79
80 #define DRAND(v)        (LRAND()/MAXRAND*(v))   /* double random 0 - v */
81
82 /* Length of nodes.  Uniform except for shorter notes at the tips for
83    colour highlights.  Sum from 0..NODES-1 should exactly 1.0 */
84 #define LEN(A) ((A<NODES-3) ? 1.0/(NODES-2.5) : 0.25/(NODES-2.5))
85
86 typedef struct {
87   double phi, phidash;
88   double eta, etadash;
89   double x;
90   double y;
91   double z;
92 } nodestruct;
93
94 typedef struct {
95   nodestruct *node;
96   XPoint *draw;
97   XPoint *erase;
98 } fiberstruct;
99
100 typedef struct {
101   double  psi;
102   double  dpsi;
103   int     count, nfibers;
104   double  cx;
105   double  rx, ry; /* Coordinates relative to root */
106   fiberstruct *fiber;
107   Pixmap      buffer; /* Double Buffer */
108   long    bright, medium, dim; /* "White" colors */
109 } fiberlampstruct;
110
111 static fiberlampstruct *fiberlamps = (fiberlampstruct *) NULL;
112
113 void
114 change_fiberlamp(ModeInfo * mi)
115 {
116   fiberlampstruct *fl;
117   if (fiberlamps == NULL)
118         return;
119   fl = &fiberlamps[MI_SCREEN(mi)];
120   fl->cx = (DRAND(SCALE/4)-SCALE/8)/SCALE; /* Knock the lamp */
121   fl->count = 0; /* Reset counter */
122   XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
123   XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), 0, 0,
124                                  MI_WIDTH(mi), MI_HEIGHT(mi));
125 }
126
127 static void
128 free_fiber(fiberlampstruct *fl)
129 {
130         if (fl->fiber) {
131                 int f;
132
133                 for (f = 0; f < fl->nfibers; f++) {
134                         fiberstruct *fs = fl->fiber + f;
135
136                         if (fs->node)
137                                 free(fs->node);
138                         if (fs->draw)
139                                 free(fs->draw);
140                         if (fs->erase)
141                                 free(fs->erase);
142                 }
143                 free(fl->fiber);
144                 fl->fiber = NULL;
145         }
146 }
147
148 static void
149 free_fiberlamp(Display *display, fiberlampstruct *fl)
150 {
151         if (fl->buffer != None) {
152                 XFreePixmap(display, fl->buffer);
153                 fl->buffer = None;
154         }
155         free_fiber(fl);
156 }
157
158 void
159 init_fiberlamp(ModeInfo * mi)
160 {
161   fiberlampstruct *fl;
162   Bool init = False;
163
164   if (fiberlamps == NULL) {
165         init = True;
166         if ((fiberlamps =
167                  (fiberlampstruct *) calloc(MI_NUM_SCREENS(mi),
168                         sizeof (fiberlampstruct))) == NULL)
169           return;
170   }
171   fl = &fiberlamps[MI_SCREEN(mi)];
172
173   /* Create or Resize double buffer */
174   if(fl->buffer != None)
175         XFreePixmap(MI_DISPLAY(mi), fl->buffer);
176   fl->buffer = XCreatePixmap(MI_DISPLAY(mi), MI_WINDOW(mi),
177                 MI_WIDTH(mi), MI_HEIGHT(mi), MI_DEPTH(mi));
178   if (fl->buffer == None) {
179         free_fiberlamp(MI_DISPLAY(mi), fl);
180         return;
181   }
182   XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
183   XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), 0, 0,
184                                  MI_WIDTH(mi), MI_HEIGHT(mi));
185
186   if(!init) /* Nothing else to do (probably a resize) */
187         return;
188
189   fl->nfibers = MI_COUNT(mi);
190   /* Allocate fibers */
191   if((fl->fiber =
192           (fiberstruct*) calloc(fl->nfibers, sizeof (fiberstruct))) == NULL) {
193         free_fiberlamp(MI_DISPLAY(mi), fl);
194         return;
195   } else {
196         int f;
197         for(f = 0; f < fl->nfibers; f++) {
198           fiberstruct *fs = fl->fiber + f;
199           if((fs->node =
200                   (nodestruct*) calloc(NODES, sizeof (nodestruct))) == NULL
201                  ||(fs->draw =
202                         (XPoint*) calloc(NODES, sizeof (XPoint))) == NULL
203                  ||(fs->erase =
204                         (XPoint*) calloc(NODES, sizeof (XPoint))) == NULL) {
205                 free_fiberlamp(MI_DISPLAY(mi), fl);
206                 return;
207           }
208         }
209   }
210
211   {
212         int f, i;
213         for(f = 0; f < fl->nfibers; f++) {
214           double phi = M_PI/180 * DRAND(SPREAD);
215           double eta = DRAND(2*M_PI) - M_PI;
216           for(i = 0; i < NODES; i++) {
217                 nodestruct *n = &fl->fiber[f].node[i];
218                 n->phi = phi;
219                 n->phidash = 0;
220                 n->eta = eta;
221                 n->etadash = 0;
222           }
223           fl->fiber[f].node[0].etadash = 0.002/DT;
224           fl->fiber[f].node[0].y = 0;
225           fl->fiber[f].node[0].z = 0;
226         }
227
228   }
229
230   /* Set up rotation */
231   fl->psi = DRAND(2*M_PI);
232   fl->dpsi = 0.01;
233
234   /* no "NoExpose" events from XCopyArea wanted */
235   XSetGraphicsExposures(MI_DISPLAY(mi), MI_GC(mi), False);
236
237   /* Make sure we're using 'thin' lines */
238   XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 0, LineSolid, CapNotLast,
239                                          JoinMiter);
240 #ifdef CHECKCOLORWHEEL
241   /* Only interested in tips, leave the rest black */
242   fl->bright = fl->medium = fl->dim = MI_BLACK_PIXEL(mi);
243 #else
244   if(MI_NPIXELS(mi) > 2) {
245         /* Set up colours for the fiber bodies.  Tips handled seperately */
246         XColor c, t;
247         if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#E0E0C0", &c, &t)){
248           fl->bright = c.pixel;
249         } else {
250           fl->bright = MI_WHITE_PIXEL(mi);
251         }
252         if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#808070", &c, &t)){
253           fl->medium = c.pixel;
254         } else {
255           fl->medium = MI_WHITE_PIXEL(mi);
256         }
257         if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#404020", &c, &t)){
258           fl->dim = c.pixel;
259         } else {
260           fl->dim = MI_BLACK_PIXEL(mi);
261         }
262   } else {
263         fl->bright = MI_WHITE_PIXEL(mi);
264         fl->medium = MI_WHITE_PIXEL(mi);
265         fl->dim = MI_BLACK_PIXEL(mi);
266   }
267 #endif
268
269   /* Clear the background. */
270   MI_CLEARWINDOW(mi);
271   change_fiberlamp(mi);
272 }
273
274 #ifdef STANDALONE
275 /* Used by xscreensaver.  xlock just uses init_fiberlamp */
276 void
277 reshape_fiberlamp(ModeInfo * mi, int width, int height)
278 {
279   init_fiberlamp(mi);
280 }
281 #endif
282
283 void
284 draw_fiberlamp(ModeInfo * mi)
285 {
286   fiberlampstruct *fl;
287   int f, i;
288   int x, y;
289   Window unused;
290
291   short cx = MI_WIDTH(mi)/2;
292 #if defined PLAN || defined CHECKCOLORWHEEL
293   short cy = MI_HEIGHT(mi)/2;
294 #else
295   short cy = MI_HEIGHT(mi);
296 #endif
297
298   if (fiberlamps == NULL)
299         return;
300   fl = &fiberlamps[MI_SCREEN(mi)];
301
302   fl->psi += fl->dpsi;      /* turn colorwheel */
303
304   XTranslateCoordinates(MI_DISPLAY(mi), MI_WINDOW(mi),
305                                                 RootWindow(MI_DISPLAY(mi),MI_SCREEN(mi)),
306                                                 cx, cy, &x, &y, &unused);
307
308   for(f = 0; f < fl->nfibers; f++) {
309         fiberstruct *fs = fl->fiber + f;
310
311         fs->node[0].eta += DT*fs->node[0].etadash;
312         fs->node[0].x = fl->cx; /* Handle center movement */
313         /* Handle window move.  NOTE, only x is deflected, since y doesn't
314          directly affect the physics */
315         fs->node[NODES-2].x *= 0.1*(fl->ry - y);
316         fs->node[NODES-2].x += 0.05*(fl->rx - x);
317
318         /* 2nd order diff equation */
319         for(i = 1; i < NODES; i++) {
320           nodestruct *n = fs->node+i;
321           nodestruct *p = fs->node+i-1;
322           double pload = 0;
323           double eload = 0;
324           double pstress = (n->phi - p->phi)*PY;
325           double estress = (n->eta - p->eta)*PY;
326           double dxi = n->x - p->x;
327           double dzi = n->z - p->z;
328           double li = sqrt(dxi*dxi + dzi*dzi)/LEN(i);
329           double drag = DAMPING*LEN(i)*LEN(i)*NODES*NODES;
330
331           if(li > 0) {
332                 int j;
333                 for(j = i+1; j < NODES; j++) {
334                   nodestruct *nn = fs->node+j;
335                   double dxj = nn->x - n->x;
336                   double dzj = nn->z - n->z;
337
338                   pload += LEN(j)*(dxi*dxj + dzi*dzj)/li; /* Radial load */
339                   eload += LEN(j)*(dxi*dzj - dzi*dxj)/li; /* Transverse load */
340                   /* Not a perfect simulation: in reality the transverse load
341                          is only indirectly coupled to the eta deflection, but of
342                          all the approaches I've tried this produces the most
343                          stable model and looks the most realistic. */
344                 }
345           }
346
347 #ifndef CHECKCOLORWHEEL
348           n->phidash += DT*(pload - pstress - drag*n->phidash)/LEN(i);
349           n->phi += DT*n->phidash;
350 #endif
351
352           n->etadash += DT*(eload - estress - drag*n->etadash)/LEN(i);
353           n->eta += DT*n->etadash;
354
355           {
356                 double sp = sin(p->phi);
357                 double cp = cos(p->phi);
358                 double se = sin(p->eta);
359                 double ce = cos(p->eta);
360
361                 n->x = p->x + LEN(i-1) * ce * sp;
362                 n->y = p->y - LEN(i-1) * cp;
363                 n->z = p->z + LEN(i-1) * se * sp;
364           }
365
366           fs->draw[i-1].x = cx + MI_WIDTH(mi)/2*n->x;
367 #if defined PLAN || defined CHECKCOLORWHEEL /* Plan */
368           fs->draw[i-1].y = cy + MI_WIDTH(mi)/2*n->z;
369 #else /* Elevation */
370           fs->draw[i-1].y = cy + MI_WIDTH(mi)/2*n->y;
371 #endif
372         }
373         MI_IS_DRAWN(mi) = True;
374
375         /* Erase: this may only be erasing an off-screen buffer, but on a
376            slow system it may still be faster than XFillRectangle() */
377         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
378         XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
379                            fs->erase, NODES-1, CoordModeOrigin);
380
381         {
382           double x = fs->node[1].x - fl->cx + 0.025;
383           double y = fs->node[1].z + 0.02;
384           double angle = atan2(y, x) + fl->psi;
385           int tipcolor = MI_PIXEL(mi,
386                 (int)(MI_NPIXELS(mi)*angle/(2*M_PI)) % MI_NPIXELS(mi));
387           int fibercolor;
388           int tiplen;
389
390           if(fs->node[1].z < 0.0) { /* Back */
391                 tiplen = 2;
392                 fibercolor = fl->dim;
393           }else if(fs->node[NODES-1].z < 0.7) { /* Middle */
394                 tiplen = 3;
395                 fibercolor = fl->medium;
396           } else {                 /* Front */
397                 tiplen = 3;
398                 fibercolor = fl->bright;
399           }
400
401           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), fibercolor);
402           XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
403                                  fs->draw, NODES-tiplen, CoordModeOrigin);
404
405           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), tipcolor);
406           XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
407                                  fs->draw+NODES-1-tiplen, tiplen, CoordModeOrigin);
408         }
409
410         { /* Switch buffers */
411           XPoint *buffer = fs->draw;
412           fs->draw = fs->erase;
413           fs->erase = buffer;
414         }
415   }
416
417   /* Update the screen from the double-buffer */
418   XCopyArea(MI_DISPLAY(mi), fl->buffer, MI_WINDOW(mi), MI_GC(mi), 0, 0,
419                         MI_WIDTH(mi), MI_HEIGHT(mi), 0, 0);
420
421   fl->rx = x;
422   fl->ry = y;
423
424   if(fl->count++ > MI_CYCLES(mi)) {
425         change_fiberlamp(mi);
426   }
427 }
428
429 void
430 release_fiberlamp(ModeInfo * mi)
431 {
432         if (fiberlamps != NULL) {
433                 int         screen;
434
435                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
436                         free_fiberlamp(MI_DISPLAY(mi), &fiberlamps[screen]);
437                 free(fiberlamps);
438                 fiberlamps = (fiberlampstruct *) NULL;
439         }
440 }
441
442 #if 0
443 void
444 refresh_fiberlamp(ModeInfo * mi)
445 {
446         MI_CLEARWINDOW(mi);
447 }
448 #endif
449 #endif /* MODE_fiberlamp */