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@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 DEFAULTS "*delay: 10000 \n" \
35 "*count: 500 \n" \
36 "*cycles: 10000 \n" \
37 "*ncolors: 64 \n" \
38 "*fpsTop: true \n" \
39
40 # define UNIFORM_COLORS
41 # define release_fiberlamp 0
42 # define reshape_fiberlamp 0
43 # define fiberlamp_handle_event 0
44 # include "xlockmore.h" /* in xscreensaver distribution */
45 #else /* STANDALONE */
46 # include "xlock.h" /* in xlockmore distribution */
47 #endif /* STANDALONE */
48
49 #ifdef MODE_fiberlamp
50
51 ENTRYPOINT ModeSpecOpt fiberlamp_opts =
52 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
53
54 #ifdef USE_MODULES
55 ModStruct fiberlamp_description =
56 {"fiberlamp", "init_fiberlamp", "draw_fiberlamp", (char *) NULL,
57 "draw_fiberlamp", "change_fiberlamp", "free_fiberlamp", &fiberlamp_opts,
58 1000, 500, 10000, 0, 64, 1.0, "", "Shows a Fiber Optic Lamp", 0, NULL};
59
60 #endif
61
62 #define SPREAD (30.0) /* Angular spread at the base */
63 #define SCALE (MI_WIDTH(mi)/2) /* Screen size */
64 #define NODES (20L) /* Number of nodes in a fiber. Variable with range
65 10 .. 30, if desired. High values have
66 stability problems unless you use small DT */
67
68 /* Physics parameters. Tune carefully to keep realism and avoid instability*/
69 #define DT (0.5) /* Time increment: Low is slow, High is less stable. */
70 #define PY (0.12) /* Rigidity: Low droops, High is stiff. */
71 #define DAMPING (0.055) /* Damping: Low allows oscillations, High is boring. */
72
73 #undef PLAN /* Plan view (for debugging) */
74 #undef CHECKCOLORWHEEL /* Plan view with no spread */
75
76 #define DRAND(v) (LRAND()/MAXRAND*(v)) /* double random 0 - v */
77
78 /* Length of nodes. Uniform except for shorter notes at the tips for
79 colour highlights. Sum from 0..NODES-1 should exactly 1.0 */
80 #define LEN(A) ((A<NODES-3) ? 1.0/(NODES-2.5) : 0.25/(NODES-2.5))
81
82
83 typedef struct {
84 double phi, phidash;
85 double eta, etadash;
86 double x;
87 double y;
88 double z;
89 } nodestruct;
90
91 typedef struct {
92 nodestruct *node;
93 XPoint *draw;
94 } fiberstruct;
95
96 typedef struct {
97 int init;
98 double psi;
99 double dpsi;
100 long count, nfibers;
101 double cx;
102 double rx, ry; /* Coordinates relative to root */
103 fiberstruct *fiber;
104 Bool dbufp;
105 Pixmap buffer; /* Double Buffer */
106 long bright, medium, dim; /* "White" colors */
107 } fiberlampstruct;
108
109 static fiberlampstruct *fiberlamps = (fiberlampstruct *) NULL;
110
111 static void
change_fiberlamp(ModeInfo * mi)112 change_fiberlamp(ModeInfo * mi)
113 {
114 fiberlampstruct *fl;
115 if (fiberlamps == NULL)
116 return;
117 fl = &fiberlamps[MI_SCREEN(mi)];
118 fl->cx = (DRAND(SCALE/4)-SCALE/8)/SCALE; /* Knock the lamp */
119 fl->count = 0; /* Reset counter */
120 if (fl->dbufp) {
121 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
122 XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), 0, 0,
123 MI_WIDTH(mi), MI_HEIGHT(mi));
124 }
125 }
126
127 static void
free_fiber(fiberlampstruct * fl)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 }
141 free(fl->fiber);
142 fl->fiber = NULL;
143 }
144 }
145
146 ENTRYPOINT void
free_fiberlamp(ModeInfo * mi)147 free_fiberlamp(ModeInfo *mi)
148 {
149 fiberlampstruct *fl = &fiberlamps[MI_SCREEN(mi)];
150 if (fl->buffer != None && fl->dbufp) {
151 XFreePixmap(MI_DISPLAY(mi), fl->buffer);
152 fl->buffer = None;
153 }
154 free_fiber(fl);
155 }
156
157 ENTRYPOINT void
init_fiberlamp(ModeInfo * mi)158 init_fiberlamp(ModeInfo * mi)
159 {
160 fiberlampstruct *fl;
161
162 MI_INIT (mi, fiberlamps);
163 fl = &fiberlamps[MI_SCREEN(mi)];
164
165 /* Create or Resize double buffer */
166 #ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */
167 fl->dbufp = False;
168 #else
169 fl->dbufp = True;
170 #endif
171
172 if(fl->buffer != None && fl->buffer != MI_WINDOW(mi) && fl->dbufp)
173 XFreePixmap(MI_DISPLAY(mi), fl->buffer);
174
175 if(fl->dbufp) {
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);
180 return;
181 }
182 } else {
183 fl->buffer = MI_WINDOW(mi);
184 }
185
186 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
187 XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), 0, 0,
188 MI_WIDTH(mi), MI_HEIGHT(mi));
189
190 if(fl->init) /* Nothing else to do (probably a resize) */
191 return;
192
193 fl->init = True;
194 fl->nfibers = MI_COUNT(mi);
195 /* Allocate fibers */
196 if((fl->fiber =
197 (fiberstruct*) calloc(fl->nfibers, sizeof (fiberstruct))) == NULL) {
198 free_fiberlamp(mi);
199 return;
200 } else {
201 int f;
202 for(f = 0; f < fl->nfibers; f++) {
203 fiberstruct *fs = fl->fiber + f;
204 if((fs->node =
205 (nodestruct*) calloc(NODES, sizeof (nodestruct))) == NULL
206 ||(fs->draw =
207 (XPoint*) calloc(NODES, sizeof (XPoint))) == NULL) {
208 free_fiberlamp(mi);
209 return;
210 }
211 }
212 }
213
214 {
215 int f, i;
216 for(f = 0; f < fl->nfibers; f++) {
217 double phi = M_PI/180 * DRAND(SPREAD);
218 double eta = DRAND(2*M_PI) - M_PI;
219 for(i = 0; i < NODES; i++) {
220 nodestruct *n = &fl->fiber[f].node[i];
221 n->phi = phi;
222 n->phidash = 0;
223 n->eta = eta;
224 n->etadash = 0;
225 }
226 fl->fiber[f].node[0].etadash = 0.002/DT;
227 fl->fiber[f].node[0].y = 0;
228 fl->fiber[f].node[0].z = 0;
229 }
230
231 }
232
233 /* Set up rotation */
234 fl->psi = DRAND(2*M_PI);
235 fl->dpsi = 0.01;
236
237 /* no "NoExpose" events from XCopyArea wanted */
238 XSetGraphicsExposures(MI_DISPLAY(mi), MI_GC(mi), False);
239
240 /* Make sure we're using 'thin' lines */
241 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 0, LineSolid, CapNotLast,
242 JoinMiter);
243 #ifdef CHECKCOLORWHEEL
244 /* Only interested in tips, leave the rest black */
245 fl->bright = fl->medium = fl->dim = MI_BLACK_PIXEL(mi);
246 #else
247 if(MI_NPIXELS(mi) > 2) {
248 /* Set up colours for the fiber bodies. Tips handled seperately */
249 XColor c, t;
250 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#E0E0C0", &c, &t)){
251 fl->bright = c.pixel;
252 } else {
253 fl->bright = MI_WHITE_PIXEL(mi);
254 }
255 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#808070", &c, &t)){
256 fl->medium = c.pixel;
257 } else {
258 fl->medium = MI_WHITE_PIXEL(mi);
259 }
260 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#404020", &c, &t)){
261 fl->dim = c.pixel;
262 } else {
263 fl->dim = MI_BLACK_PIXEL(mi);
264 }
265 } else {
266 fl->bright = MI_WHITE_PIXEL(mi);
267 fl->medium = MI_WHITE_PIXEL(mi);
268 fl->dim = MI_BLACK_PIXEL(mi);
269 }
270 #endif
271
272 /* Clear the background. */
273 MI_CLEARWINDOW(mi);
274 change_fiberlamp(mi);
275 }
276
277 /* sort fibers so they get drawn back-to-front, one bubble pass is
278 enough as the order only changes slowly */
279 static void
sort_fibers(fiberlampstruct * fl)280 sort_fibers(fiberlampstruct *fl)
281 {
282 int i;
283
284 for(i = 1; i < fl->nfibers; i++) {
285 if (fl->fiber[i - 1].node[NODES - 1].z > fl->fiber[i].node[NODES - 1].z) {
286 fiberstruct tmp = fl->fiber[i - 1];
287 fl->fiber[i - 1] = fl->fiber[i];
288 fl->fiber[i] = tmp;
289 }
290 }
291 }
292
293 ENTRYPOINT void
draw_fiberlamp(ModeInfo * mi)294 draw_fiberlamp (ModeInfo * mi)
295 {
296 fiberlampstruct *fl;
297 int f, i;
298 int x, y;
299 int ww, hh;
300 Window unused;
301 short cx, cy;
302
303 ww = MI_WIDTH(mi);
304 hh = MI_HEIGHT(mi);
305
306 cx = MI_WIDTH(mi)/2;
307 #if defined PLAN || defined CHECKCOLORWHEEL
308 cy = MI_HEIGHT(mi)/2;
309 #else
310 cy = MI_HEIGHT(mi);
311 #endif
312
313 if (ww > hh * 5 || /* window has weird aspect */
314 hh > ww * 5)
315 {
316 if (ww > hh)
317 {
318 hh = ww;
319 cy = hh / 4;
320 }
321 else
322 {
323 ww = hh;
324 cx = 0;
325 cy = hh*3/4;
326 }
327 }
328
329 if (fiberlamps == NULL)
330 return;
331 fl = &fiberlamps[MI_SCREEN(mi)];
332
333 fl->psi += fl->dpsi; /* turn colorwheel */
334
335 XTranslateCoordinates(MI_DISPLAY(mi), MI_WINDOW(mi),
336 RootWindow(MI_DISPLAY(mi),0/*#### MI_SCREEN(mi)*/),
337 cx, cy, &x, &y, &unused);
338 sort_fibers(fl);
339
340 for(f = 0; f < fl->nfibers; f++) {
341 fiberstruct *fs = fl->fiber + f;
342
343 fs->node[0].eta += DT*fs->node[0].etadash;
344 fs->node[0].x = fl->cx; /* Handle center movement */
345 /* Handle window move. NOTE, only x is deflected, since y doesn't
346 directly affect the physics */
347 fs->node[NODES-2].x *= 0.1*(fl->ry - y);
348 fs->node[NODES-2].x += 0.05*(fl->rx - x);
349
350 /* 2nd order diff equation */
351 for(i = 1; i < NODES; i++) {
352 nodestruct *n = fs->node+i;
353 nodestruct *p = fs->node+i-1;
354 double pload = 0;
355 double eload = 0;
356 double pstress = (n->phi - p->phi)*PY;
357 double estress = (n->eta - p->eta)*PY;
358 double dxi = n->x - p->x;
359 double dzi = n->z - p->z;
360 double li = sqrt(dxi*dxi + dzi*dzi)/LEN(i);
361 double drag = DAMPING*LEN(i)*LEN(i)*NODES*NODES;
362
363 if(li > 0) {
364 int j;
365 for(j = i+1; j < NODES; j++) {
366 nodestruct *nn = fs->node+j;
367 double dxj = nn->x - n->x;
368 double dzj = nn->z - n->z;
369
370 pload += LEN(j)*(dxi*dxj + dzi*dzj)/li; /* Radial load */
371 eload += LEN(j)*(dxi*dzj - dzi*dxj)/li; /* Transverse load */
372 /* Not a perfect simulation: in reality the transverse load
373 is only indirectly coupled to the eta deflection, but of
374 all the approaches I've tried this produces the most
375 stable model and looks the most realistic. */
376 }
377 }
378
379 #ifndef CHECKCOLORWHEEL
380 n->phidash += DT*(pload - pstress - drag*n->phidash)/LEN(i);
381 n->phi += DT*n->phidash;
382 #endif
383
384 n->etadash += DT*(eload - estress - drag*n->etadash)/LEN(i);
385 n->eta += DT*n->etadash;
386
387 {
388 double sp = sin(p->phi);
389 double cp = cos(p->phi);
390 double se = sin(p->eta);
391 double ce = cos(p->eta);
392
393 n->x = p->x + LEN(i-1) * ce * sp;
394 n->y = p->y - LEN(i-1) * cp;
395 n->z = p->z + LEN(i-1) * se * sp;
396 }
397
398 fs->draw[i-1].x = cx + ww/2*n->x;
399 #if defined PLAN || defined CHECKCOLORWHEEL /* Plan */
400 fs->draw[i-1].y = cy + ww/2*n->z;
401 #else /* Elevation */
402 fs->draw[i-1].y = cy + ww/2*n->y;
403 #endif
404 }
405 MI_IS_DRAWN(mi) = True;
406
407 /* Erase: this may only be erasing an off-screen buffer, but on a
408 slow system it may still be faster than XFillRectangle() */
409 /* That's unpossible. -jwz */
410 }
411
412 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
413 XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
414 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi));
415
416 for(f = 0; f < fl->nfibers; f++) {
417 fiberstruct *fs = fl->fiber + f;
418
419 {
420 double x = fs->node[1].x - fl->cx + 0.025;
421 double y = fs->node[1].z + 0.02;
422 double angle = atan2(y, x) + fl->psi;
423 long tipcolor = (int)(MI_NPIXELS(mi)*angle/(2*M_PI)) % MI_NPIXELS(mi);
424 long fibercolor;
425 long tiplen;
426
427 if (tipcolor < 0) tipcolor += MI_NPIXELS(mi);
428 tipcolor = MI_PIXEL(mi, tipcolor);
429
430 if(fs->node[1].z < 0.0) { /* Back */
431 tiplen = 2;
432 fibercolor = fl->dim;
433 }else if(fs->node[NODES-1].z < 0.7) { /* Middle */
434 tiplen = 3;
435 fibercolor = fl->medium;
436 } else { /* Front */
437 tiplen = 3;
438 fibercolor = fl->bright;
439 }
440
441 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), fibercolor);
442 XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
443 fs->draw, NODES-tiplen, CoordModeOrigin);
444
445 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), tipcolor);
446 XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
447 fs->draw+NODES-1-tiplen, tiplen, CoordModeOrigin);
448 }
449 }
450
451 /* Update the screen from the double-buffer */
452 if (fl->dbufp)
453 XCopyArea(MI_DISPLAY(mi), fl->buffer, MI_WINDOW(mi), MI_GC(mi), 0, 0,
454 MI_WIDTH(mi), MI_HEIGHT(mi), 0, 0);
455
456 fl->rx = x;
457 fl->ry = y;
458
459 if(fl->count++ > MI_CYCLES(mi)) {
460 change_fiberlamp(mi);
461 }
462 }
463
464 #ifndef STANDALONE
465 ENTRYPOINT void
refresh_fiberlamp(ModeInfo * mi)466 refresh_fiberlamp(ModeInfo * mi)
467 {
468 MI_CLEARWINDOW(mi);
469 }
470 #endif
471
472 XSCREENSAVER_MODULE ("Fiberlamp", fiberlamp)
473
474 #endif /* MODE_fiberlamp */
475