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" \
40 # define UNIFORM_COLORS
41 # define fiberlamp_handle_event 0
42 # include "xlockmore.h" /* in xscreensaver distribution */
43 #else /* STANDALONE */
44 # include "xlock.h" /* in xlockmore distribution */
45 #endif /* STANDALONE */
49 ENTRYPOINT ModeSpecOpt fiberlamp_opts =
50 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
53 ModStruct fiberlamp_description =
54 {"fiberlamp", "init_fiberlamp", "draw_fiberlamp", "release_fiberlamp",
55 "draw_fiberlamp", "change_fiberlamp", (char *) NULL, &fiberlamp_opts,
56 1000, 500, 10000, 0, 64, 1.0, "", "Shows a Fiber Optic Lamp", 0, NULL};
60 #define SPREAD (30.0) /* Angular spread at the base */
61 #define SCALE (MI_WIDTH(mi)/2) /* Screen size */
62 #define NODES (20) /* Number of nodes in a fiber. Variable with range
63 10 .. 30, if desired. High values have
64 stability problems unless you use small DT */
66 /* Physics parameters. Tune carefully to keep realism and avoid instability*/
67 #define DT (0.5) /* Time increment: Low is slow, High is less stable. */
68 #define PY (0.12) /* Rigidity: Low droops, High is stiff. */
69 #define DAMPING (0.055) /* Damping: Low allows oscillations, High is boring. */
71 #undef PLAN /* Plan view (for debugging) */
72 #undef CHECKCOLORWHEEL /* Plan view with no spread */
74 #define DRAND(v) (LRAND()/MAXRAND*(v)) /* double random 0 - v */
76 /* Length of nodes. Uniform except for shorter notes at the tips for
77 colour highlights. Sum from 0..NODES-1 should exactly 1.0 */
78 #define LEN(A) ((A<NODES-3) ? 1.0/(NODES-2.5) : 0.25/(NODES-2.5))
100 double rx, ry; /* Coordinates relative to root */
103 Pixmap buffer; /* Double Buffer */
104 long bright, medium, dim; /* "White" colors */
107 static fiberlampstruct *fiberlamps = (fiberlampstruct *) NULL;
110 change_fiberlamp(ModeInfo * mi)
113 if (fiberlamps == NULL)
115 fl = &fiberlamps[MI_SCREEN(mi)];
116 fl->cx = (DRAND(SCALE/4)-SCALE/8)/SCALE; /* Knock the lamp */
117 fl->count = 0; /* Reset counter */
119 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
120 XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), 0, 0,
121 MI_WIDTH(mi), MI_HEIGHT(mi));
126 free_fiber(fiberlampstruct *fl)
131 for (f = 0; f < fl->nfibers; f++) {
132 fiberstruct *fs = fl->fiber + f;
145 free_fiberlamp(ModeInfo *mi, fiberlampstruct *fl)
147 if (fl->buffer != None && fl->dbufp) {
148 XFreePixmap(MI_DISPLAY(mi), fl->buffer);
155 init_fiberlamp(ModeInfo * mi)
159 if (fiberlamps == NULL) {
161 (fiberlampstruct *) calloc(MI_NUM_SCREENS(mi),
162 sizeof (fiberlampstruct))) == NULL)
165 fl = &fiberlamps[MI_SCREEN(mi)];
167 /* Create or Resize double buffer */
168 #ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
174 if(fl->buffer != None && fl->buffer != MI_WINDOW(mi) && fl->dbufp)
175 XFreePixmap(MI_DISPLAY(mi), fl->buffer);
178 fl->buffer = XCreatePixmap(MI_DISPLAY(mi), MI_WINDOW(mi),
179 MI_WIDTH(mi), MI_HEIGHT(mi), MI_DEPTH(mi));
180 if (fl->buffer == None) {
181 free_fiberlamp(mi, fl);
185 fl->buffer = MI_WINDOW(mi);
188 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
189 XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), 0, 0,
190 MI_WIDTH(mi), MI_HEIGHT(mi));
192 if(fl->init) /* Nothing else to do (probably a resize) */
196 fl->nfibers = MI_COUNT(mi);
197 /* Allocate fibers */
199 (fiberstruct*) calloc(fl->nfibers, sizeof (fiberstruct))) == NULL) {
200 free_fiberlamp(mi, fl);
204 for(f = 0; f < fl->nfibers; f++) {
205 fiberstruct *fs = fl->fiber + f;
207 (nodestruct*) calloc(NODES, sizeof (nodestruct))) == NULL
209 (XPoint*) calloc(NODES, sizeof (XPoint))) == NULL) {
210 free_fiberlamp(mi, fl);
218 for(f = 0; f < fl->nfibers; f++) {
219 double phi = M_PI/180 * DRAND(SPREAD);
220 double eta = DRAND(2*M_PI) - M_PI;
221 for(i = 0; i < NODES; i++) {
222 nodestruct *n = &fl->fiber[f].node[i];
228 fl->fiber[f].node[0].etadash = 0.002/DT;
229 fl->fiber[f].node[0].y = 0;
230 fl->fiber[f].node[0].z = 0;
235 /* Set up rotation */
236 fl->psi = DRAND(2*M_PI);
239 /* no "NoExpose" events from XCopyArea wanted */
240 XSetGraphicsExposures(MI_DISPLAY(mi), MI_GC(mi), False);
242 /* Make sure we're using 'thin' lines */
243 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 0, LineSolid, CapNotLast,
245 #ifdef CHECKCOLORWHEEL
246 /* Only interested in tips, leave the rest black */
247 fl->bright = fl->medium = fl->dim = MI_BLACK_PIXEL(mi);
249 if(MI_NPIXELS(mi) > 2) {
250 /* Set up colours for the fiber bodies. Tips handled seperately */
252 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#E0E0C0", &c, &t)){
253 fl->bright = c.pixel;
255 fl->bright = MI_WHITE_PIXEL(mi);
257 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#808070", &c, &t)){
258 fl->medium = c.pixel;
260 fl->medium = MI_WHITE_PIXEL(mi);
262 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#404020", &c, &t)){
265 fl->dim = MI_BLACK_PIXEL(mi);
268 fl->bright = MI_WHITE_PIXEL(mi);
269 fl->medium = MI_WHITE_PIXEL(mi);
270 fl->dim = MI_BLACK_PIXEL(mi);
274 /* Clear the background. */
276 change_fiberlamp(mi);
279 /* Used by xscreensaver. xlock just uses init_fiberlamp */
281 reshape_fiberlamp(ModeInfo * mi, int width, int height)
286 /* sort fibers so they get drawn back-to-front, one bubble pass is
287 enough as the order only changes slowly */
289 sort_fibers(fiberlampstruct *fl)
293 for(i = 1; i < fl->nfibers; i++) {
294 if (fl->fiber[i - 1].node[NODES - 1].z > fl->fiber[i].node[NODES - 1].z) {
295 fiberstruct tmp = fl->fiber[i - 1];
296 fl->fiber[i - 1] = fl->fiber[i];
303 draw_fiberlamp (ModeInfo * mi)
310 short cx = MI_WIDTH(mi)/2;
311 #if defined PLAN || defined CHECKCOLORWHEEL
312 short cy = MI_HEIGHT(mi)/2;
314 short cy = MI_HEIGHT(mi);
317 if (fiberlamps == NULL)
319 fl = &fiberlamps[MI_SCREEN(mi)];
321 fl->psi += fl->dpsi; /* turn colorwheel */
323 XTranslateCoordinates(MI_DISPLAY(mi), MI_WINDOW(mi),
324 RootWindow(MI_DISPLAY(mi),0/*#### MI_SCREEN(mi)*/),
325 cx, cy, &x, &y, &unused);
328 for(f = 0; f < fl->nfibers; f++) {
329 fiberstruct *fs = fl->fiber + f;
331 fs->node[0].eta += DT*fs->node[0].etadash;
332 fs->node[0].x = fl->cx; /* Handle center movement */
333 /* Handle window move. NOTE, only x is deflected, since y doesn't
334 directly affect the physics */
335 fs->node[NODES-2].x *= 0.1*(fl->ry - y);
336 fs->node[NODES-2].x += 0.05*(fl->rx - x);
338 /* 2nd order diff equation */
339 for(i = 1; i < NODES; i++) {
340 nodestruct *n = fs->node+i;
341 nodestruct *p = fs->node+i-1;
344 double pstress = (n->phi - p->phi)*PY;
345 double estress = (n->eta - p->eta)*PY;
346 double dxi = n->x - p->x;
347 double dzi = n->z - p->z;
348 double li = sqrt(dxi*dxi + dzi*dzi)/LEN(i);
349 double drag = DAMPING*LEN(i)*LEN(i)*NODES*NODES;
353 for(j = i+1; j < NODES; j++) {
354 nodestruct *nn = fs->node+j;
355 double dxj = nn->x - n->x;
356 double dzj = nn->z - n->z;
358 pload += LEN(j)*(dxi*dxj + dzi*dzj)/li; /* Radial load */
359 eload += LEN(j)*(dxi*dzj - dzi*dxj)/li; /* Transverse load */
360 /* Not a perfect simulation: in reality the transverse load
361 is only indirectly coupled to the eta deflection, but of
362 all the approaches I've tried this produces the most
363 stable model and looks the most realistic. */
367 #ifndef CHECKCOLORWHEEL
368 n->phidash += DT*(pload - pstress - drag*n->phidash)/LEN(i);
369 n->phi += DT*n->phidash;
372 n->etadash += DT*(eload - estress - drag*n->etadash)/LEN(i);
373 n->eta += DT*n->etadash;
376 double sp = sin(p->phi);
377 double cp = cos(p->phi);
378 double se = sin(p->eta);
379 double ce = cos(p->eta);
381 n->x = p->x + LEN(i-1) * ce * sp;
382 n->y = p->y - LEN(i-1) * cp;
383 n->z = p->z + LEN(i-1) * se * sp;
386 fs->draw[i-1].x = cx + MI_WIDTH(mi)/2*n->x;
387 #if defined PLAN || defined CHECKCOLORWHEEL /* Plan */
388 fs->draw[i-1].y = cy + MI_WIDTH(mi)/2*n->z;
389 #else /* Elevation */
390 fs->draw[i-1].y = cy + MI_WIDTH(mi)/2*n->y;
393 MI_IS_DRAWN(mi) = True;
395 /* Erase: this may only be erasing an off-screen buffer, but on a
396 slow system it may still be faster than XFillRectangle() */
397 /* That's unpossible. -jwz */
400 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
401 XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
402 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi));
404 for(f = 0; f < fl->nfibers; f++) {
405 fiberstruct *fs = fl->fiber + f;
408 double x = fs->node[1].x - fl->cx + 0.025;
409 double y = fs->node[1].z + 0.02;
410 double angle = atan2(y, x) + fl->psi;
411 int tipcolor = (int)(MI_NPIXELS(mi)*angle/(2*M_PI)) % MI_NPIXELS(mi);
415 if (tipcolor < 0) tipcolor += MI_NPIXELS(mi);
416 tipcolor = MI_PIXEL(mi, tipcolor);
418 if(fs->node[1].z < 0.0) { /* Back */
420 fibercolor = fl->dim;
421 }else if(fs->node[NODES-1].z < 0.7) { /* Middle */
423 fibercolor = fl->medium;
426 fibercolor = fl->bright;
429 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), fibercolor);
430 XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
431 fs->draw, NODES-tiplen, CoordModeOrigin);
433 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), tipcolor);
434 XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
435 fs->draw+NODES-1-tiplen, tiplen, CoordModeOrigin);
439 /* Update the screen from the double-buffer */
441 XCopyArea(MI_DISPLAY(mi), fl->buffer, MI_WINDOW(mi), MI_GC(mi), 0, 0,
442 MI_WIDTH(mi), MI_HEIGHT(mi), 0, 0);
447 if(fl->count++ > MI_CYCLES(mi)) {
448 change_fiberlamp(mi);
453 release_fiberlamp(ModeInfo * mi)
455 if (fiberlamps != NULL) {
458 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
459 free_fiberlamp(mi, &fiberlamps[screen]);
461 fiberlamps = (fiberlampstruct *) NULL;
466 refresh_fiberlamp(ModeInfo * mi)
471 XSCREENSAVER_MODULE ("Fiberlamp", fiberlamp)
473 #endif /* MODE_fiberlamp */