ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-4.22.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 <tda10.geo@yahoo.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 /* sort fibers so they get drawn back-to-front, one bubble pass is
284    enough as the order only changes slowly */
285 static void
286 sort_fibers(fiberlampstruct *fl)
287 {
288         int i;
289
290         for(i = 1; i < fl->nfibers; i++) {
291                 if (fl->fiber[i - 1].node[NODES - 1].z > fl->fiber[i].node[NODES - 1].z) {
292                         fiberstruct tmp = fl->fiber[i - 1];
293                         fl->fiber[i - 1] = fl->fiber[i];
294                         fl->fiber[i] = tmp;
295                 }
296         }
297 }
298
299 void
300 draw_fiberlamp(ModeInfo * mi)
301 {
302   fiberlampstruct *fl;
303   int f, i;
304   int x, y;
305   Window unused;
306
307   short cx = MI_WIDTH(mi)/2;
308 #if defined PLAN || defined CHECKCOLORWHEEL
309   short cy = MI_HEIGHT(mi)/2;
310 #else
311   short cy = MI_HEIGHT(mi);
312 #endif
313
314   if (fiberlamps == NULL)
315         return;
316   fl = &fiberlamps[MI_SCREEN(mi)];
317
318   fl->psi += fl->dpsi;      /* turn colorwheel */
319
320   XTranslateCoordinates(MI_DISPLAY(mi), MI_WINDOW(mi),
321                                                 RootWindow(MI_DISPLAY(mi),MI_SCREEN(mi)),
322                                                 cx, cy, &x, &y, &unused);
323   sort_fibers(fl);
324
325   for(f = 0; f < fl->nfibers; f++) {
326         fiberstruct *fs = fl->fiber + f;
327
328         fs->node[0].eta += DT*fs->node[0].etadash;
329         fs->node[0].x = fl->cx; /* Handle center movement */
330         /* Handle window move.  NOTE, only x is deflected, since y doesn't
331          directly affect the physics */
332         fs->node[NODES-2].x *= 0.1*(fl->ry - y);
333         fs->node[NODES-2].x += 0.05*(fl->rx - x);
334
335         /* 2nd order diff equation */
336         for(i = 1; i < NODES; i++) {
337           nodestruct *n = fs->node+i;
338           nodestruct *p = fs->node+i-1;
339           double pload = 0;
340           double eload = 0;
341           double pstress = (n->phi - p->phi)*PY;
342           double estress = (n->eta - p->eta)*PY;
343           double dxi = n->x - p->x;
344           double dzi = n->z - p->z;
345           double li = sqrt(dxi*dxi + dzi*dzi)/LEN(i);
346           double drag = DAMPING*LEN(i)*LEN(i)*NODES*NODES;
347
348           if(li > 0) {
349                 int j;
350                 for(j = i+1; j < NODES; j++) {
351                   nodestruct *nn = fs->node+j;
352                   double dxj = nn->x - n->x;
353                   double dzj = nn->z - n->z;
354
355                   pload += LEN(j)*(dxi*dxj + dzi*dzj)/li; /* Radial load */
356                   eload += LEN(j)*(dxi*dzj - dzi*dxj)/li; /* Transverse load */
357                   /* Not a perfect simulation: in reality the transverse load
358                          is only indirectly coupled to the eta deflection, but of
359                          all the approaches I've tried this produces the most
360                          stable model and looks the most realistic. */
361                 }
362           }
363
364 #ifndef CHECKCOLORWHEEL
365           n->phidash += DT*(pload - pstress - drag*n->phidash)/LEN(i);
366           n->phi += DT*n->phidash;
367 #endif
368
369           n->etadash += DT*(eload - estress - drag*n->etadash)/LEN(i);
370           n->eta += DT*n->etadash;
371
372           {
373                 double sp = sin(p->phi);
374                 double cp = cos(p->phi);
375                 double se = sin(p->eta);
376                 double ce = cos(p->eta);
377
378                 n->x = p->x + LEN(i-1) * ce * sp;
379                 n->y = p->y - LEN(i-1) * cp;
380                 n->z = p->z + LEN(i-1) * se * sp;
381           }
382
383           fs->draw[i-1].x = cx + MI_WIDTH(mi)/2*n->x;
384 #if defined PLAN || defined CHECKCOLORWHEEL /* Plan */
385           fs->draw[i-1].y = cy + MI_WIDTH(mi)/2*n->z;
386 #else /* Elevation */
387           fs->draw[i-1].y = cy + MI_WIDTH(mi)/2*n->y;
388 #endif
389         }
390         MI_IS_DRAWN(mi) = True;
391
392         /* Erase: this may only be erasing an off-screen buffer, but on a
393            slow system it may still be faster than XFillRectangle() */
394         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
395         XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
396                            fs->erase, NODES-1, CoordModeOrigin);
397   }
398
399   for(f = 0; f < fl->nfibers; f++) {
400         fiberstruct *fs = fl->fiber + f;
401
402         {
403           double x = fs->node[1].x - fl->cx + 0.025;
404           double y = fs->node[1].z + 0.02;
405           double angle = atan2(y, x) + fl->psi;
406           int tipcolor = MI_PIXEL(mi,
407                 (int)(MI_NPIXELS(mi)*angle/(2*M_PI)) % MI_NPIXELS(mi));
408           int fibercolor;
409           int tiplen;
410
411           if(fs->node[1].z < 0.0) { /* Back */
412                 tiplen = 2;
413                 fibercolor = fl->dim;
414           }else if(fs->node[NODES-1].z < 0.7) { /* Middle */
415                 tiplen = 3;
416                 fibercolor = fl->medium;
417           } else {                 /* Front */
418                 tiplen = 3;
419                 fibercolor = fl->bright;
420           }
421
422           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), fibercolor);
423           XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
424                                  fs->draw, NODES-tiplen, CoordModeOrigin);
425
426           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), tipcolor);
427           XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
428                                  fs->draw+NODES-1-tiplen, tiplen, CoordModeOrigin);
429         }
430
431         { /* Switch buffers */
432           XPoint *buffer = fs->draw;
433           fs->draw = fs->erase;
434           fs->erase = buffer;
435         }
436   }
437
438   /* Update the screen from the double-buffer */
439   XCopyArea(MI_DISPLAY(mi), fl->buffer, MI_WINDOW(mi), MI_GC(mi), 0, 0,
440                         MI_WIDTH(mi), MI_HEIGHT(mi), 0, 0);
441
442   fl->rx = x;
443   fl->ry = y;
444
445   if(fl->count++ > MI_CYCLES(mi)) {
446         change_fiberlamp(mi);
447   }
448 }
449
450 void
451 release_fiberlamp(ModeInfo * mi)
452 {
453         if (fiberlamps != NULL) {
454                 int         screen;
455
456                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
457                         free_fiberlamp(MI_DISPLAY(mi), &fiberlamps[screen]);
458                 free(fiberlamps);
459                 fiberlamps = (fiberlampstruct *) NULL;
460         }
461 }
462
463 #if 0
464 void
465 refresh_fiberlamp(ModeInfo * mi)
466 {
467         MI_CLEARWINDOW(mi);
468 }
469 #endif
470 #endif /* MODE_fiberlamp */