1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* fiberlamp --- A Fiber Optic Lamp */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)fiberlamp.c 5.00 2000/11/01 xlockmore";
10 * Copyright (c) 2005 by Tim Auckland <tda10.geo@yahoo.com>
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.
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.
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.
29 * 13-Jan-2005: Initial development.
33 #define MODE_fiberlamp
34 #define DEFAULTS "*delay: 10000 \n" \
38 # define UNIFORM_COLORS
39 # define fiberlamp_handle_event 0
40 # include "xlockmore.h" /* in xscreensaver distribution */
41 #else /* STANDALONE */
42 # include "xlock.h" /* in xlockmore distribution */
43 #endif /* STANDALONE */
47 ENTRYPOINT ModeSpecOpt fiberlamp_opts =
48 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
51 ModStruct fiberlamp_description =
52 {"fiberlamp", "init_fiberlamp", "draw_fiberlamp", "release_fiberlamp",
53 "draw_fiberlamp", "change_fiberlamp", (char *) NULL, &fiberlamp_opts,
54 1000, 500, 10000, 0, 64, 1.0, "", "Shows a Fiber Optic Lamp", 0, NULL};
58 #define SPREAD (30.0) /* Angular spread at the base */
59 #define SCALE (MI_WIDTH(mi)/2) /* Screen size */
60 #define NODES (20) /* Number of nodes in a fiber. Variable with range
61 10 .. 30, if desired. High values have
62 stability problems unless you use small DT */
64 /* Physics parameters. Tune carefully to keep realism and avoid instability*/
65 #define DT (0.5) /* Time increment: Low is slow, High is less stable. */
66 #define PY (0.12) /* Rigidity: Low droops, High is stiff. */
67 #define DAMPING (0.055) /* Damping: Low allows oscillations, High is boring. */
69 #undef PLAN /* Plan view (for debugging) */
70 #undef CHECKCOLORWHEEL /* Plan view with no spread */
72 #define DRAND(v) (LRAND()/MAXRAND*(v)) /* double random 0 - v */
74 /* Length of nodes. Uniform except for shorter notes at the tips for
75 colour highlights. Sum from 0..NODES-1 should exactly 1.0 */
76 #define LEN(A) ((A<NODES-3) ? 1.0/(NODES-2.5) : 0.25/(NODES-2.5))
98 double rx, ry; /* Coordinates relative to root */
101 Pixmap buffer; /* Double Buffer */
102 long bright, medium, dim; /* "White" colors */
105 static fiberlampstruct *fiberlamps = (fiberlampstruct *) NULL;
108 change_fiberlamp(ModeInfo * mi)
111 if (fiberlamps == NULL)
113 fl = &fiberlamps[MI_SCREEN(mi)];
114 fl->cx = (DRAND(SCALE/4)-SCALE/8)/SCALE; /* Knock the lamp */
115 fl->count = 0; /* Reset counter */
117 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
118 XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), 0, 0,
119 MI_WIDTH(mi), MI_HEIGHT(mi));
124 free_fiber(fiberlampstruct *fl)
129 for (f = 0; f < fl->nfibers; f++) {
130 fiberstruct *fs = fl->fiber + f;
143 free_fiberlamp(ModeInfo *mi, fiberlampstruct *fl)
145 if (fl->buffer != None && fl->dbufp) {
146 XFreePixmap(MI_DISPLAY(mi), fl->buffer);
153 init_fiberlamp(ModeInfo * mi)
157 if (fiberlamps == NULL) {
159 (fiberlampstruct *) calloc(MI_NUM_SCREENS(mi),
160 sizeof (fiberlampstruct))) == NULL)
163 fl = &fiberlamps[MI_SCREEN(mi)];
165 /* Create or Resize double buffer */
166 #ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
172 if(fl->buffer != None && fl->buffer != MI_WINDOW(mi) && fl->dbufp)
173 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, fl);
183 fl->buffer = MI_WINDOW(mi);
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));
190 if(fl->init) /* Nothing else to do (probably a resize) */
194 fl->nfibers = MI_COUNT(mi);
195 /* Allocate fibers */
197 (fiberstruct*) calloc(fl->nfibers, sizeof (fiberstruct))) == NULL) {
198 free_fiberlamp(mi, fl);
202 for(f = 0; f < fl->nfibers; f++) {
203 fiberstruct *fs = fl->fiber + f;
205 (nodestruct*) calloc(NODES, sizeof (nodestruct))) == NULL
207 (XPoint*) calloc(NODES, sizeof (XPoint))) == NULL) {
208 free_fiberlamp(mi, fl);
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];
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;
233 /* Set up rotation */
234 fl->psi = DRAND(2*M_PI);
237 /* no "NoExpose" events from XCopyArea wanted */
238 XSetGraphicsExposures(MI_DISPLAY(mi), MI_GC(mi), False);
240 /* Make sure we're using 'thin' lines */
241 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 0, LineSolid, CapNotLast,
243 #ifdef CHECKCOLORWHEEL
244 /* Only interested in tips, leave the rest black */
245 fl->bright = fl->medium = fl->dim = MI_BLACK_PIXEL(mi);
247 if(MI_NPIXELS(mi) > 2) {
248 /* Set up colours for the fiber bodies. Tips handled seperately */
250 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#E0E0C0", &c, &t)){
251 fl->bright = c.pixel;
253 fl->bright = MI_WHITE_PIXEL(mi);
255 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#808070", &c, &t)){
256 fl->medium = c.pixel;
258 fl->medium = MI_WHITE_PIXEL(mi);
260 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#404020", &c, &t)){
263 fl->dim = MI_BLACK_PIXEL(mi);
266 fl->bright = MI_WHITE_PIXEL(mi);
267 fl->medium = MI_WHITE_PIXEL(mi);
268 fl->dim = MI_BLACK_PIXEL(mi);
272 /* Clear the background. */
274 change_fiberlamp(mi);
277 /* Used by xscreensaver. xlock just uses init_fiberlamp */
279 reshape_fiberlamp(ModeInfo * mi, int width, int height)
284 /* sort fibers so they get drawn back-to-front, one bubble pass is
285 enough as the order only changes slowly */
287 sort_fibers(fiberlampstruct *fl)
291 for(i = 1; i < fl->nfibers; i++) {
292 if (fl->fiber[i - 1].node[NODES - 1].z > fl->fiber[i].node[NODES - 1].z) {
293 fiberstruct tmp = fl->fiber[i - 1];
294 fl->fiber[i - 1] = fl->fiber[i];
301 draw_fiberlamp (ModeInfo * mi)
308 short cx = MI_WIDTH(mi)/2;
309 #if defined PLAN || defined CHECKCOLORWHEEL
310 short cy = MI_HEIGHT(mi)/2;
312 short cy = MI_HEIGHT(mi);
315 if (fiberlamps == NULL)
317 fl = &fiberlamps[MI_SCREEN(mi)];
319 fl->psi += fl->dpsi; /* turn colorwheel */
321 XTranslateCoordinates(MI_DISPLAY(mi), MI_WINDOW(mi),
322 RootWindow(MI_DISPLAY(mi),0/*#### MI_SCREEN(mi)*/),
323 cx, cy, &x, &y, &unused);
326 for(f = 0; f < fl->nfibers; f++) {
327 fiberstruct *fs = fl->fiber + f;
329 fs->node[0].eta += DT*fs->node[0].etadash;
330 fs->node[0].x = fl->cx; /* Handle center movement */
331 /* Handle window move. NOTE, only x is deflected, since y doesn't
332 directly affect the physics */
333 fs->node[NODES-2].x *= 0.1*(fl->ry - y);
334 fs->node[NODES-2].x += 0.05*(fl->rx - x);
336 /* 2nd order diff equation */
337 for(i = 1; i < NODES; i++) {
338 nodestruct *n = fs->node+i;
339 nodestruct *p = fs->node+i-1;
342 double pstress = (n->phi - p->phi)*PY;
343 double estress = (n->eta - p->eta)*PY;
344 double dxi = n->x - p->x;
345 double dzi = n->z - p->z;
346 double li = sqrt(dxi*dxi + dzi*dzi)/LEN(i);
347 double drag = DAMPING*LEN(i)*LEN(i)*NODES*NODES;
351 for(j = i+1; j < NODES; j++) {
352 nodestruct *nn = fs->node+j;
353 double dxj = nn->x - n->x;
354 double dzj = nn->z - n->z;
356 pload += LEN(j)*(dxi*dxj + dzi*dzj)/li; /* Radial load */
357 eload += LEN(j)*(dxi*dzj - dzi*dxj)/li; /* Transverse load */
358 /* Not a perfect simulation: in reality the transverse load
359 is only indirectly coupled to the eta deflection, but of
360 all the approaches I've tried this produces the most
361 stable model and looks the most realistic. */
365 #ifndef CHECKCOLORWHEEL
366 n->phidash += DT*(pload - pstress - drag*n->phidash)/LEN(i);
367 n->phi += DT*n->phidash;
370 n->etadash += DT*(eload - estress - drag*n->etadash)/LEN(i);
371 n->eta += DT*n->etadash;
374 double sp = sin(p->phi);
375 double cp = cos(p->phi);
376 double se = sin(p->eta);
377 double ce = cos(p->eta);
379 n->x = p->x + LEN(i-1) * ce * sp;
380 n->y = p->y - LEN(i-1) * cp;
381 n->z = p->z + LEN(i-1) * se * sp;
384 fs->draw[i-1].x = cx + MI_WIDTH(mi)/2*n->x;
385 #if defined PLAN || defined CHECKCOLORWHEEL /* Plan */
386 fs->draw[i-1].y = cy + MI_WIDTH(mi)/2*n->z;
387 #else /* Elevation */
388 fs->draw[i-1].y = cy + MI_WIDTH(mi)/2*n->y;
391 MI_IS_DRAWN(mi) = True;
393 /* Erase: this may only be erasing an off-screen buffer, but on a
394 slow system it may still be faster than XFillRectangle() */
395 /* That's unpossible. -jwz */
398 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
399 XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
400 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi));
402 for(f = 0; f < fl->nfibers; f++) {
403 fiberstruct *fs = fl->fiber + f;
406 double x = fs->node[1].x - fl->cx + 0.025;
407 double y = fs->node[1].z + 0.02;
408 double angle = atan2(y, x) + fl->psi;
409 int tipcolor = (int)(MI_NPIXELS(mi)*angle/(2*M_PI)) % MI_NPIXELS(mi);
413 if (tipcolor < 0) tipcolor += MI_NPIXELS(mi);
414 tipcolor = MI_PIXEL(mi, tipcolor);
416 if(fs->node[1].z < 0.0) { /* Back */
418 fibercolor = fl->dim;
419 }else if(fs->node[NODES-1].z < 0.7) { /* Middle */
421 fibercolor = fl->medium;
424 fibercolor = fl->bright;
427 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), fibercolor);
428 XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
429 fs->draw, NODES-tiplen, CoordModeOrigin);
431 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), tipcolor);
432 XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
433 fs->draw+NODES-1-tiplen, tiplen, CoordModeOrigin);
437 /* 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);
445 if(fl->count++ > MI_CYCLES(mi)) {
446 change_fiberlamp(mi);
451 release_fiberlamp(ModeInfo * mi)
453 if (fiberlamps != NULL) {
456 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
457 free_fiberlamp(mi, &fiberlamps[screen]);
459 fiberlamps = (fiberlampstruct *) NULL;
464 refresh_fiberlamp(ModeInfo * mi)
469 XSCREENSAVER_MODULE ("Fiberlamp", fiberlamp)
471 #endif /* MODE_fiberlamp */