/* -*- Mode: C; tab-width: 4 -*- */ /* fiberlamp --- A Fiber Optic Lamp */ #if 0 static const char sccsid[] = "@(#)fiberlamp.c 5.00 2000/11/01 xlockmore"; #endif /*- * Copyright (c) 2005 by Tim Auckland * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * "fiberlamp" shows Fiber Optic Lamp. Since there is no closed-form * solution to the large-amplitude cantilever equation, the flexible * fiber is modeled as a set of descrete nodes. * * Revision History: * 13-Jan-2005: Initial development. */ #ifdef STANDALONE #define MODE_fiberlamp #define DEFAULTS "*delay: 10000 \n" \ "*count: 500 \n" \ "*cycles: 10000 \n" \ "*ncolors: 64 \n" \ "*fpsTop: true \n" \ # define UNIFORM_COLORS # define release_fiberlamp 0 # define reshape_fiberlamp 0 # define fiberlamp_handle_event 0 # include "xlockmore.h" /* in xscreensaver distribution */ #else /* STANDALONE */ # include "xlock.h" /* in xlockmore distribution */ #endif /* STANDALONE */ #ifdef MODE_fiberlamp ENTRYPOINT ModeSpecOpt fiberlamp_opts = {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL}; #ifdef USE_MODULES ModStruct fiberlamp_description = {"fiberlamp", "init_fiberlamp", "draw_fiberlamp", (char *) NULL, "draw_fiberlamp", "change_fiberlamp", "free_fiberlamp", &fiberlamp_opts, 1000, 500, 10000, 0, 64, 1.0, "", "Shows a Fiber Optic Lamp", 0, NULL}; #endif #define SPREAD (30.0) /* Angular spread at the base */ #define SCALE (MI_WIDTH(mi)/2) /* Screen size */ #define NODES (20L) /* Number of nodes in a fiber. Variable with range 10 .. 30, if desired. High values have stability problems unless you use small DT */ /* Physics parameters. Tune carefully to keep realism and avoid instability*/ #define DT (0.5) /* Time increment: Low is slow, High is less stable. */ #define PY (0.12) /* Rigidity: Low droops, High is stiff. */ #define DAMPING (0.055) /* Damping: Low allows oscillations, High is boring. */ #undef PLAN /* Plan view (for debugging) */ #undef CHECKCOLORWHEEL /* Plan view with no spread */ #define DRAND(v) (LRAND()/MAXRAND*(v)) /* double random 0 - v */ /* Length of nodes. Uniform except for shorter notes at the tips for colour highlights. Sum from 0..NODES-1 should exactly 1.0 */ #define LEN(A) ((Acx = (DRAND(SCALE/4)-SCALE/8)/SCALE; /* Knock the lamp */ fl->count = 0; /* Reset counter */ if (fl->dbufp) { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi)); } } static void free_fiber(fiberlampstruct *fl) { if (fl->fiber) { int f; for (f = 0; f < fl->nfibers; f++) { fiberstruct *fs = fl->fiber + f; if (fs->node) free(fs->node); if (fs->draw) free(fs->draw); } free(fl->fiber); fl->fiber = NULL; } } ENTRYPOINT void free_fiberlamp(ModeInfo *mi) { fiberlampstruct *fl = &fiberlamps[MI_SCREEN(mi)]; if (fl->buffer != None && fl->dbufp) { XFreePixmap(MI_DISPLAY(mi), fl->buffer); fl->buffer = None; } free_fiber(fl); } ENTRYPOINT void init_fiberlamp(ModeInfo * mi) { fiberlampstruct *fl; MI_INIT (mi, fiberlamps); fl = &fiberlamps[MI_SCREEN(mi)]; /* Create or Resize double buffer */ #ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */ fl->dbufp = False; #else fl->dbufp = True; #endif if(fl->buffer != None && fl->buffer != MI_WINDOW(mi) && fl->dbufp) XFreePixmap(MI_DISPLAY(mi), fl->buffer); if(fl->dbufp) { fl->buffer = XCreatePixmap(MI_DISPLAY(mi), MI_WINDOW(mi), MI_WIDTH(mi), MI_HEIGHT(mi), MI_DEPTH(mi)); if (fl->buffer == None) { free_fiberlamp(mi); return; } } else { fl->buffer = MI_WINDOW(mi); } XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi)); if(fl->init) /* Nothing else to do (probably a resize) */ return; fl->init = True; fl->nfibers = MI_COUNT(mi); /* Allocate fibers */ if((fl->fiber = (fiberstruct*) calloc(fl->nfibers, sizeof (fiberstruct))) == NULL) { free_fiberlamp(mi); return; } else { int f; for(f = 0; f < fl->nfibers; f++) { fiberstruct *fs = fl->fiber + f; if((fs->node = (nodestruct*) calloc(NODES, sizeof (nodestruct))) == NULL ||(fs->draw = (XPoint*) calloc(NODES, sizeof (XPoint))) == NULL) { free_fiberlamp(mi); return; } } } { int f, i; for(f = 0; f < fl->nfibers; f++) { double phi = M_PI/180 * DRAND(SPREAD); double eta = DRAND(2*M_PI) - M_PI; for(i = 0; i < NODES; i++) { nodestruct *n = &fl->fiber[f].node[i]; n->phi = phi; n->phidash = 0; n->eta = eta; n->etadash = 0; } fl->fiber[f].node[0].etadash = 0.002/DT; fl->fiber[f].node[0].y = 0; fl->fiber[f].node[0].z = 0; } } /* Set up rotation */ fl->psi = DRAND(2*M_PI); fl->dpsi = 0.01; /* no "NoExpose" events from XCopyArea wanted */ XSetGraphicsExposures(MI_DISPLAY(mi), MI_GC(mi), False); /* Make sure we're using 'thin' lines */ XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 0, LineSolid, CapNotLast, JoinMiter); #ifdef CHECKCOLORWHEEL /* Only interested in tips, leave the rest black */ fl->bright = fl->medium = fl->dim = MI_BLACK_PIXEL(mi); #else if(MI_NPIXELS(mi) > 2) { /* Set up colours for the fiber bodies. Tips handled seperately */ XColor c, t; if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#E0E0C0", &c, &t)){ fl->bright = c.pixel; } else { fl->bright = MI_WHITE_PIXEL(mi); } if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#808070", &c, &t)){ fl->medium = c.pixel; } else { fl->medium = MI_WHITE_PIXEL(mi); } if(XAllocNamedColor(MI_DISPLAY(mi), MI_COLORMAP(mi), "#404020", &c, &t)){ fl->dim = c.pixel; } else { fl->dim = MI_BLACK_PIXEL(mi); } } else { fl->bright = MI_WHITE_PIXEL(mi); fl->medium = MI_WHITE_PIXEL(mi); fl->dim = MI_BLACK_PIXEL(mi); } #endif /* Clear the background. */ MI_CLEARWINDOW(mi); change_fiberlamp(mi); } /* sort fibers so they get drawn back-to-front, one bubble pass is enough as the order only changes slowly */ static void sort_fibers(fiberlampstruct *fl) { int i; for(i = 1; i < fl->nfibers; i++) { if (fl->fiber[i - 1].node[NODES - 1].z > fl->fiber[i].node[NODES - 1].z) { fiberstruct tmp = fl->fiber[i - 1]; fl->fiber[i - 1] = fl->fiber[i]; fl->fiber[i] = tmp; } } } ENTRYPOINT void draw_fiberlamp (ModeInfo * mi) { fiberlampstruct *fl; int f, i; int x, y; int ww, hh; Window unused; short cx, cy; ww = MI_WIDTH(mi); hh = MI_HEIGHT(mi); cx = MI_WIDTH(mi)/2; #if defined PLAN || defined CHECKCOLORWHEEL cy = MI_HEIGHT(mi)/2; #else cy = MI_HEIGHT(mi); #endif if (ww > hh * 5 || /* window has weird aspect */ hh > ww * 5) { if (ww > hh) { hh = ww; cy = hh / 4; } else { ww = hh; cx = 0; cy = hh*3/4; } } if (fiberlamps == NULL) return; fl = &fiberlamps[MI_SCREEN(mi)]; fl->psi += fl->dpsi; /* turn colorwheel */ XTranslateCoordinates(MI_DISPLAY(mi), MI_WINDOW(mi), RootWindow(MI_DISPLAY(mi),0/*#### MI_SCREEN(mi)*/), cx, cy, &x, &y, &unused); sort_fibers(fl); for(f = 0; f < fl->nfibers; f++) { fiberstruct *fs = fl->fiber + f; fs->node[0].eta += DT*fs->node[0].etadash; fs->node[0].x = fl->cx; /* Handle center movement */ /* Handle window move. NOTE, only x is deflected, since y doesn't directly affect the physics */ fs->node[NODES-2].x *= 0.1*(fl->ry - y); fs->node[NODES-2].x += 0.05*(fl->rx - x); /* 2nd order diff equation */ for(i = 1; i < NODES; i++) { nodestruct *n = fs->node+i; nodestruct *p = fs->node+i-1; double pload = 0; double eload = 0; double pstress = (n->phi - p->phi)*PY; double estress = (n->eta - p->eta)*PY; double dxi = n->x - p->x; double dzi = n->z - p->z; double li = sqrt(dxi*dxi + dzi*dzi)/LEN(i); double drag = DAMPING*LEN(i)*LEN(i)*NODES*NODES; if(li > 0) { int j; for(j = i+1; j < NODES; j++) { nodestruct *nn = fs->node+j; double dxj = nn->x - n->x; double dzj = nn->z - n->z; pload += LEN(j)*(dxi*dxj + dzi*dzj)/li; /* Radial load */ eload += LEN(j)*(dxi*dzj - dzi*dxj)/li; /* Transverse load */ /* Not a perfect simulation: in reality the transverse load is only indirectly coupled to the eta deflection, but of all the approaches I've tried this produces the most stable model and looks the most realistic. */ } } #ifndef CHECKCOLORWHEEL n->phidash += DT*(pload - pstress - drag*n->phidash)/LEN(i); n->phi += DT*n->phidash; #endif n->etadash += DT*(eload - estress - drag*n->etadash)/LEN(i); n->eta += DT*n->etadash; { double sp = sin(p->phi); double cp = cos(p->phi); double se = sin(p->eta); double ce = cos(p->eta); n->x = p->x + LEN(i-1) * ce * sp; n->y = p->y - LEN(i-1) * cp; n->z = p->z + LEN(i-1) * se * sp; } fs->draw[i-1].x = cx + ww/2*n->x; #if defined PLAN || defined CHECKCOLORWHEEL /* Plan */ fs->draw[i-1].y = cy + ww/2*n->z; #else /* Elevation */ fs->draw[i-1].y = cy + ww/2*n->y; #endif } MI_IS_DRAWN(mi) = True; /* Erase: this may only be erasing an off-screen buffer, but on a slow system it may still be faster than XFillRectangle() */ /* That's unpossible. -jwz */ } XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); XFillRectangle(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi)); for(f = 0; f < fl->nfibers; f++) { fiberstruct *fs = fl->fiber + f; { double x = fs->node[1].x - fl->cx + 0.025; double y = fs->node[1].z + 0.02; double angle = atan2(y, x) + fl->psi; long tipcolor = (int)(MI_NPIXELS(mi)*angle/(2*M_PI)) % MI_NPIXELS(mi); long fibercolor; long tiplen; if (tipcolor < 0) tipcolor += MI_NPIXELS(mi); tipcolor = MI_PIXEL(mi, tipcolor); if(fs->node[1].z < 0.0) { /* Back */ tiplen = 2; fibercolor = fl->dim; }else if(fs->node[NODES-1].z < 0.7) { /* Middle */ tiplen = 3; fibercolor = fl->medium; } else { /* Front */ tiplen = 3; fibercolor = fl->bright; } XSetForeground(MI_DISPLAY(mi), MI_GC(mi), fibercolor); XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), fs->draw, NODES-tiplen, CoordModeOrigin); XSetForeground(MI_DISPLAY(mi), MI_GC(mi), tipcolor); XDrawLines(MI_DISPLAY(mi), fl->buffer, MI_GC(mi), fs->draw+NODES-1-tiplen, tiplen, CoordModeOrigin); } } /* Update the screen from the double-buffer */ if (fl->dbufp) XCopyArea(MI_DISPLAY(mi), fl->buffer, MI_WINDOW(mi), MI_GC(mi), 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi), 0, 0); fl->rx = x; fl->ry = y; if(fl->count++ > MI_CYCLES(mi)) { change_fiberlamp(mi); } } #ifndef STANDALONE ENTRYPOINT void refresh_fiberlamp(ModeInfo * mi) { MI_CLEARWINDOW(mi); } #endif XSCREENSAVER_MODULE ("Fiberlamp", fiberlamp) #endif /* MODE_fiberlamp */