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 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" \
44 #define UNIFORM_COLORS
45 #include "xlockmore.h" /* in xscreensaver distribution */
47 # define MI_DEPTH MI_WIN_DEPTH
49 #else /* STANDALONE */
50 #include "xlock.h" /* in xlockmore distribution */
51 #endif /* STANDALONE */
55 ModeSpecOpt fiberlamp_opts =
56 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
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};
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 */
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. */
77 #undef PLAN /* Plan view (for debugging) */
78 #undef CHECKCOLORWHEEL /* Plan view with no spread */
80 #define DRAND(v) (LRAND()/MAXRAND*(v)) /* double random 0 - v */
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))
105 double rx, ry; /* Coordinates relative to root */
107 Pixmap buffer; /* Double Buffer */
108 long bright, medium, dim; /* "White" colors */
111 static fiberlampstruct *fiberlamps = (fiberlampstruct *) NULL;
114 change_fiberlamp(ModeInfo * mi)
117 if (fiberlamps == NULL)
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));
128 free_fiber(fiberlampstruct *fl)
133 for (f = 0; f < fl->nfibers; f++) {
134 fiberstruct *fs = fl->fiber + f;
149 free_fiberlamp(Display *display, fiberlampstruct *fl)
151 if (fl->buffer != None) {
152 XFreePixmap(display, fl->buffer);
159 init_fiberlamp(ModeInfo * mi)
164 if (fiberlamps == NULL) {
167 (fiberlampstruct *) calloc(MI_NUM_SCREENS(mi),
168 sizeof (fiberlampstruct))) == NULL)
171 fl = &fiberlamps[MI_SCREEN(mi)];
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);
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));
186 if(!init) /* Nothing else to do (probably a resize) */
189 fl->nfibers = MI_COUNT(mi);
190 /* Allocate fibers */
192 (fiberstruct*) calloc(fl->nfibers, sizeof (fiberstruct))) == NULL) {
193 free_fiberlamp(MI_DISPLAY(mi), fl);
197 for(f = 0; f < fl->nfibers; f++) {
198 fiberstruct *fs = fl->fiber + f;
200 (nodestruct*) calloc(NODES, sizeof (nodestruct))) == NULL
202 (XPoint*) calloc(NODES, sizeof (XPoint))) == NULL
204 (XPoint*) calloc(NODES, sizeof (XPoint))) == NULL) {
205 free_fiberlamp(MI_DISPLAY(mi), fl);
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];
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;
230 /* Set up rotation */
231 fl->psi = DRAND(2*M_PI);
234 /* no "NoExpose" events from XCopyArea wanted */
235 XSetGraphicsExposures(MI_DISPLAY(mi), MI_GC(mi), False);
237 /* Make sure we're using 'thin' lines */
238 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 0, LineSolid, CapNotLast,
240 #ifdef CHECKCOLORWHEEL
241 /* Only interested in tips, leave the rest black */
242 fl->bright = fl->medium = fl->dim = MI_BLACK_PIXEL(mi);
244 if(MI_NPIXELS(mi) > 2) {
245 /* Set up colours for the fiber bodies. Tips handled seperately */
247 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#E0E0C0", &c, &t)){
248 fl->bright = c.pixel;
250 fl->bright = MI_WHITE_PIXEL(mi);
252 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#808070", &c, &t)){
253 fl->medium = c.pixel;
255 fl->medium = MI_WHITE_PIXEL(mi);
257 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#404020", &c, &t)){
260 fl->dim = MI_BLACK_PIXEL(mi);
263 fl->bright = MI_WHITE_PIXEL(mi);
264 fl->medium = MI_WHITE_PIXEL(mi);
265 fl->dim = MI_BLACK_PIXEL(mi);
269 /* Clear the background. */
271 change_fiberlamp(mi);
275 /* Used by xscreensaver. xlock just uses init_fiberlamp */
277 reshape_fiberlamp(ModeInfo * mi, int width, int height)
283 /* sort fibers so they get drawn back-to-front, one bubble pass is
284 enough as the order only changes slowly */
286 sort_fibers(fiberlampstruct *fl)
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];
300 draw_fiberlamp(ModeInfo * mi)
307 short cx = MI_WIDTH(mi)/2;
308 #if defined PLAN || defined CHECKCOLORWHEEL
309 short cy = MI_HEIGHT(mi)/2;
311 short cy = MI_HEIGHT(mi);
314 if (fiberlamps == NULL)
316 fl = &fiberlamps[MI_SCREEN(mi)];
318 fl->psi += fl->dpsi; /* turn colorwheel */
320 XTranslateCoordinates(MI_DISPLAY(mi), MI_WINDOW(mi),
321 RootWindow(MI_DISPLAY(mi),MI_SCREEN(mi)),
322 cx, cy, &x, &y, &unused);
325 for(f = 0; f < fl->nfibers; f++) {
326 fiberstruct *fs = fl->fiber + f;
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);
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;
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;
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;
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. */
364 #ifndef CHECKCOLORWHEEL
365 n->phidash += DT*(pload - pstress - drag*n->phidash)/LEN(i);
366 n->phi += DT*n->phidash;
369 n->etadash += DT*(eload - estress - drag*n->etadash)/LEN(i);
370 n->eta += DT*n->etadash;
373 double sp = sin(p->phi);
374 double cp = cos(p->phi);
375 double se = sin(p->eta);
376 double ce = cos(p->eta);
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;
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;
390 MI_IS_DRAWN(mi) = True;
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);
399 for(f = 0; f < fl->nfibers; f++) {
400 fiberstruct *fs = fl->fiber + f;
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));
411 if(fs->node[1].z < 0.0) { /* Back */
413 fibercolor = fl->dim;
414 }else if(fs->node[NODES-1].z < 0.7) { /* Middle */
416 fibercolor = fl->medium;
419 fibercolor = fl->bright;
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);
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);
431 { /* Switch buffers */
432 XPoint *buffer = fs->draw;
433 fs->draw = fs->erase;
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);
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_DISPLAY(mi), &fiberlamps[screen]);
459 fiberlamps = (fiberlampstruct *) NULL;
465 refresh_fiberlamp(ModeInfo * mi)
470 #endif /* MODE_fiberlamp */