683c21baa610b7aae8573b3af77bb9d9bddd669d
[xscreensaver] / hacks / xlyap.c
1 /* Lyap - calculate and display Lyapunov exponents */
2
3 /* Written by Ron Record (rr@sco) 03 Sep 1991 */
4
5 /* The idea here is to calculate the Lyapunov exponent for a periodically
6  * forced logistic map (later i added several other nonlinear maps of the unit
7  * interval). In order to turn the 1-dimensional parameter space of the
8  * logistic map into a 2-dimensional parameter space, select two parameter
9  * values ('a' and 'b') then alternate the iterations of the logistic map using
10  * first 'a' then 'b' as the parameter. This program accepts an argument to
11  * specify a forcing function, so instead of just alternating 'a' and 'b', you
12  * can use 'a' as the parameter for say 6 iterations, then 'b' for 6 iterations
13  * and so on. An interesting forcing function to look at is abbabaab (the
14  * Morse-Thue sequence, an aperiodic self-similar, self-generating sequence).
15  * Anyway, step through all the values of 'a' and 'b' in the ranges you want,
16  * calculating the Lyapunov exponent for each pair of values. The exponent
17  * is calculated by iterating out a ways (specified by the variable "settle")
18  * then on subsequent iterations calculating an average of the logarithm of
19  * the absolute value of the derivative at that point. Points in parameter
20  * space with a negative Lyapunov exponent are colored one way (using the
21  * value of the exponent to index into a color map) while points with a
22  * non-negative exponent are colored differently.
23  *
24  * The algorithm was taken from the September 1991 Scientific American article
25  * by A. K. Dewdney who gives credit to Mario Markus of the Max Planck Institute
26  * for its creation. Additional information and ideas were gleaned from the
27  * discussion on alt.fractals involving Stephen Hall, Ed Kubaitis, Dave Platt
28  * and Baback Moghaddam. Assistance with colormaps and spinning color wheels
29  * and X was gleaned from Hiram Clawson. Rubber banding code was adapted from
30  * an existing Mandelbrot program written by Stacey Campbell.
31  */
32
33 #define LYAP_PATCHLEVEL 4
34 #define LYAP_VERSION "#(@) lyap 2.3 2/20/92"
35
36 #include <assert.h>
37 #include <math.h>
38
39 #include "screenhack.h"
40 #include "yarandom.h"
41 #include "hsv.h"
42
43 #include <X11/cursorfont.h> 
44 #include <X11/Xutil.h> 
45
46 char *progclass = "XLyap";
47
48 char *defaults [] = {
49   ".background:         black",
50   ".foreground:         white",
51   "*randomize:          false",
52   "*builtin:            -1",
53   "*minColor:           1",
54   "*maxColor:           256",
55   "*dwell:              50",
56   "*useLog:             false",
57   "*colorExponent:      1.0",
58   "*colorOffset:        0",
59   "*randomForce:        ",              /* 0.5 */
60   "*settle:             50",
61   "*minA:               2.0",
62   "*minB:               2.0",
63   "*wheels:             7",
64   "*function:           10101010",
65   "*forcingFunction:    abbabaab",
66   "*bRange:             ",              /* 2.0 */
67   "*startX:             0.65",
68   "*mapIndex:           ",              /* 0 */
69   "*outputFile:         ",
70   "*beNegative:         false",
71   "*rgbMax:             65000",
72   "*spinLength:         256",
73   "*show:               false",
74   "*aRange:             ",              /* 2.0 */
75   0
76 };
77
78 XrmOptionDescRec options [] = {
79   { "-randomize", ".randomize", XrmoptionNoArg, "true" },
80   { "-builtin",   ".builtin",   XrmoptionSepArg, 0 },
81   { "-C", ".minColor",          XrmoptionSepArg, 0 },   /* n */
82   { "-D", ".dwell",             XrmoptionSepArg, 0 },   /* n */
83   { "-L", ".useLog",            XrmoptionNoArg, "true" },
84   { "-M", ".colorExponent",     XrmoptionSepArg, 0 },   /* r */
85   { "-O", ".colorOffset",       XrmoptionSepArg, 0 },   /* n */
86   { "-R", ".randomForce",       XrmoptionSepArg, 0 },   /* p */
87   { "-S", ".settle",            XrmoptionSepArg, 0 },   /* n */
88   { "-a", ".minA",              XrmoptionSepArg, 0 },   /* r */
89   { "-b", ".minB",              XrmoptionSepArg, 0 },   /* n */
90   { "-c", ".wheels",            XrmoptionSepArg, 0 },   /* n */
91   { "-F", ".function",          XrmoptionSepArg, 0 },   /* 10101010 */
92   { "-f", ".forcingFunction",   XrmoptionSepArg, 0 },   /* abbabaab */
93   { "-h", ".bRange",            XrmoptionSepArg, 0 },   /* r */
94   { "-i", ".startX",            XrmoptionSepArg, 0 },   /* r */
95   { "-m", ".mapIndex",          XrmoptionSepArg, 0 },   /* n */
96   { "-o", ".outputFile",        XrmoptionSepArg, 0 },   /* filename */
97   { "-p", ".beNegative",        XrmoptionNoArg, "true" },
98   { "-r", ".rgbMax",            XrmoptionSepArg, 0 },   /* n */
99   { "-s", ".spinLength",        XrmoptionSepArg, 0 },   /* n */
100   { "-v", ".show",              XrmoptionNoArg, "true" },
101   { "-w", ".aRange",            XrmoptionSepArg, 0 },   /* r */
102   { 0, 0, 0, 0 }
103 };
104
105
106 #define ABS(a)  (((a)<0) ? (0-(a)) : (a) )
107 #define Min(x,y) ((x < y)?x:y)
108 #define Max(x,y) ((x > y)?x:y)
109
110 #ifdef SIXTEEN_COLORS
111 #define MAXPOINTS  128
112 #ifdef BIGMEM
113 #define MAXFRAMES 4
114 #else
115 #define MAXFRAMES 2
116 #endif
117 #define MAXCOLOR 16
118 static int maxcolor=16, startcolor=0, color_offset=0, mincolindex=1;
119 static int dwell=50, settle=25;
120 static int width=128, height=128, xposition=128, yposition=128;
121 #else
122 #define MAXPOINTS  256
123 #ifdef BIGMEM
124 #define MAXFRAMES 8
125 #else
126 #define MAXFRAMES 2
127 #endif
128 #define MAXCOLOR 256
129 static int maxcolor=256, startcolor=17, color_offset=96, mincolindex=33;
130 static int dwell=100, settle=50;
131 static int width=256, height=256;
132 #endif
133
134 #ifndef TRUE
135 #define TRUE 1
136 #define FALSE 0
137 #endif
138
139 static int screen;
140 static Display* dpy;
141 static Visual *visual;
142
143 static unsigned long foreground, background;
144
145 static Window canvas;
146
147 typedef struct {
148         int x, y;
149 } xy_t;
150
151 typedef struct {
152         int start_x, start_y;
153         int last_x, last_y;
154         } rubber_band_data_t;
155
156 typedef struct {
157         Cursor band_cursor;
158         double p_min, p_max, q_min, q_max;
159         rubber_band_data_t rubber_band;
160         } image_data_t;
161
162 typedef struct points_t {
163         XPoint data[MAXCOLOR][MAXPOINTS];
164         int npoints[MAXCOLOR];
165         } points_t;
166
167 static points_t Points;
168 static image_data_t rubber_data;
169
170 #ifndef TRUE
171 #define TRUE 1
172 #define FALSE 0
173 #endif
174
175 static GC Data_GC[MAXCOLOR], RubberGC;
176
177 #define MAXINDEX 64
178 #define FUNCMAXINDEX 16
179 #define MAXWHEELS 7
180 #define NUMMAPS 5
181
182 typedef double (*PFD)(double,double);
183
184 static double logistic(double,double), circle(double,double), leftlog(double,double), rightlog(double,double), doublelog(double,double);
185 static double dlogistic(double,double), dcircle(double,double), dleftlog(double,double), drightlog(double,double), ddoublelog(double,double);
186 static PFD map, deriv;
187 static PFD Maps[NUMMAPS] = { logistic, circle, leftlog, rightlog, doublelog };
188 static PFD Derivs[NUMMAPS] = { dlogistic, dcircle, dleftlog, drightlog, ddoublelog };
189
190 static int aflag=0, bflag=0, wflag=0, hflag=0, Rflag=0;
191 static double pmins[NUMMAPS] = { 2.0, 0.0, 0.0, 0.0, 0.0 };
192 static double pmaxs[NUMMAPS] = { 4.0, 1.0, 6.75, 6.75, 16.0 };
193 static double amins[NUMMAPS] = { 2.0, 0.0, 0.0, 0.0, 0.0 };
194 static double aranges[NUMMAPS] = { 2.0, 1.0, 6.75, 6.75, 16.0 };
195 static double bmins[NUMMAPS] = { 2.0, 0.0, 0.0, 0.0, 0.0 };
196 static double branges[NUMMAPS] = { 2.0, 1.0, 6.75, 6.75, 16.0 };
197
198 static int   forcing[MAXINDEX] = { 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,
199                         0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,
200                         0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1 };
201 static int   Forcing[FUNCMAXINDEX] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
202
203 static int   maxindex = MAXINDEX;
204 static int   funcmaxindex = FUNCMAXINDEX;
205 static double   min_a=2.0, min_b=2.0, a_range=2.0, b_range=2.0, minlyap=1.0;
206 static double  max_a=4.0, max_b=4.0;
207 static double  start_x=0.65, lyapunov, a_inc, b_inc, a, b;
208 static int      numcolors=16, numfreecols, displayplanes, lowrange;
209 static xy_t     point;
210 static Pixmap  pixmap;
211 static Colormap cmap;
212 static XColor   Colors[MAXCOLOR];
213 static double  *exponents[MAXFRAMES];
214 static double  a_minimums[MAXFRAMES], b_minimums[MAXFRAMES];
215 static double  a_maximums[MAXFRAMES], b_maximums[MAXFRAMES];
216 static double  minexp, maxexp, prob=0.5;
217 static int     expind[MAXFRAMES]={0}, resized[MAXFRAMES]={0};
218 static int      numwheels=MAXWHEELS, force=0, Force=0, negative=1;
219 static int     rgb_max=65000, nostart=1, stripe_interval=7;
220 static int      save=1, show=0, useprod=1, spinlength=256, savefile=0;
221 static int      maxframe=0, frame=0, dorecalc=0, mapindex=0, run=1;
222 static char     *outname="lyap.out";
223
224
225 const char * const version = LYAP_VERSION;
226
227 static void resize(void);
228 static void redisplay(Window w, XExposeEvent *event);
229 static void Spin(Window w);
230 static void show_defaults(void);
231 static void StartRubberBand(Window w, image_data_t *data, XEvent *event);
232 static void TrackRubberBand(Window w, image_data_t *data, XEvent *event);
233 static void EndRubberBand(Window w, image_data_t *data, XEvent *event);
234 static void CreateXorGC(void);
235 static void InitBuffer(void);
236 static void BufferPoint(Display *display, Window window, int color,
237                         int x, int y);
238 static void FlushBuffer(void);
239 static void init_canvas(void);
240 static void init_data(void);
241 static void init_color(void);
242 static void parseargs(void);
243 static void Clear(void);
244 static void setupmem(void);
245 static void main_event(void);
246 static int complyap(void);
247 static void Getkey(XKeyEvent *event);
248 static int sendpoint(double expo);
249 static void save_to_file(void);
250 static void setforcing(void);
251 static void check_params(int mapnum, int parnum);
252 static void usage(void);
253 static void Destroy_frame(void);
254 static void freemem(void);
255 static void Redraw(void);
256 static void redraw(double *exparray, int index, int cont);
257 static void recalc(void);
258 static void SetupCorners(XPoint *corners, image_data_t *data);
259 static void set_new_params(Window w, image_data_t *data);
260 static void go_down(void);
261 static void go_back(void);
262 static void go_init(void);
263 static void jumpwin(void);
264 static void print_help(void);
265 static void print_values(void);
266
267
268 void
269 screenhack (Display *d, Window window)
270 {
271   XWindowAttributes xgwa;
272   int builtin = -1;
273   dpy = d;
274   XGetWindowAttributes (dpy, window, &xgwa);
275   width = xgwa.width;
276   height = xgwa.height;
277   visual = xgwa.visual;
278   cmap = xgwa.colormap;
279
280   parseargs();
281
282   if (get_boolean_resource("randomize", "Boolean"))
283     builtin = random() % 22;
284   else {
285     char *s = get_string_resource("builtin", "Integer");
286     if (s && *s)
287       builtin = atoi(s);
288     if (s) free (s);
289   }
290     
291   if (builtin >= 0)
292     {
293       char *ff = 0;
294       switch (builtin) {
295       case 0:
296         min_a = 3.75; aflag++;
297         min_b = 3.299999; bflag++;
298         a_range = 0.05; wflag++;
299         b_range = 0.05; hflag++;
300         dwell = 200;
301         settle = 100;
302         ff = "abaabbaaabbb";
303         break;
304
305       case 1:
306         min_a = 3.8; aflag++;
307         min_b = 3.2; bflag++;
308         b_range = .05; hflag++;
309         a_range = .05; wflag++;
310         ff = "bbbbbaaaaa";
311         break;
312
313       case 2:
314         min_a =  3.4; aflag++;
315         min_b =  3.04; bflag++;
316         a_range =  .5; wflag++;
317         b_range =  .5; hflag++;
318         ff = "abbbbbbbbb";
319         settle = 500;
320         dwell = 1000;
321         break;
322
323       case 3:
324         min_a = 3.5; aflag++;
325         min_b = 3.0; bflag++;
326         a_range = 0.2; wflag++;
327         b_range = 0.2; hflag++;
328         dwell = 600;
329         settle = 300;
330         ff = "aaabbbab";
331         break;
332
333       case 4:
334         min_a = 3.55667; aflag++;
335         min_b = 3.2; bflag++;
336         b_range = .05; hflag++;
337         a_range = .05; wflag++;
338         ff = "bbbbbaaaaa";
339         break;
340
341       case 5:
342         min_a = 3.79; aflag++;
343         min_b = 3.22; bflag++;
344         b_range = .02999; hflag++;
345         a_range = .02999; wflag++;
346         ff = "bbbbbaaaaa";
347         break;
348
349       case 6:
350         min_a = 3.7999; aflag++;
351         min_b = 3.299999; bflag++;
352         a_range = 0.2; wflag++;
353         b_range = 0.2; hflag++;
354         dwell = 300;
355         settle = 150;
356         ff = "abaabbaaabbb";
357         break;
358
359       case 7:
360         min_a = 3.89; aflag++;
361         min_b = 3.22; bflag++;
362         b_range = .028; hflag++;
363         a_range = .02999; wflag++;
364         ff = "bbbbbaaaaa";
365         settle = 600;
366         dwell = 1000;
367         break;
368
369       case 8:
370         min_a = 3.2; aflag++;
371         min_b = 3.7; bflag++;
372         a_range = 0.05; wflag++;
373         b_range = .005; hflag++;
374         ff = "abbbbaa";
375         break;
376
377       case 9:
378         ff = "aaaaaabbbbbb";
379         mapindex = 1;
380         dwell =  400;
381         settle =  200;
382         minlyap = maxexp = ABS(-0.85);
383         minexp = -1.0 * minlyap;
384         break;
385
386       case 10:
387         ff = "aaaaaabbbbbb";
388         mapindex = 1;
389         dwell =  400;
390         settle = 200;
391         minlyap = maxexp = ABS(-0.85);
392         minexp = -1.0 * minlyap;
393         break;
394
395       case 11:
396         mapindex = 1;
397         dwell =  400;
398         settle = 200;
399         minlyap = maxexp = ABS(-0.85);
400         minexp = -1.0 * minlyap;
401         break;
402
403       case 12:
404         ff = "abbb";
405         mapindex = 1;
406         dwell =  400;
407         settle = 200;
408         minlyap = maxexp = ABS(-0.85);
409         minexp = -1.0 * minlyap;
410         break;
411
412       case 13:
413         ff = "abbabaab";
414         mapindex = 1;
415         dwell =  400;
416         settle = 200;
417         minlyap = maxexp = ABS(-0.85);
418         minexp = -1.0 * minlyap;
419         break;
420
421       case 14:
422         ff = "abbabaab";
423         dwell =  800;
424         settle = 200;
425         minlyap = maxexp = ABS(-0.85);
426         minexp = -1.0 * minlyap;
427         /* ####  -x 0.05 */
428         min_a = 3.91; aflag++;
429         a_range =  0.0899999999; wflag++;
430         min_b =  3.28; bflag++;
431         b_range =  0.35; hflag++;
432         break;
433
434       case 15:
435         ff = "aaaaaabbbbbb";
436         dwell =  400;
437         settle = 200;
438         minlyap = maxexp = ABS(-0.85);
439         minexp = -1.0 * minlyap;
440         break;
441
442       case 16:
443         dwell =  400;
444         settle = 200;
445         minlyap = maxexp = ABS(-0.85);
446         minexp = -1.0 * minlyap;
447         break;
448
449       case 17:
450         ff = "abbb";
451         dwell =  400;
452         settle = 200;
453         minlyap = maxexp = ABS(-0.85);
454         minexp = -1.0 * minlyap;
455         break;
456
457       case 18:
458         ff = "abbabaab";
459         dwell =  400;
460         settle = 200;
461         minlyap = maxexp = ABS(-0.85);
462         minexp = -1.0 * minlyap;
463         break;
464
465       case 19:
466         mapindex = 2;
467         ff = "aaaaaabbbbbb";
468         dwell =  400;
469         settle = 200;
470         minlyap = maxexp = ABS(-0.85);
471         minexp = -1.0 * minlyap;
472         break;
473
474       case 20:
475         mapindex = 2;
476         dwell =  400;
477         settle = 200;
478         minlyap = maxexp = ABS(-0.85);
479         minexp = -1.0 * minlyap;
480         break;
481
482       case 21:
483         mapindex = 2;
484         ff = "abbb";
485         dwell =  400;
486         settle = 200;
487         minlyap = maxexp = ABS(-0.85);
488         minexp = -1.0 * minlyap;
489         break;
490
491       case 22:
492         mapindex = 2;
493         ff = "abbabaab";
494         dwell =  400;
495         settle = 200;
496         minlyap = maxexp = ABS(-0.85);
497         minexp = -1.0 * minlyap;
498         break;
499       }
500
501       if (ff) {
502         char *ch;
503         int bindex = 0;
504         maxindex = strlen(ff);
505         if (maxindex > MAXINDEX)
506           usage();
507         ch = ff;
508         force++;
509         while (bindex < maxindex) {
510           if (*ch == 'a')
511             forcing[bindex++] = 0;
512           else if (*ch == 'b')
513             forcing[bindex++] = 1;
514           else
515             usage();
516           ch++;
517         }
518       }
519     }
520
521   screen = DefaultScreen(dpy);
522   background = BlackPixel(dpy, screen);
523   setupmem();
524   init_data();
525   if (displayplanes > 1)
526     foreground = startcolor;
527   else
528     foreground = WhitePixel(dpy, screen);
529
530   /*
531   * Create the window to display the Lyapunov exponents
532   */
533   canvas = window;
534   init_canvas();
535
536   if (displayplanes > 1) {
537     init_color();
538   } else {
539     XQueryColors(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
540         Colors, numcolors);
541   }
542   pixmap = XCreatePixmap(dpy, window, width, height, xgwa.depth);
543   rubber_data.band_cursor = XCreateFontCursor(dpy, XC_hand2);
544   CreateXorGC();
545   Clear();
546   for(;;)
547       main_event();
548 }
549
550 static void
551 main_event(void)
552 {
553   int n;
554   XEvent event;
555
556   if (complyap() == TRUE)
557       run=0;
558   n = XEventsQueued(dpy, QueuedAfterFlush);
559   while (n--) {
560           XNextEvent(dpy, &event);
561             switch(event.type)
562             {
563             case KeyPress:
564     Getkey(&event.xkey);
565     break;
566             case Expose:
567     redisplay(canvas, &event.xexpose);
568           break;
569             case ConfigureNotify:
570     resize();
571           break;
572             case ButtonPress:
573     StartRubberBand(canvas, &rubber_data, &event);
574           break;
575             case MotionNotify:
576     TrackRubberBand(canvas, &rubber_data, &event);
577           break;
578             case ButtonRelease:
579     EndRubberBand(canvas, &rubber_data, &event);
580           break;
581             default: 
582     screenhack_handle_event (dpy, &event);
583           break;
584             }
585         }
586 }
587
588 /* complyap() is the guts of the program. This is where the Lyapunov exponent
589  * is calculated. For each iteration (past some large number of iterations)
590  * calculate the logarithm of the absolute value of the derivative at that
591  * point. Then average them over some large number of iterations. Some small
592  * speed up is achieved by utilizing the fact that log(a*b) = log(a) + log(b).
593  */
594 static int
595 complyap(void)
596 {
597   int i, bindex;
598   double total, prod, x, dx, r;
599
600   if (!run)
601     return TRUE;
602   a += a_inc;
603   if (a >= max_a) {
604     if (sendpoint(lyapunov) == TRUE)
605       return FALSE;
606     else {
607       FlushBuffer();
608       if (savefile)
609         save_to_file();
610       return TRUE;
611     }
612   }
613   if (b >= max_b) {
614     FlushBuffer();
615     if (savefile)
616       save_to_file();
617     return TRUE;
618   }
619   prod = 1.0;
620   total = 0.0;
621   bindex = 0;
622   x = start_x;
623   r = (forcing[bindex]) ? b : a;
624 #ifdef MAPS
625   findex = 0;
626   map = Maps[Forcing[findex]];
627 #endif
628   for (i=0;i<settle;i++) {     /* Here's where we let the thing */
629     x = (*map)(x, r);    /* "settle down". There is usually */
630     if (++bindex >= maxindex) { /* some initial "noise" in the */
631       bindex = 0;    /* iterations. How can we optimize */
632       if (Rflag)      /* the value of settle ??? */
633           setforcing();
634     }
635     r = (forcing[bindex]) ? b : a;
636 #ifdef MAPS
637     if (++findex >= funcmaxindex)
638       findex = 0;
639     map = Maps[Forcing[findex]];
640 #endif
641   }
642 #ifdef MAPS
643   deriv = Derivs[Forcing[findex]];
644 #endif
645   if (useprod) {      /* using log(a*b) */
646     for (i=0;i<dwell;i++) {
647       x = (*map)(x, r);
648       dx = (*deriv)(x, r); /* ABS is a macro, so don't be fancy */
649       dx = ABS(dx);
650       if (dx == 0.0) /* log(0) is nasty so break out. */
651       {
652         i++;
653         break;
654       }
655       prod *= dx;
656       /* we need to prevent overflow and underflow */
657       if ((prod > 1.0e12) || (prod < 1.0e-12)) {
658         total += log(prod);
659         prod = 1.0;
660       }
661       if (++bindex >= maxindex) {
662         bindex = 0;
663         if (Rflag)
664           setforcing();
665       }
666       r = (forcing[bindex]) ? b : a;
667 #ifdef MAPS
668       if (++findex >= funcmaxindex)
669         findex = 0;
670       map = Maps[Forcing[findex]];
671       deriv = Derivs[Forcing[findex]];
672 #endif
673     }
674     total += log(prod);
675     lyapunov = (total * M_LOG2E) / (double)i;   
676   }
677   else {        /* use log(a) + log(b) */
678     for (i=0;i<dwell;i++) {
679       x = (*map)(x, r);
680       dx = (*deriv)(x, r); /* ABS is a macro, so don't be fancy */
681       dx = ABS(dx);
682       if (x == 0.0)  /* log(0) check */
683       {
684         i++;
685         break;
686       }
687       total += log(dx);
688       if (++bindex >= maxindex) {
689         bindex = 0;
690         if (Rflag)
691           setforcing();
692       }
693       r = (forcing[bindex]) ? b : a;
694 #ifdef MAPS
695       if (++findex >= funcmaxindex)
696         findex = 0;
697       map = Maps[Forcing[findex]];
698       deriv = Derivs[Forcing[findex]];
699 #endif
700     }
701     lyapunov = (total * M_LOG2E) / (double)i;
702   }
703
704   if (sendpoint(lyapunov) == TRUE)
705     return FALSE;
706   else {
707     FlushBuffer();
708     if (savefile)
709       save_to_file();
710     return TRUE;
711   }
712 }
713
714 static double
715 logistic(double x, double r)        /* the familiar logistic map */
716 {
717   return(r * x * (1.0 - x));
718 }
719
720 static double
721 dlogistic(double x, double r)       /* the derivative of logistic map */
722 {
723   return(r - (2.0 * r * x));
724 }
725
726 static double
727 circle(double x, double r)        /* sin() hump or sorta like the circle map */
728 {
729   return(r * sin(M_PI * x));
730 }
731
732 static double
733 dcircle(double x, double r)       /* derivative of the "sin() hump" */
734 {
735   return(r * M_PI * cos(M_PI * x));
736 }
737
738 static double
739 leftlog(double x, double r)       /* left skewed logistic */
740 {
741   double d;
742
743   d = 1.0 - x;
744   return(r * x * d * d);
745 }
746
747 static double
748 dleftlog(double x, double r)    /* derivative of the left skewed logistic */
749 {
750   return(r * (1.0 - (4.0 * x) + (3.0 * x * x)));
751 }
752
753 static double
754 rightlog(double x, double r)    /* right skewed logistic */
755 {
756   return(r * x * x * (1.0 - x));
757 }
758
759 static double
760 drightlog(double x, double r)    /* derivative of the right skewed logistic */
761 {
762   return(r * ((2.0 * x) - (3.0 * x * x)));
763 }
764
765 static double
766 doublelog(double x, double r)    /* double logistic */
767 {
768   double d;
769
770   d = 1.0 - x;
771   return(r * x * x * d * d);
772 }
773
774 static double
775 ddoublelog(double x, double r)   /* derivative of the double logistic */
776 {
777   double d;
778
779   d = x * x;
780   return(r * ((2.0 * x) - (6.0 * d) + (4.0 * x * d)));
781 }
782
783 static void
784 init_data(void)
785 {
786   numcolors = XDisplayCells(dpy, XDefaultScreen(dpy));
787   displayplanes = DisplayPlanes(dpy, XDefaultScreen(dpy));
788   if (numcolors > maxcolor)
789     numcolors = maxcolor;
790   numfreecols = numcolors - mincolindex;
791   lowrange = mincolindex - startcolor;
792   a_inc = a_range / (double)width;
793   b_inc = b_range / (double)height;
794   point.x = -1;
795   point.y = 0;
796   a = rubber_data.p_min = min_a;
797   b = rubber_data.q_min = min_b;
798   rubber_data.p_max = max_a;
799   rubber_data.q_max = max_b;
800   if (show)
801     show_defaults();
802   InitBuffer();
803 }
804
805 static void
806 init_canvas(void)
807 {
808   static int i;
809
810   /*
811   * create default, writable, graphics contexts for the canvas.
812   */
813         for (i=0; i<maxcolor; i++) {
814             Data_GC[i] = XCreateGC(dpy, canvas,
815                 (unsigned long) NULL, (XGCValues *) NULL);
816             /* set the background to black */
817             XSetBackground(dpy,Data_GC[i],BlackPixel(dpy,XDefaultScreen(dpy)));
818             /* set the foreground of the ith context to i */
819             XSetForeground(dpy, Data_GC[i], i);
820         }
821         if (displayplanes == 1) {
822             XSetForeground(dpy,Data_GC[0],BlackPixel(dpy,XDefaultScreen(dpy)));
823             XSetForeground(dpy,Data_GC[1],WhitePixel(dpy,XDefaultScreen(dpy)));
824         }
825 }
826
827 #if 0
828 static void
829 hls2rgb(int hue_light_sat[3],
830         int rgb[3])             /*      Each in range [0..65535]        */
831 {
832   unsigned short r, g, b;
833   hsv_to_rgb((int) (hue_light_sat[0] / 10),             /* 0-3600 -> 0-360 */
834              (int) ((hue_light_sat[2]/1000.0) * 64435), /* 0-1000 -> 0-65535 */
835              (int) ((hue_light_sat[1]/1000.0) * 64435), /* 0-1000 -> 0-65535 */
836              &r, &g, &b);
837   rgb[0] = r;
838   rgb[1] = g;
839   rgb[2] = b;
840 }
841 #endif /* 0 */
842
843
844 static void
845 init_color(void)
846 {
847 #if 1
848
849   int i;
850   XColor colors[256];
851   int ncolors = maxcolor;
852   Bool writable = False;
853   make_smooth_colormap(dpy, visual, cmap,
854                         colors, &ncolors, True, &writable, True);
855
856   for (i = 0; i < maxcolor; i++)
857     XSetForeground(dpy, Data_GC[i],
858                    colors[((int) ((i / ((float)maxcolor)) * ncolors))].pixel);
859
860 #else
861   static int i, j, colgap, leg, step;
862   static Visual *visual;
863   Colormap def_cmap;
864   int hls[3], rgb[3];
865
866   def_cmap = DefaultColormap(dpy, DefaultScreen(dpy));
867   for (i=0; i<numcolors; i++) {
868     Colors[i].pixel = i;
869     Colors[i].flags = DoRed|DoGreen|DoBlue;
870   }
871
872   /* Try to write into a new color map */
873   visual = DefaultVisual(dpy, DefaultScreen(dpy));
874   cmap = XCreateColormap(dpy, canvas, visual, AllocAll);
875   XQueryColors(dpy, def_cmap, Colors, numcolors);
876   if (mincolindex)
877     colgap = rgb_max / mincolindex;
878   else
879     colgap = rgb_max;
880   hls[0] = 50;  /* Hue in low range */
881   hls[2] = 1000;  /* Fully saturated */
882   for (i=startcolor; i<lowrange + startcolor; i++) {
883     hls[1] = 1000L * (i-startcolor) / lowrange;
884     hls2rgb(hls, rgb);
885     Colors[i].red = rgb[0];
886     Colors[i].green = rgb[1];
887     Colors[i].blue = rgb[2];
888   }
889   colgap = rgb_max / numcolors;
890   if (numwheels == 0)
891     XQueryColors(dpy, def_cmap, Colors, numcolors);
892   else if (numwheels == 1) {
893     colgap = 2*rgb_max/(numcolors - color_offset);
894     for (i=mincolindex; i<(numcolors/2); i++) {
895       Colors[i].blue = 0;
896       Colors[i].green=((i+color_offset)*colgap);
897       Colors[i].red=((i+color_offset)*colgap);
898     }
899     for (i=(numcolors/2); i<(numcolors); i++) {
900       Colors[i].blue = 0;
901       Colors[i].green=(((numcolors-i)+color_offset)*colgap);
902       Colors[i].red=(((numcolors-i)+color_offset)*colgap);
903     }
904   }
905   else if (numwheels == 2) {
906           hls[0] = 800; /* Hue in mid range */
907           hls[2] = 1000;  /* Fully saturated */
908           for (i=startcolor; i<lowrange + startcolor; i++) {
909       hls[1] = 1000L * (i-startcolor) / lowrange;
910       hls2rgb(hls, rgb);
911       Colors[i].red = rgb[0];
912       Colors[i].green = rgb[1];
913       Colors[i].blue = rgb[2];
914           }
915     for (i=mincolindex; i<(numcolors/2); i++) {
916       Colors[i].blue = rgb_max;
917       Colors[i].green = 0;
918       Colors[i].red=(i*2*rgb_max/numcolors);
919     }
920     for (i=(numcolors/2); i<numcolors; i++) {
921       Colors[i].blue = rgb_max;
922       Colors[i].green = 0;
923       Colors[i].red=((numcolors - i)*2*rgb_max/numcolors);
924     }
925   }
926   else if (numwheels == 3) {
927           hls[0] = 800; /* Hue in mid range */
928           hls[2] = 1000;  /* Fully saturated */
929           for (i=startcolor; i<lowrange + startcolor; i++) {
930       hls[1] = 1000L * (i-startcolor) / lowrange;
931       hls2rgb(hls, rgb);
932       Colors[i].red = rgb[0];
933       Colors[i].green = rgb[1];
934       Colors[i].blue = rgb[2];
935           }
936     colgap = 4*rgb_max/numcolors;
937     for (i=mincolindex; i<(numcolors/4); i++) {
938       Colors[i].blue = rgb_max;
939       Colors[i].green = 0;
940       Colors[i].red=(i*colgap);
941     }
942     for (i=(numcolors/4); i<(numcolors/2); i++) {
943       Colors[i].red = rgb_max;
944       Colors[i].green = 0;
945       Colors[i].blue=((numcolors/2) - i) * colgap;
946     }
947     for (i=(numcolors/2); i<(0.75*numcolors); i++) {
948       Colors[i].red = rgb_max;
949       Colors[i].blue=(i * colgap);
950       Colors[i].green = 0;
951     }
952     for (i=(0.75*numcolors); i<numcolors; i++) {
953       Colors[i].blue = rgb_max;
954       Colors[i].green = 0;
955       Colors[i].red=(numcolors-i)*colgap;
956     }
957   }
958   else if (numwheels == 4) {
959           hls[0] = 800; /* Hue in mid range */
960           hls[2] = 1000;  /* Fully saturated */
961           for (i=startcolor; i<lowrange + startcolor; i++) {
962       hls[1] = 1000L * (i-startcolor) / lowrange;
963       hls2rgb(hls, rgb);
964       Colors[i].red = rgb[0];
965       Colors[i].green = rgb[1];
966       Colors[i].blue = rgb[2];
967           }
968     colgap = numwheels * rgb_max / numcolors;
969     for (i=mincolindex; i<(numcolors/numwheels); i++) {
970       Colors[i].blue = rgb_max;
971       Colors[i].green = 0;
972       Colors[i].red=(i*colgap);
973     }
974     for (i=(numcolors/numwheels); i<(2*numcolors/numwheels); i++) {
975       Colors[i].red = rgb_max;
976       Colors[i].green = 0;
977       Colors[i].blue=((2*numcolors/numwheels) - i) * colgap;
978     }
979     for (i=(2*numcolors/numwheels); i<numcolors; i++) {
980       Colors[i].red = rgb_max;
981       Colors[i].green=(i - (2*numcolors/numwheels)) * colgap;
982       Colors[i].blue = 0;
983     }
984   }
985   else if (numwheels == 5) {
986     hls[1] = 700; /* Lightness in midrange */
987     hls[2] = 1000;  /* Fully saturated */
988     for (i=mincolindex; i<numcolors; i++) {
989       hls[0] = 3600L * i / numcolors;
990       hls2rgb(hls, rgb);
991       Colors[i].red = rgb[0];
992       Colors[i].green = rgb[1];
993       Colors[i].blue = rgb[2];
994     }
995     for (i=mincolindex; i<numcolors; i+=stripe_interval) {
996       hls[0] = 3600L * i / numcolors;
997       hls2rgb(hls, rgb);
998       Colors[i].red = rgb[0] / 2;
999       Colors[i].green = rgb[1] / 2;
1000       Colors[i].blue = rgb[2] / 2;
1001     }
1002   }
1003   else if (numwheels == 6) {
1004       hls[0] = 800; /* Hue in mid range */
1005       hls[2] = 1000;  /* Fully saturated */
1006       for (i=startcolor; i<lowrange + startcolor; i++) {
1007     hls[1] = 1000L * (i-startcolor) / lowrange;
1008     hls2rgb(hls, rgb);
1009     Colors[i].red = rgb[0];
1010     Colors[i].green = rgb[1];
1011     Colors[i].blue = rgb[2];
1012       }
1013       step = numfreecols / 3;
1014       leg = step+mincolindex;
1015       for (i = mincolindex; i < leg; ++i)
1016       {
1017     Colors[i].pixel = i;
1018     Colors[i].red = fabs(65535 - (double)i / step * 65535.0);
1019     Colors[i].blue = (double)i / step * 65535.0;
1020     Colors[i].green = 0;
1021     Colors[i].flags = DoRed | DoGreen | DoBlue;
1022       }
1023       for (j = 0, i = leg, leg += step; i < leg; ++i, ++j)
1024       {
1025     Colors[i].pixel = i;
1026     Colors[i].red = (double)j / step * 65535.0;
1027     Colors[i].blue = 65535;
1028     Colors[i].green = Colors[i].red;
1029     Colors[i].flags = DoRed | DoGreen | DoBlue;
1030       }
1031       for (j = 0, i = leg, leg += step; i < leg; ++i, ++j)
1032       {
1033     Colors[i].pixel = i;
1034     Colors[i].red = 65535;
1035     Colors[i].blue = fabs(65535 - (double)j / step * 65535.0);
1036     Colors[i].green = Colors[i].blue;
1037     Colors[i].flags = DoRed | DoGreen | DoBlue;
1038       }
1039   }
1040   else if (numwheels == MAXWHEELS) {  /* rainbow palette */
1041     hls[1] = 500; /* Lightness in midrange */
1042     hls[2] = 1000;  /* Fully saturated */
1043     for (i=mincolindex; i<numcolors; i++) {
1044       hls[0] = 3600L * i / numcolors;
1045       hls2rgb(hls, rgb);
1046       Colors[i].red = rgb[0];
1047       Colors[i].green = rgb[1];
1048       Colors[i].blue = rgb[2];
1049     }
1050   }
1051   XStoreColors(dpy, cmap, Colors, numcolors);
1052
1053   XSetWindowColormap(dpy, canvas, cmap);
1054 #endif
1055 }
1056
1057 static void
1058 parseargs()
1059 {
1060   static int i;
1061   int bindex=0, findex;
1062   char *s, *ch;
1063
1064   map = Maps[0];
1065   deriv = Derivs[0];
1066   maxexp=minlyap; minexp= -1.0 * minlyap;
1067
1068   mincolindex = get_integer_resource("minColor", "Integer");
1069   dwell = get_integer_resource("dwell", "Integer");
1070 #ifdef MAPS
1071   {
1072     char *optarg = get_string_resource("function", "String");
1073     funcmaxindex = strlen(optarg);
1074     if (funcmaxindex > FUNCMAXINDEX)
1075       usage();
1076     ch = optarg;
1077     Force++;
1078     for (findex=0;findex<funcmaxindex;findex++) {
1079       Forcing[findex] = (int)(*ch++ - '0');;
1080       if (Forcing[findex] >= NUMMAPS)
1081         usage();
1082     }
1083   }
1084 #endif
1085   if (get_boolean_resource("useLog", "Boolean"))
1086     useprod=0;
1087
1088   minlyap=ABS(get_float_resource("colorExponent", "Float"));
1089   maxexp=minlyap;
1090   minexp= -1.0 * minlyap;
1091
1092   color_offset = get_integer_resource("colorOffset", "Integer");
1093
1094   maxcolor=ABS(get_integer_resource("maxColor", "Integer"));
1095   if ((maxcolor - startcolor) <= 0)
1096     startcolor = 0;
1097   if ((maxcolor - mincolindex) <= 0) {
1098     mincolindex = 1;
1099     color_offset = 0;
1100   }
1101
1102   s = get_string_resource("randomForce", "Float");
1103   if (s && *s) {
1104     prob=atof(s); Rflag++; setforcing();
1105   }
1106
1107   settle = get_integer_resource("settle", "Integer");
1108
1109   s = get_string_resource("minA", "Float");
1110   if (s && *s) {
1111     min_a = atof(s);
1112     aflag++;
1113   }
1114   
1115   s = get_string_resource("minB", "Float");
1116   if (s && *s) {
1117     min_b=atof(s); bflag++;
1118   }
1119   
1120   numwheels = get_integer_resource("wheels", "Integer");
1121
1122   s = get_string_resource("forcingFunction", "String");
1123   if (s && *s) {
1124     maxindex = strlen(s);
1125     if (maxindex > MAXINDEX)
1126       usage();
1127     ch = s;
1128     force++;
1129     while (bindex < maxindex) {
1130       if (*ch == 'a')
1131         forcing[bindex++] = 0;
1132       else if (*ch == 'b')
1133         forcing[bindex++] = 1;
1134       else
1135         usage();
1136       ch++;
1137     }
1138   }
1139
1140   s = get_string_resource("bRange", "Float");
1141   if (s && *s) {
1142     b_range = atof(s);
1143     hflag++;
1144   }
1145
1146   start_x = get_float_resource("startX", "Float");
1147
1148   s = get_string_resource("mapIndex", "Integer");
1149   if (s && *s) {
1150     mapindex=atoi(s);
1151     if ((mapindex >= NUMMAPS) || (mapindex < 0))
1152       usage();
1153     map = Maps[mapindex];
1154     deriv = Derivs[mapindex];
1155     if (!aflag)
1156       min_a = amins[mapindex];
1157     if (!wflag)
1158       a_range = aranges[mapindex];
1159     if (!bflag)
1160       min_b = bmins[mapindex];
1161     if (!hflag)
1162       b_range = branges[mapindex];
1163     if (!Force)
1164       for (i=0;i<FUNCMAXINDEX;i++)
1165         Forcing[i] = mapindex;
1166   }
1167
1168   outname = get_string_resource("outputFile", "Integer");
1169
1170   if (get_boolean_resource("beNegative", "Boolean"))
1171     negative--;
1172
1173   rgb_max = get_integer_resource("rgbMax", "Integer");
1174   spinlength = get_integer_resource("spinLength", "Integer");
1175   show = get_boolean_resource("show", "Boolean");
1176
1177   s = get_string_resource("aRange", "Float");
1178   if (s && *s) {
1179     a_range = atof(s); wflag++;
1180   }
1181
1182   max_a = min_a + a_range;
1183   max_b = min_b + b_range;
1184
1185   a_minimums[0] = min_a; b_minimums[0] = min_b;
1186   a_maximums[0] = max_a; b_maximums[0] = max_b;
1187
1188   if (Force)
1189     if (maxindex == funcmaxindex)
1190       for (findex=0;findex<funcmaxindex;findex++)
1191         check_params(Forcing[findex],forcing[findex]);
1192     else
1193       fprintf(stderr, "Warning! Unable to check parameters\n");
1194   else
1195     check_params(mapindex,2);
1196 }
1197
1198 static void
1199 check_params(int mapnum, int parnum)
1200 {
1201
1202   if (parnum != 1) {
1203       if ((max_a > pmaxs[mapnum]) || (min_a < pmins[mapnum])) {
1204     fprintf(stderr, "Warning! Parameter 'a' out of range.\n");
1205     fprintf(stderr, "You have requested a range of (%f,%f).\n",
1206       min_a,max_a);
1207     fprintf(stderr, "Valid range is (%f,%f).\n",
1208       pmins[mapnum],pmaxs[mapnum]);
1209       }
1210   }
1211   if (parnum != 0) {
1212       if ((max_b > pmaxs[mapnum]) || (min_b < pmins[mapnum])) {
1213     fprintf(stderr, "Warning! Parameter 'b' out of range.\n");
1214     fprintf(stderr, "You have requested a range of (%f,%f).\n",
1215       min_b,max_b);
1216     fprintf(stderr, "Valid range is (%f,%f).\n",
1217       pmins[mapnum],pmaxs[mapnum]);
1218       }
1219   }
1220 }
1221
1222 static void
1223 usage(void)
1224 {
1225     fprintf(stderr,"lyap [-BLs][-W#][-H#][-a#][-b#][-w#][-h#][-x xstart]\n");
1226     fprintf(stderr,"\t[-M#][-S#][-D#][-f string][-r#][-O#][-C#][-c#][-m#]\n");
1227 #ifdef MAPS
1228     fprintf(stderr,"\t[-F string]\n");
1229 #endif
1230     fprintf(stderr,"\tWhere: -C# specifies the minimum color index\n");
1231     fprintf(stderr,"\t       -r# specifies the maxzimum rgb value\n");
1232     fprintf(stderr,"\t       -u displays this message\n");
1233     fprintf(stderr,"\t       -a# specifies the minimum horizontal parameter\n");
1234     fprintf(stderr,"\t       -b# specifies the minimum vertical parameter\n");
1235     fprintf(stderr,"\t       -w# specifies the horizontal parameter range\n");
1236     fprintf(stderr,"\t       -h# specifies the vertical parameter range\n");
1237     fprintf(stderr,"\t       -D# specifies the dwell\n");
1238     fprintf(stderr,"\t       -S# specifies the settle\n");
1239     fprintf(stderr,"\t       -H# specifies the initial window height\n");
1240     fprintf(stderr,"\t       -W# specifies the initial window width\n");
1241     fprintf(stderr,"\t       -O# specifies the color offset\n");
1242     fprintf(stderr,"\t       -c# specifies the desired color wheel\n");
1243     fprintf(stderr,"\t       -m# specifies the desired map (0-4)\n");
1244     fprintf(stderr,"\t       -f aabbb specifies a forcing function of 00111\n");
1245 #ifdef MAPS
1246     fprintf(stderr,"\t       -F 00111 specifies the function forcing function\n");
1247 #endif
1248     fprintf(stderr,"\t       -L indicates use log(x)+log(y) rather than log(xy)\n");
1249     fprintf(stderr,"\tDuring display :\n");
1250     fprintf(stderr,"\t     Use the mouse to zoom in on an area\n");
1251     fprintf(stderr,"\t     e or E recalculates color indices\n");
1252     fprintf(stderr,"\t     f or F saves exponents to a file\n");
1253     fprintf(stderr,"\t     KJmn increase/decrease minimum negative exponent\n");
1254     fprintf(stderr,"\t     r or R redraws\n");
1255     fprintf(stderr,"\t     s or S spins the colorwheel\n");
1256     fprintf(stderr,"\t     w or W changes the color wheel\n");
1257     fprintf(stderr,"\t     x or X clears the window\n");
1258     fprintf(stderr,"\t     q or Q exits\n");
1259     exit(1);
1260 }
1261
1262 static void
1263 Cycle_frames(void)
1264 {
1265   static int i;
1266   for (i=0;i<=maxframe;i++)
1267     redraw(exponents[i], expind[i], 1);
1268 }
1269
1270 static void
1271 Spin(Window w)
1272 {
1273   static int i, j;
1274   long tmpxcolor;
1275
1276   if (displayplanes > 1) {
1277     for (j=0;j<spinlength;j++) {
1278       tmpxcolor = Colors[mincolindex].pixel;
1279       for (i=mincolindex;i<numcolors-1;i++)
1280         Colors[i].pixel = Colors[i+1].pixel;
1281       Colors[numcolors-1].pixel = tmpxcolor;
1282       XStoreColors(dpy, cmap, Colors, numcolors);
1283     }
1284     for (j=0;j<spinlength;j++) {
1285       tmpxcolor = Colors[numcolors-1].pixel;
1286       for (i=numcolors-1;i>mincolindex;i--)
1287         Colors[i].pixel = Colors[i-1].pixel;
1288       Colors[mincolindex].pixel = tmpxcolor;
1289       XStoreColors(dpy, cmap, Colors, numcolors);
1290     }
1291   }
1292 }
1293
1294 static void
1295 Getkey(XKeyEvent *event)
1296 {
1297   unsigned char key;
1298   static int i;
1299   if (XLookupString(event, (char *)&key, sizeof(key), (KeySym *)0,
1300             (XComposeStatus *) 0) > 0)
1301     switch (key) {
1302   case '<': dwell /= 2; if (dwell < 1) dwell = 1; break;
1303   case '>': dwell *= 2; break;
1304   case '[': settle /= 2; if (settle < 1) settle = 1; break;
1305   case ']': settle *= 2; break;
1306   case 'd': go_down(); break;
1307   case 'D': FlushBuffer(); break;
1308   case 'e':
1309   case 'E': FlushBuffer();
1310       dorecalc = (!dorecalc);
1311       if (dorecalc)
1312       recalc();
1313       else {
1314       maxexp = minlyap; minexp = -1.0 * minlyap;
1315       }
1316       redraw(exponents[frame], expind[frame], 1);
1317       break;
1318   case 'f':
1319   case 'F': save_to_file(); break;
1320   case 'i': if (stripe_interval > 0) {
1321       stripe_interval--;
1322         if (displayplanes > 1) {
1323             init_color();
1324         }
1325       }
1326       break;
1327   case 'I': stripe_interval++;
1328       if (displayplanes > 1) {
1329         init_color();
1330       }
1331       break;
1332   case 'K': if (minlyap > 0.05)
1333       minlyap -= 0.05;
1334        break;
1335   case 'J': minlyap += 0.05;
1336        break;
1337   case 'm': mapindex++;
1338                   if (mapindex >= NUMMAPS)
1339                         mapindex=0;
1340                   map = Maps[mapindex];
1341                   deriv = Derivs[mapindex];
1342       if (!aflag)
1343                         min_a = amins[mapindex];
1344                   if (!wflag)
1345                         a_range = aranges[mapindex];
1346                   if (!bflag)
1347                         min_b = bmins[mapindex];
1348                   if (!hflag)
1349                         b_range = branges[mapindex];
1350                   if (!Force)
1351                         for (i=0;i<FUNCMAXINDEX;i++)
1352                              Forcing[i] = mapindex;
1353             max_a = min_a + a_range;
1354             max_b = min_b + b_range;
1355             a_minimums[0] = min_a; b_minimums[0] = min_b;
1356             a_maximums[0] = max_a; b_maximums[0] = max_b;
1357             a_inc = a_range / (double)width;
1358             b_inc = b_range / (double)height;
1359             point.x = -1;
1360             point.y = 0;
1361             a = rubber_data.p_min = min_a;
1362             b = rubber_data.q_min = min_b;
1363             rubber_data.p_max = max_a;
1364             rubber_data.q_max = max_b;
1365                   Clear();
1366                   break;
1367   case 'M': if (minlyap > 0.005)
1368       minlyap -= 0.005;
1369        break;
1370   case 'N': minlyap += 0.005;
1371        break;
1372   case 'p':
1373   case 'P': negative = (!negative);
1374       FlushBuffer(); redraw(exponents[frame], expind[frame], 1);
1375       break;
1376   case 'r': FlushBuffer(); redraw(exponents[frame], expind[frame], 1);
1377       break;
1378   case 'R': FlushBuffer(); Redraw(); break;
1379   case 's':
1380        spinlength=spinlength/2;
1381   case 'S': if (displayplanes > 1)
1382       Spin(canvas);
1383        spinlength=spinlength*2; break;
1384   case 'u': go_back(); break;
1385   case 'U': go_init(); break;
1386   case 'v':
1387   case 'V': print_values(); break;
1388   case 'W': if (numwheels < MAXWHEELS)
1389       numwheels++;
1390        else
1391       numwheels = 0;
1392        if (displayplanes > 1) {
1393         init_color();
1394        }
1395        break;
1396   case 'w': if (numwheels > 0)
1397       numwheels--;
1398        else
1399       numwheels = MAXWHEELS;
1400        if (displayplanes > 1) {
1401         init_color();
1402        }
1403        break;
1404   case 'x': Clear(); break;
1405   case 'X': Destroy_frame(); break;
1406   case 'z': Cycle_frames(); redraw(exponents[frame], expind[frame], 1);
1407       break;
1408   case 'Z': while (!XPending(dpy)) Cycle_frames();
1409       redraw(exponents[frame], expind[frame], 1); break;
1410   case 'q':
1411   case 'Q': exit(0); break;
1412   case '?':
1413   case 'h':
1414   case 'H': print_help(); break;
1415   default:  break;
1416   }
1417 }
1418
1419 /* Here's where we index into a color map. After the Lyapunov exponent is
1420  * calculated, it is used to determine what color to use for that point.
1421  * I suppose there are a lot of ways to do this. I used the following :
1422  * if it's non-negative then there's a reserved area at the lower range
1423  * of the color map that i index into. The ratio of some "minimum exponent
1424  * value" and the calculated value is used as a ratio of how high to index
1425  * into this reserved range. Usually these colors are dark red (see init_color).
1426  * If the exponent is negative, the same ratio (expo/minlyap) is used to index
1427  * into the remaining portion of the colormap (which is usually some light
1428  * shades of color or a rainbow wheel). The coloring scheme can actually make
1429  * a great deal of difference in the quality of the picture. Different colormaps
1430  * bring out different details of the dynamics while different indexing
1431  * algorithms also greatly effect what details are seen. Play around with this.
1432  */
1433 static int
1434 sendpoint(double expo)
1435 {
1436   static int index;
1437   static double tmpexpo;
1438
1439 #if 0
1440 /* The relationship minexp <= expo <= maxexp should always be true. This test
1441    enforces that. But maybe not enforcing it makes better pictures. */
1442   if (expo < minexp)
1443     expo = minexp;
1444   else if (expo > maxexp)
1445     expo = maxexp;
1446 #endif
1447
1448   point.x++;
1449   tmpexpo = (negative) ? expo : -1.0 * expo;
1450   if (tmpexpo > 0) {
1451     if (displayplanes >1) {
1452         index = (int)(tmpexpo*lowrange/maxexp);
1453         index = (index % lowrange) + startcolor;
1454     }
1455     else
1456         index = 0;
1457   }
1458   else {
1459     if (displayplanes >1) {
1460         index = (int)(tmpexpo*numfreecols/minexp);
1461         index = (index % numfreecols) + mincolindex;
1462     }
1463     else
1464         index = 1;
1465   }
1466     BufferPoint(dpy, canvas, index, point.x, point.y);
1467   if (save)
1468     exponents[frame][expind[frame]++] = expo;
1469   if (point.x >= width) {
1470     point.y++;
1471     point.x = 0;
1472     if (save) {
1473       b += b_inc;
1474       a = min_a;
1475     }
1476     if (point.y >= height)
1477       return FALSE;
1478     else
1479       return TRUE;
1480   }
1481   return TRUE;
1482 }
1483
1484 static void
1485 redisplay (Window w, XExposeEvent *event)
1486 {
1487   /*
1488   * Extract the exposed area from the event and copy
1489   * from the saved pixmap to the window.
1490   */
1491   XCopyArea(dpy, pixmap, canvas, Data_GC[0],
1492            event->x, event->y, event->width, event->height,
1493            event->x, event->y);
1494 }
1495
1496 static void
1497 resize(void)
1498 {
1499   Window r;
1500   int n, x, y;
1501   unsigned int bw, d, new_w, new_h;
1502
1503   XGetGeometry(dpy,canvas,&r,&x,&y,&new_w,&new_h,&bw,&d);
1504   if ((new_w == width) && (new_h == height))
1505     return;
1506   width = new_w; height = new_h;
1507   XClearWindow(dpy, canvas);
1508   if (pixmap)
1509     XFreePixmap(dpy, pixmap);
1510   pixmap = XCreatePixmap(dpy, canvas, width, height, d);
1511   a_inc = a_range / (double)width;
1512   b_inc = b_range / (double)height;
1513   point.x = -1;
1514   point.y = 0;
1515   run = 1;
1516   a = rubber_data.p_min = min_a;
1517   b = rubber_data.q_min = min_b;
1518   rubber_data.p_max = max_a;
1519   rubber_data.q_max = max_b;
1520   freemem();
1521   setupmem();
1522         for (n=0;n<MAXFRAMES;n++)
1523     if ((n <= maxframe) && (n != frame))
1524         resized[n] = 1;
1525   InitBuffer();
1526   Clear();
1527   Redraw();
1528 }
1529
1530 static void
1531 redraw(double *exparray, int index, int cont)
1532 {
1533   static int i;
1534   static int x_sav, y_sav;
1535
1536   x_sav = point.x;
1537   y_sav = point.y;
1538
1539   point.x = -1;
1540   point.y = 0;
1541
1542   save=0;
1543   for (i=0;i<index;i++)
1544     sendpoint(exparray[i]);
1545   save=1;
1546
1547   if (cont) {
1548     point.x = x_sav;
1549     point.y = y_sav;
1550   }
1551   else {
1552     a = point.x * a_inc + min_a;
1553     b = point.y * b_inc + min_b;
1554   }
1555   FlushBuffer();
1556 }
1557
1558 static void
1559 Redraw(void)
1560 {
1561   FlushBuffer();
1562         point.x = -1;
1563         point.y = 0;
1564   run = 1;
1565         a = min_a;
1566         b = min_b;
1567   expind[frame] = 0;
1568   resized[frame] = 0;
1569 }
1570
1571 /* Store color pics in PPM format and monochrome in PGM */
1572 static void
1573 save_to_file(void)
1574 {
1575   FILE *outfile;
1576   unsigned char c;
1577   XImage *ximage;
1578   static int i,j;
1579   struct Colormap {
1580     unsigned char red;
1581     unsigned char green;
1582     unsigned char blue;
1583   };
1584   struct Colormap *colormap=NULL;
1585
1586   if (colormap)
1587     free(colormap);
1588   if ((colormap=
1589     (struct Colormap *)malloc(sizeof(struct Colormap)*maxcolor))
1590       == NULL) {
1591     fprintf(stderr,"Error malloc'ing colormap array\n");
1592     exit(-1);
1593   }
1594   outfile = fopen(outname,"w");
1595   if(!outfile) {
1596     perror(outname);
1597     exit(-1);
1598   }
1599
1600   ximage=XGetImage(dpy, pixmap, 0, 0, width, height, AllPlanes, XYPixmap);
1601
1602   if (displayplanes > 1) {
1603     for (i=0;i<maxcolor;i++) {
1604       colormap[i].red=(unsigned char)(Colors[i].red >> 8);
1605       colormap[i].green=(unsigned char)(Colors[i].green >> 8);
1606       colormap[i].blue =(unsigned char)(Colors[i].blue >> 8);
1607     }
1608     fprintf(outfile,"P%d %d %d\n",6,width,height);
1609   }
1610   else
1611     fprintf(outfile,"P%d %d %d\n",5,width,height);
1612   fprintf(outfile,"# settle=%d  dwell=%d start_x=%f\n",settle,dwell,
1613         start_x);
1614   fprintf(outfile,"# min_a=%f  a_rng=%f  max_a=%f\n",min_a,a_range,max_a);
1615   fprintf(outfile,"# min_b=%f  b_rng=%f  max_b=%f\n",min_b,b_range,max_b);
1616   if (Rflag)
1617     fprintf(outfile,"# pseudo-random forcing\n");
1618   else if (force) {
1619     fprintf(outfile,"# periodic forcing=");
1620     for (i=0;i<maxindex;i++) {
1621       fprintf(outfile,"%d",forcing[i]);
1622     }
1623     fprintf(outfile,"\n");
1624   }
1625   else
1626     fprintf(outfile,"# periodic forcing=01\n");
1627   if (Force) {
1628     fprintf(outfile,"# function forcing=");
1629     for (i=0;i<funcmaxindex;i++) {
1630       fprintf(outfile,"%d",Forcing[i]);
1631     }
1632     fprintf(outfile,"\n");
1633   }
1634   fprintf(outfile,"%d\n",numcolors-1);
1635
1636   for (j=0;j<height;j++)
1637       for (i=0;i<width;i++) {
1638     c = (unsigned char)XGetPixel(ximage,i,j);
1639     if (displayplanes > 1)
1640         fwrite((char *)&colormap[c],sizeof colormap[0],1,outfile);
1641     else
1642         fwrite((char *)&c,sizeof c,1,outfile);
1643       }
1644   fclose(outfile);
1645 }
1646
1647 static void
1648 recalc(void)
1649 {
1650   static int i, x, y;
1651
1652   minexp = maxexp = 0.0;
1653   x = y = 0;
1654   for (i=0;i<expind[frame];i++) {
1655     if (exponents[frame][i] < minexp)
1656       minexp = exponents[frame][i];
1657     if (exponents[frame][i] > maxexp)
1658       maxexp = exponents[frame][i];
1659   }
1660 }
1661
1662 static void
1663 Clear(void)
1664 {
1665       XClearWindow(dpy, canvas);
1666   XCopyArea(dpy, canvas, pixmap, Data_GC[0],
1667             0, 0, width, height, 0, 0);
1668   InitBuffer();
1669 }
1670
1671 static void
1672 show_defaults(void)
1673 {
1674
1675   printf("Width=%d  Height=%d  numcolors=%d  settle=%d  dwell=%d\n",
1676     width,height,numcolors,settle,dwell);
1677   printf("min_a=%f  a_range=%f  max_a=%f\n", min_a,a_range,max_a);
1678   printf("min_b=%f  b_range=%f  max_b=%f\n", min_b,b_range,max_b);
1679   printf("minlyap=%f  minexp=%f  maxexp=%f\n", minlyap,minexp,maxexp);
1680   exit(0);
1681 }
1682
1683 static void
1684 CreateXorGC(void)
1685 {
1686   XGCValues values;
1687
1688   values.foreground = foreground;
1689   values.line_style = LineSolid;
1690   values.function = GXxor;
1691   RubberGC = XCreateGC(dpy, canvas,
1692         GCForeground | GCBackground | GCFunction | GCLineStyle, &values);
1693 }
1694
1695 static void
1696 StartRubberBand(Window w, image_data_t *data, XEvent *event)
1697 {
1698   XPoint corners[5];
1699
1700   nostart = 0;
1701   data->rubber_band.last_x = data->rubber_band.start_x = event->xbutton.x;
1702   data->rubber_band.last_y = data->rubber_band.start_y = event->xbutton.y;
1703   SetupCorners(corners, data);
1704   XDrawLines(dpy, canvas, RubberGC,
1705       corners, sizeof(corners) / sizeof(corners[0]), CoordModeOrigin);
1706 }
1707
1708 static void
1709 SetupCorners(XPoint *corners, image_data_t *data)
1710 {
1711   corners[0].x = data->rubber_band.start_x;
1712   corners[0].y = data->rubber_band.start_y;
1713   corners[1].x = data->rubber_band.start_x;
1714   corners[1].y = data->rubber_band.last_y;
1715   corners[2].x = data->rubber_band.last_x;
1716   corners[2].y = data->rubber_band.last_y;
1717   corners[3].x = data->rubber_band.last_x;
1718   corners[3].y = data->rubber_band.start_y;
1719   corners[4] = corners[0];
1720 }
1721
1722 static void
1723 TrackRubberBand(Window w, image_data_t *data, XEvent *event)
1724 {
1725   XPoint corners[5];
1726   int xdiff, ydiff;
1727
1728   if (nostart)
1729     return;
1730   SetupCorners(corners, data);
1731   XDrawLines(dpy, canvas, RubberGC,
1732       corners, sizeof(corners) / sizeof(corners[0]), CoordModeOrigin);
1733   ydiff = event->xbutton.y - data->rubber_band.start_y;
1734   xdiff = event->xbutton.x - data->rubber_band.start_x;
1735   data->rubber_band.last_x = data->rubber_band.start_x + xdiff;
1736   data->rubber_band.last_y = data->rubber_band.start_y + ydiff;
1737   if (data->rubber_band.last_y < data->rubber_band.start_y ||
1738       data->rubber_band.last_x < data->rubber_band.start_x)
1739   {
1740     data->rubber_band.last_y = data->rubber_band.start_y;
1741     data->rubber_band.last_x = data->rubber_band.start_x;
1742   }
1743   SetupCorners(corners, data);
1744   XDrawLines(dpy, canvas, RubberGC,
1745       corners, sizeof(corners) / sizeof(corners[0]), CoordModeOrigin);
1746 }
1747
1748 static void
1749 EndRubberBand(Window w, image_data_t *data, XEvent *event)
1750 {
1751   XPoint corners[5];
1752   XPoint top, bot;
1753   double delta, diff;
1754
1755   nostart = 1;
1756   SetupCorners(corners, data);
1757   XDrawLines(dpy, canvas, RubberGC,
1758       corners, sizeof(corners) / sizeof(corners[0]), CoordModeOrigin);
1759   if (data->rubber_band.start_x >= data->rubber_band.last_x ||
1760       data->rubber_band.start_y >= data->rubber_band.last_y)
1761     return;
1762   top.x = data->rubber_band.start_x;
1763   bot.x = data->rubber_band.last_x;
1764   top.y = data->rubber_band.start_y;
1765   bot.y = data->rubber_band.last_y;
1766   diff = data->q_max - data->q_min;
1767   delta = (double)top.y / (double)height;
1768   data->q_min += diff * delta;
1769   delta = (double)(height - bot.y) / (double)height;
1770   data->q_max -= diff * delta;
1771   diff = data->p_max - data->p_min;
1772   delta = (double)top.x / (double)width;
1773   data->p_min += diff * delta;
1774   delta = (double)(width - bot.x) / (double)width;
1775   data->p_max -= diff * delta;
1776   fflush(stdout);
1777   set_new_params(w, data);
1778 }
1779
1780 static void
1781 set_new_params(Window w, image_data_t *data)
1782 {
1783   frame = (maxframe + 1) % MAXFRAMES;
1784   if (frame > maxframe)
1785     maxframe = frame;
1786   a_range = data->p_max - data->p_min;
1787   b_range = data->q_max - data->q_min;
1788         a_minimums[frame] = min_a = data->p_min;
1789         b_minimums[frame] = min_b = data->q_min;
1790         a_inc = a_range / (double)width;
1791         b_inc = b_range / (double)height;
1792         point.x = -1;
1793         point.y = 0;
1794   run = 1;
1795         a = min_a;
1796         b = min_b;
1797         a_maximums[frame] = max_a = data->p_max;
1798         b_maximums[frame] = max_b = data->q_max;
1799   expind[frame] = 0;;
1800   Clear();
1801 }
1802
1803 static void
1804 go_down(void)
1805 {
1806   frame++;
1807   if (frame > maxframe)
1808     frame = 0;
1809   jumpwin();
1810 }
1811
1812 static void
1813 go_back(void)
1814 {
1815   frame--;
1816   if (frame < 0)
1817     frame = maxframe;
1818   jumpwin();
1819 }
1820
1821 static void
1822 jumpwin(void)
1823 {
1824   rubber_data.p_min = min_a = a_minimums[frame];
1825   rubber_data.q_min = min_b = b_minimums[frame];
1826   rubber_data.p_max = max_a = a_maximums[frame];
1827   rubber_data.q_max = max_b = b_maximums[frame];
1828   a_range = max_a - min_a;
1829   b_range = max_b - min_b;
1830         a_inc = a_range / (double)width;
1831         b_inc = b_range / (double)height;
1832         point.x = -1;
1833         point.y = 0;
1834         a = min_a;
1835         b = min_b;
1836   Clear();
1837   if (resized[frame])
1838     Redraw();
1839   else
1840     redraw(exponents[frame], expind[frame], 0);
1841 }
1842
1843 static void
1844 go_init(void)
1845 {
1846   frame = 0;
1847   jumpwin();
1848 }
1849
1850 static void
1851 Destroy_frame(void)
1852 {
1853   static int i;
1854
1855   for (i=frame; i<maxframe; i++) {
1856     exponents[frame] = exponents[frame+1];
1857     expind[frame] = expind[frame+1];
1858     a_minimums[frame] = a_minimums[frame+1];
1859     b_minimums[frame] = b_minimums[frame+1];
1860     a_maximums[frame] = a_maximums[frame+1];
1861     b_maximums[frame] = b_maximums[frame+1];
1862   }
1863   maxframe--;
1864   go_back();
1865 }
1866
1867 static void
1868 InitBuffer(void)
1869 {
1870   int i;
1871
1872   for (i = 0 ; i < maxcolor; ++i)
1873     Points.npoints[i] = 0;
1874 }
1875
1876 static void
1877 BufferPoint(Display *display, Window window, int color, int x, int y)
1878 {
1879
1880 /* Guard against bogus color values. Shouldn't be necessary but paranoia
1881    is good. */
1882   if (color < 0)
1883     color = 0;
1884   else if (color >= maxcolor)
1885     color = maxcolor - 1;
1886
1887   if (Points.npoints[color] == MAXPOINTS)
1888   {
1889     XDrawPoints(display, window, Data_GC[color],
1890         Points.data[color], Points.npoints[color], CoordModeOrigin);
1891     XDrawPoints(display, pixmap, Data_GC[color],
1892         Points.data[color], Points.npoints[color], CoordModeOrigin);
1893     Points.npoints[color] = 0;
1894   }
1895   Points.data[color][Points.npoints[color]].x = x;
1896   Points.data[color][Points.npoints[color]].y = y;
1897   ++Points.npoints[color];
1898 }
1899
1900 static void
1901 FlushBuffer(void)
1902 {
1903   int color;
1904
1905   for (color = 0; color < maxcolor; ++color)
1906     if (Points.npoints[color])
1907     {
1908         XDrawPoints(dpy, canvas, Data_GC[color],
1909           Points.data[color], Points.npoints[color],
1910           CoordModeOrigin);
1911         XDrawPoints(dpy, pixmap, Data_GC[color],
1912           Points.data[color], Points.npoints[color],
1913           CoordModeOrigin);
1914         Points.npoints[color] = 0;
1915     }
1916 }
1917
1918 static void
1919 print_help(void)
1920 {
1921     printf("During run-time, interactive control can be exerted via : \n");
1922     printf("Mouse buttons allow rubber-banding of a zoom box\n");
1923     printf("< halves the 'dwell', > doubles the 'dwell'\n");
1924     printf("[ halves the 'settle', ] doubles the 'settle'\n");
1925     printf("D flushes the drawing buffer\n");
1926     printf("e or E recalculates color indices\n");
1927     printf("f or F saves exponents to a file\n");
1928     printf("h or H or ? displays this message\n");
1929     printf("i decrements, I increments the stripe interval\n");
1930     printf("KJMN increase/decrease minimum negative exponent\n");
1931     printf("m increments the map index, changing maps\n");
1932     printf("p or P reverses the colormap for negative/positive exponents\n");
1933     printf("r redraws without recalculating\n");
1934     printf("R redraws, recalculating with new dwell and settle values\n");
1935     printf("s or S spins the colorwheel\n");
1936     printf("u pops back up to the last zoom\n");
1937     printf("U pops back up to the first picture\n");
1938     printf("v or V displays the values of various settings\n");
1939     printf("w decrements, W increments the color wheel index\n");
1940     printf("x or X clears the window\n");
1941     printf("q or Q exits\n");
1942 }
1943
1944 static void
1945 print_values(void)
1946 {
1947     static int i;
1948
1949     printf("\nminlyap=%f minexp=%f maxexp=%f\n",minlyap,minexp,maxexp);
1950     printf("width=%d height=%d\n",width,height);
1951     printf("settle=%d  dwell=%d start_x=%f\n",settle,dwell, start_x);
1952     printf("min_a=%f  a_rng=%f  max_a=%f\n",min_a,a_range,max_a);
1953     printf("min_b=%f  b_rng=%f  max_b=%f\n",min_b,b_range,max_b);
1954     if (Rflag)
1955   printf("pseudo-random forcing\n");
1956     else if (force) {
1957   printf("periodic forcing=");
1958   for (i=0;i<maxindex;i++)
1959     printf("%d",forcing[i]);
1960   printf("\n");
1961     }
1962     else
1963   printf("periodic forcing=01\n");
1964     if (Force) {
1965   printf("function forcing=");
1966   for (i=0;i<funcmaxindex;i++) {
1967     printf("%d",Forcing[i]);
1968   }
1969   printf("\n");
1970     }
1971     printf("numcolors=%d\n",numcolors-1);
1972 }
1973
1974 static void
1975 freemem(void)
1976 {
1977   static int i;
1978
1979         for (i=0;i<MAXFRAMES;i++)
1980                 free(exponents[i]);
1981 }
1982
1983 static void
1984 setupmem(void)
1985 {
1986   static int i;
1987
1988         for (i=0;i<MAXFRAMES;i++) {
1989                 if((exponents[i]=
1990                     (double *)malloc(sizeof(double)*width*height))==NULL){
1991                     fprintf(stderr,"Error malloc'ing exponent array.\n");
1992                     exit(-1);
1993                 }
1994         }
1995 }
1996
1997 static void
1998 setforcing(void)
1999 {
2000   static int i;
2001   for (i=0;i<MAXINDEX;i++)
2002     forcing[i] = (random() > prob) ? 0 : 1;
2003 }