1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* fiberlamp --- A Fiber Optic Lamp */
3
4 #if 0
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 AT 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 * 21-Mar-2005: [JLH] Incremental Bubble Sort patch.
30 * 15-Feb-2005: [TDA] Shake Window patch.
31 * 13-Jan-2005: [TDA] Initial development.
32 */
33
34 #ifdef STANDALONE
35 #define MODE_fiberlamp
36 #define DEFAULTS "*delay: 10000 \n" \
37 "*count: 500 \n" \
38 "*cycles: 10000 \n" \
39 "*ncolors: 64 \n" \
40
41 # define free_fiberlamp 0
42 # define fiberlamp_handle_event 0
43 #define UNIFORM_COLORS
44 #include "xlockmore.h" /* in xscreensaver distribution */
45 # ifndef MI_DEPTH
46 # define MI_DEPTH MI_WIN_DEPTH
47 # endif
48 #else /* STANDALONE */
49 #include "xlock.h" /* in xlockmore distribution */
50 #endif /* STANDALONE */
51
52 #ifdef MODE_fiberlamp
53
54 ENTRYPOINT ModeSpecOpt fiberlamp_opts =
55 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
56
57 #ifdef USE_MODULES
58 ModStruct fiberlamp_description =
59 {"fiberlamp", "init_fiberlamp", "draw_fiberlamp", "release_fiberlamp",
60 "draw_fiberlamp", "change_fiberlamp", (char *) NULL, &fiberlamp_opts,
61 1000, 500, 10000, 0, 64, 1.0, "", "Shows a Fiber Optic Lamp", 0, NULL};
62
63 #endif
64
65 #define SPREAD (30.0) /* Angular spread at the base */
66 #define SCALE (MI_WIDTH(mi)/2) /* Screen size */
67 #define NODES (20) /* Number of nodes in a fiber. Variable with range
68 10 .. 30, if desired. High values have
69 stability problems unless you use small DT */
70
71 /* Physics parameters. Tune carefully to keep realism and avoid instability*/
72 #define DT (0.5) /* Time increment: Low is slow, High is less stable. */
73 #define PY (0.12) /* Rigidity: Low droops, High is stiff. */
74 #define DAMPING (0.055) /* Damping: Low allows oscillations, High is boring. */
75
76 #undef PLAN /* Plan view (for debugging) */
77 #undef CHECKCOLORWHEEL /* Plan view with no spread */
78
79 #define DRAND(v) (LRAND()/MAXRAND*(v)) /* double random 0 - v */
80
81 /* Length of nodes. Uniform except for shorter notes at the tips for
82 colour highlights. Sum from 0..NODES-1 should exactly 1.0 */
83 #define LEN(A) ((A<NODES-3) ? 1.0/(NODES-2.5) : 0.25/(NODES-2.5))
84
85 typedef struct {
86 double phi, phidash;
87 double eta, etadash;
88 double x;
89 double y;
90 double z;
91 } nodestruct;
92
93 typedef struct {
94 nodestruct *node;
95 XPoint *draw;
96 XPoint *erase;
97 } fiberstruct;
98
99 typedef struct {
100 double psi;
101 double dpsi;
102 int count, nfibers;
103 double cx;
104 double rx, ry; /* Coordinates relative to root */
105 fiberstruct *fiber;
106 Pixmap buffer; /* Double Buffer */
107 long bright, medium, dim; /* "White" colors */
108 } fiberlampstruct;
109
110 static fiberlampstruct *fiberlamps = (fiberlampstruct *) NULL;
111
112 static void
free_fiber(fiberlampstruct * fl)113 free_fiber(fiberlampstruct *fl)
114 {
115 if (fl->fiber) {
116 int f;
117
118 for (f = 0; f < fl->nfibers; f++) {
119 fiberstruct *fs = fl->fiber + f;
120
121 if (fs->node)
122 free(fs->node);
123 if (fs->draw)
124 free(fs->draw);
125 if (fs->erase)
126 free(fs->erase);
127 }
128 free(fl->fiber);
129 fl->fiber = NULL;
130 }
131 }
132
133 static void
free_fiberlamp_screen(Display * display,fiberlampstruct * fl)134 free_fiberlamp_screen(Display *display, fiberlampstruct *fl)
135 {
136 if (fl == NULL) {
137 return;
138 }
139 if (fl->buffer != None) {
140 XFreePixmap(display, fl->buffer);
141 fl->buffer = None;
142 }
143 free_fiber(fl);
144 fl = NULL;
145 }
146
147 ENTRYPOINT void
change_fiberlamp(ModeInfo * mi)148 change_fiberlamp(ModeInfo * mi)
149 {
150 fiberlampstruct *fl;
151 if (fiberlamps == NULL)
152 return;
153 fl = &fiberlamps[MI_SCREEN(mi)];
154 fl->cx = (DRAND(SCALE/4)-SCALE/8)/SCALE; /* Knock the lamp */
155 fl->count = 0; /* Reset counter */
156 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
157 XFillRectangle(MI_DISPLAY(mi), (Drawable) fl->buffer, MI_GC(mi), 0, 0,
158 MI_WIDTH(mi), MI_HEIGHT(mi));
159 }
160
161 ENTRYPOINT void
init_fiberlamp(ModeInfo * mi)162 init_fiberlamp(ModeInfo * mi)
163 {
164 fiberlampstruct *fl;
165 Bool init = False;
166
167 if (fiberlamps == NULL) {
168 init = True;
169 MI_INIT(mi, fiberlamps);
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_screen(MI_DISPLAY(mi), fl);
180 return;
181 }
182 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
183 XFillRectangle(MI_DISPLAY(mi), (Drawable) 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_screen(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_screen(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 separately */
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 /* sort fibers so they get drawn back-to-front, one bubble pass is
275 enough as the order only changes slowly */
276 static void
sort_fibers(fiberlampstruct * fl)277 sort_fibers(fiberlampstruct *fl)
278 {
279 int i;
280
281 for(i = 1; i < fl->nfibers; i++) {
282 if (fl->fiber[i - 1].node[NODES - 1].z > fl->fiber[i].node[NODES - 1].z) {
283 fiberstruct tmp = fl->fiber[i - 1];
284 fl->fiber[i - 1] = fl->fiber[i];
285 fl->fiber[i] = tmp;
286 }
287 }
288 }
289
290 ENTRYPOINT void
draw_fiberlamp(ModeInfo * mi)291 draw_fiberlamp(ModeInfo * mi)
292 {
293 fiberlampstruct *fl;
294 int f, i;
295 int x, y;
296 Window unused;
297
298 short cx = MI_WIDTH(mi)/2;
299 #if defined PLAN || defined CHECKCOLORWHEEL
300 short cy = MI_HEIGHT(mi)/2;
301 #else
302 short cy = MI_HEIGHT(mi);
303 #endif
304
305 if (fiberlamps == NULL)
306 return;
307 fl = &fiberlamps[MI_SCREEN(mi)];
308
309 fl->psi += fl->dpsi; /* turn colorwheel */
310
311 XTranslateCoordinates(MI_DISPLAY(mi), MI_WINDOW(mi),
312 RootWindow(MI_DISPLAY(mi),MI_SCREEN(mi)),
313 cx, cy, &x, &y, &unused);
314 sort_fibers(fl);
315
316 for(f = 0; f < fl->nfibers; f++) {
317 fiberstruct *fs = fl->fiber + f;
318
319 fs->node[0].eta += DT*fs->node[0].etadash;
320 fs->node[0].x = fl->cx; /* Handle center movement */
321 /* Handle window move. NOTE, only x is deflected, since y doesn't
322 directly affect the physics */
323 fs->node[NODES-2].x *= 0.1*(fl->ry - y);
324 fs->node[NODES-2].x += 0.05*(fl->rx - x);
325
326 /* 2nd order diff equation */
327 for(i = 1; i < NODES; i++) {
328 nodestruct *n = fs->node+i;
329 nodestruct *p = fs->node+i-1;
330 double pload = 0;
331 double eload = 0;
332 double pstress = (n->phi - p->phi)*PY;
333 double estress = (n->eta - p->eta)*PY;
334 double dxi = n->x - p->x;
335 double dzi = n->z - p->z;
336 double li = sqrt(dxi*dxi + dzi*dzi)/LEN(i);
337 double drag = DAMPING*LEN(i)*LEN(i)*NODES*NODES;
338
339 if(li > 0) {
340 int j;
341 for(j = i+1; j < NODES; j++) {
342 nodestruct *nn = fs->node+j;
343 double dxj = nn->x - n->x;
344 double dzj = nn->z - n->z;
345
346 pload += LEN(j)*(dxi*dxj + dzi*dzj)/li; /* Radial load */
347 eload += LEN(j)*(dxi*dzj - dzi*dxj)/li; /* Transverse load */
348 /* Not a perfect simulation: in reality the transverse load
349 is only indirectly coupled to the eta deflection, but of
350 all the approaches I've tried this produces the most
351 stable model and looks the most realistic. */
352 }
353 }
354
355 #ifndef CHECKCOLORWHEEL
356 n->phidash += DT*(pload - pstress - drag*n->phidash)/LEN(i);
357 n->phi += DT*n->phidash;
358 #endif
359
360 n->etadash += DT*(eload - estress - drag*n->etadash)/LEN(i);
361 n->eta += DT*n->etadash;
362
363 {
364 double sp = sin(p->phi);
365 double cp = cos(p->phi);
366 double se = sin(p->eta);
367 double ce = cos(p->eta);
368
369 n->x = p->x + LEN(i-1) * ce * sp;
370 n->y = p->y - LEN(i-1) * cp;
371 n->z = p->z + LEN(i-1) * se * sp;
372 }
373
374 fs->draw[i-1].x = (short) (cx + MI_WIDTH(mi)/2*n->x);
375 #if defined PLAN || defined CHECKCOLORWHEEL /* Plan */
376 fs->draw[i-1].y = (short) (cy + MI_WIDTH(mi)/2*n->z);
377 #else /* Elevation */
378 fs->draw[i-1].y = (short) (cy + MI_WIDTH(mi)/2*n->y);
379 #endif
380 }
381 MI_IS_DRAWN(mi) = True;
382
383 /* Erase: this may only be erasing an off-screen buffer, but on a
384 slow system it may still be faster than XFillRectangle() */
385 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
386 XDrawLines(MI_DISPLAY(mi), (Drawable) fl->buffer, MI_GC(mi),
387 fs->erase, NODES-1, CoordModeOrigin);
388 }
389
390 for(f = 0; f < fl->nfibers; f++) {
391 fiberstruct *fs = fl->fiber + f;
392
393 {
394 double x = fs->node[1].x - fl->cx + 0.025;
395 double y = fs->node[1].z + 0.02;
396 double angle = atan2(y, x) + fl->psi;
397 int tipcolor = MI_PIXEL(mi,
398 (int)(MI_NPIXELS(mi)*angle/(2*M_PI) + MI_NPIXELS(mi)) %
399 MI_NPIXELS(mi));
400 int fibercolor;
401 int tiplen;
402
403 if(fs->node[1].z < 0.0) { /* Back */
404 tiplen = 2;
405 fibercolor = fl->dim;
406 }else if(fs->node[NODES-1].z < 0.7) { /* Middle */
407 tiplen = 3;
408 fibercolor = fl->medium;
409 } else { /* Front */
410 tiplen = 3;
411 fibercolor = fl->bright;
412 }
413
414 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), fibercolor);
415 XDrawLines(MI_DISPLAY(mi), (Drawable) fl->buffer, MI_GC(mi),
416 fs->draw, NODES-tiplen, CoordModeOrigin);
417
418 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), tipcolor);
419 XDrawLines(MI_DISPLAY(mi), (Drawable) fl->buffer, MI_GC(mi),
420 fs->draw+NODES-1-tiplen, tiplen, CoordModeOrigin);
421 }
422
423 { /* Switch buffers */
424 XPoint *buffer = fs->draw;
425 fs->draw = fs->erase;
426 fs->erase = buffer;
427 }
428 }
429
430 /* Update the screen from the double-buffer */
431 XCopyArea(MI_DISPLAY(mi), (Drawable) fl->buffer, MI_WINDOW(mi), MI_GC(mi),
432 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi), 0, 0);
433
434 fl->rx = x;
435 fl->ry = y;
436
437 if(fl->count++ > MI_CYCLES(mi)) {
438 change_fiberlamp(mi);
439 }
440 }
441
442 ENTRYPOINT void
release_fiberlamp(ModeInfo * mi)443 release_fiberlamp(ModeInfo * mi)
444 {
445 if (fiberlamps != NULL) {
446 int screen;
447
448 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
449 free_fiberlamp_screen(MI_DISPLAY(mi), &fiberlamps[screen]);
450 free(fiberlamps);
451 fiberlamps = (fiberlampstruct *) NULL;
452 }
453 }
454
455 #ifdef STANDALONE
456 /* Used by xscreensaver. xlock just uses init_fiberlamp */
457 ENTRYPOINT void
reshape_fiberlamp(ModeInfo * mi,int width,int height)458 reshape_fiberlamp(ModeInfo * mi, int width, int height)
459 {
460 init_fiberlamp(mi);
461 }
462 #endif
463
464 XSCREENSAVER_MODULE ("Fiberlamp", fiberlamp)
465
466 #endif /* MODE_fiberlamp */
467