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