1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* fiberlamp --- A Fiber Optic Lamp */
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 release_fiberlamp 0
42 # define fiberlamp_handle_event 0
43 # include "xlockmore.h" /* in xscreensaver distribution */
44 #else /* STANDALONE */
45 # include "xlock.h" /* in xlockmore distribution */
46 #endif /* STANDALONE */
50 ENTRYPOINT ModeSpecOpt fiberlamp_opts =
51 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
54 ModStruct fiberlamp_description =
55 {"fiberlamp", "init_fiberlamp", "draw_fiberlamp", (char *) NULL,
56 "draw_fiberlamp", "change_fiberlamp", (char *) NULL, &fiberlamp_opts,
57 1000, 500, 10000, 0, 64, 1.0, "", "Shows a Fiber Optic Lamp", 0, NULL};
61 #define SPREAD (30.0) /* Angular spread at the base */
62 #define SCALE (MI_WIDTH(mi)/2) /* Screen size */
63 #define NODES (20L) /* Number of nodes in a fiber. Variable with range
64 10 .. 30, if desired. High values have
65 stability problems unless you use small DT */
67 /* Physics parameters. Tune carefully to keep realism and avoid instability*/
68 #define DT (0.5) /* Time increment: Low is slow, High is less stable. */
69 #define PY (0.12) /* Rigidity: Low droops, High is stiff. */
70 #define DAMPING (0.055) /* Damping: Low allows oscillations, High is boring. */
72 #undef PLAN /* Plan view (for debugging) */
73 #undef CHECKCOLORWHEEL /* Plan view with no spread */
75 #define DRAND(v) (LRAND()/MAXRAND*(v)) /* double random 0 - v */
77 /* Length of nodes. Uniform except for shorter notes at the tips for
78 colour highlights. Sum from 0..NODES-1 should exactly 1.0 */
79 #define LEN(A) ((A<NODES-3) ? 1.0/(NODES-2.5) : 0.25/(NODES-2.5))
101 double rx, ry; /* Coordinates relative to root */
104 Pixmap buffer; /* Double Buffer */
105 long bright, medium, dim; /* "White" colors */
108 static fiberlampstruct *fiberlamps = (fiberlampstruct *) NULL;
111 change_fiberlamp(ModeInfo * mi)
114 if (fiberlamps == NULL)
116 fl = &fiberlamps[MI_SCREEN(mi)];
117 fl->cx = (DRAND(SCALE/4)-SCALE/8)/SCALE; /* Knock the lamp */
118 fl->count = 0; /* Reset counter */
120 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
121 XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), 0, 0,
122 MI_WIDTH(mi), MI_HEIGHT(mi));
127 free_fiber(fiberlampstruct *fl)
132 for (f = 0; f < fl->nfibers; f++) {
133 fiberstruct *fs = fl->fiber + f;
146 free_fiberlamp(ModeInfo *mi)
148 fiberlampstruct *fl = &fiberlamps[MI_SCREEN(mi)];
149 if (fl->buffer != None && fl->dbufp) {
150 XFreePixmap(MI_DISPLAY(mi), fl->buffer);
157 init_fiberlamp(ModeInfo * mi)
161 MI_INIT (mi, fiberlamps, free_fiberlamp);
162 fl = &fiberlamps[MI_SCREEN(mi)];
164 /* Create or Resize double buffer */
165 #ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */
171 if(fl->buffer != None && fl->buffer != MI_WINDOW(mi) && fl->dbufp)
172 XFreePixmap(MI_DISPLAY(mi), fl->buffer);
175 fl->buffer = XCreatePixmap(MI_DISPLAY(mi), MI_WINDOW(mi),
176 MI_WIDTH(mi), MI_HEIGHT(mi), MI_DEPTH(mi));
177 if (fl->buffer == None) {
182 fl->buffer = MI_WINDOW(mi);
185 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
186 XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), 0, 0,
187 MI_WIDTH(mi), MI_HEIGHT(mi));
189 if(fl->init) /* Nothing else to do (probably a resize) */
193 fl->nfibers = MI_COUNT(mi);
194 /* Allocate fibers */
196 (fiberstruct*) calloc(fl->nfibers, sizeof (fiberstruct))) == NULL) {
201 for(f = 0; f < fl->nfibers; f++) {
202 fiberstruct *fs = fl->fiber + f;
204 (nodestruct*) calloc(NODES, sizeof (nodestruct))) == NULL
206 (XPoint*) calloc(NODES, sizeof (XPoint))) == NULL) {
215 for(f = 0; f < fl->nfibers; f++) {
216 double phi = M_PI/180 * DRAND(SPREAD);
217 double eta = DRAND(2*M_PI) - M_PI;
218 for(i = 0; i < NODES; i++) {
219 nodestruct *n = &fl->fiber[f].node[i];
225 fl->fiber[f].node[0].etadash = 0.002/DT;
226 fl->fiber[f].node[0].y = 0;
227 fl->fiber[f].node[0].z = 0;
232 /* Set up rotation */
233 fl->psi = DRAND(2*M_PI);
236 /* no "NoExpose" events from XCopyArea wanted */
237 XSetGraphicsExposures(MI_DISPLAY(mi), MI_GC(mi), False);
239 /* Make sure we're using 'thin' lines */
240 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 0, LineSolid, CapNotLast,
242 #ifdef CHECKCOLORWHEEL
243 /* Only interested in tips, leave the rest black */
244 fl->bright = fl->medium = fl->dim = MI_BLACK_PIXEL(mi);
246 if(MI_NPIXELS(mi) > 2) {
247 /* Set up colours for the fiber bodies. Tips handled seperately */
249 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#E0E0C0", &c, &t)){
250 fl->bright = c.pixel;
252 fl->bright = MI_WHITE_PIXEL(mi);
254 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#808070", &c, &t)){
255 fl->medium = c.pixel;
257 fl->medium = MI_WHITE_PIXEL(mi);
259 if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#404020", &c, &t)){
262 fl->dim = MI_BLACK_PIXEL(mi);
265 fl->bright = MI_WHITE_PIXEL(mi);
266 fl->medium = MI_WHITE_PIXEL(mi);
267 fl->dim = MI_BLACK_PIXEL(mi);
271 /* Clear the background. */
273 change_fiberlamp(mi);
276 /* Used by xscreensaver. xlock just uses init_fiberlamp */
278 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),0/*#### 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 /* That's unpossible. -jwz */
397 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
398 XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
399 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi));
401 for(f = 0; f < fl->nfibers; f++) {
402 fiberstruct *fs = fl->fiber + f;
405 double x = fs->node[1].x - fl->cx + 0.025;
406 double y = fs->node[1].z + 0.02;
407 double angle = atan2(y, x) + fl->psi;
408 long tipcolor = (int)(MI_NPIXELS(mi)*angle/(2*M_PI)) % MI_NPIXELS(mi);
412 if (tipcolor < 0) tipcolor += MI_NPIXELS(mi);
413 tipcolor = MI_PIXEL(mi, tipcolor);
415 if(fs->node[1].z < 0.0) { /* Back */
417 fibercolor = fl->dim;
418 }else if(fs->node[NODES-1].z < 0.7) { /* Middle */
420 fibercolor = fl->medium;
423 fibercolor = fl->bright;
426 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), fibercolor);
427 XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
428 fs->draw, NODES-tiplen, CoordModeOrigin);
430 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), tipcolor);
431 XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi),
432 fs->draw+NODES-1-tiplen, tiplen, CoordModeOrigin);
436 /* Update the screen from the double-buffer */
438 XCopyArea(MI_DISPLAY(mi), fl->buffer, MI_WINDOW(mi), MI_GC(mi), 0, 0,
439 MI_WIDTH(mi), MI_HEIGHT(mi), 0, 0);
444 if(fl->count++ > MI_CYCLES(mi)) {
445 change_fiberlamp(mi);
450 refresh_fiberlamp(ModeInfo * mi)
455 XSCREENSAVER_MODULE ("Fiberlamp", fiberlamp)
457 #endif /* MODE_fiberlamp */