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