From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / hacks / ccurve.c
1 /* ccurve, Copyright (c) 1998, 1999
2  *  Rick Campbell <rick@campbellcentral.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or
10  * implied warranty.
11  *
12  */
13
14 /* Draw self-similar linear fractals including the classic ``C Curve''
15  * 
16  * 16 Aug 1999  Rick Campbell <rick@campbellcentral.org>
17  *      Eliminated sub-windows-with-backing-store-double-buffering crap in
18  *      favor of drawing the new image in a pixmap and then splatting that on
19  *      the window.
20  *
21  * 19 Dec 1998  Rick Campbell <rick@campbellcentral.org>
22  *      Original version.
23  */
24
25 #include <assert.h>
26 #include <math.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <time.h>
30
31 #include "screenhack.h"
32 #include "colors.h"
33 #include "erase.h"
34
35 #define SQRT3 (1.73205080756887729353)
36 #define MAXIMUM_COLOR_COUNT (256)
37 #define EPSILON (1e-5)
38
39 typedef struct Position_struct
40 {
41     double x;
42     double y;
43 }
44 Position;
45
46 typedef struct Segment_struct
47 {
48     double angle;
49     double length;
50 }
51 Segment;
52
53 struct state {
54   Display *dpy;
55   Window window;
56
57   int                  color_count;
58   int                  color_index;
59   Colormap             color_map;
60   XColor               colors [MAXIMUM_COLOR_COUNT];
61   int                  line_count;
62   int                  maximum_lines;
63   double               plot_maximum_x;
64   double               plot_maximum_y;
65   double               plot_minimum_x;
66   double               plot_minimum_y;
67   int                  total_lines;
68
69   unsigned long int    background;
70   GC                   context;
71   Pixmap               pixmap;
72   int                  width;
73   int                  height;
74   float                delay;
75   float                delay2;
76
77   int    draw_index;
78
79   int draw_iterations;
80   double draw_maximum_x;
81   double draw_maximum_y;
82   double draw_minimum_x;
83   double draw_minimum_y;
84   int draw_segment_count;
85   Segment* draw_segments;
86   double draw_x1;
87   double draw_y1;
88   double draw_x2;
89   double draw_y2;
90 };
91
92
93
94
95 /* normalize alters the sequence to go from (0,0) to (1,0) */
96 static void
97 normalized_plot (int       segment_count,
98                  Segment*  segments,
99                  Position* points)
100 {
101     double   angle = 0.0;
102     double   cosine = 0.0;
103     int      index = 0;
104     double   length = 0.0;
105     double   sine = 0.0;
106     double   x = 0.0;
107     double   y = 0.0;
108
109     for (index = 0; index < segment_count; ++index)
110     {
111         Segment* segment = segments + index;
112         double length = segment->length;
113         double angle = segment->angle;
114
115         x += length * cos (angle);
116         y += length * sin (angle);
117         points [index].x = x;
118         points [index].y = y;
119     }
120     angle = -(atan2 (y, x));
121     cosine = cos (angle);
122     sine = sin (angle);
123     length = sqrt ((x * x) + (y * y));
124     /* rotate and scale */
125     for (index = 0; index < segment_count; ++index)
126     {
127         double temp_x = points [index].x;
128         double temp_y = points [index].y;
129         points [index].x = ((temp_x * cosine) + (temp_y * (-sine))) / length;
130         points [index].y = ((temp_x * sine) + (temp_y * cosine)) / length;
131     }
132 }
133
134 static void
135 copy_points (int       segment_count,
136              Position* source,
137              Position* target)
138 {
139     int      index = 0;
140
141     for (index = 0; index < segment_count; ++index)
142     {
143         target [index] = source [index];
144     }
145 }
146
147 static void
148 realign (double    x1,
149          double    y1,
150          double    x2,
151          double    y2,
152          int       segment_count,
153          Position* points)
154 {
155     double angle = 0.0;
156     double cosine = 0.0;
157     double delta_x = 0.0;
158     double delta_y = 0.0;
159     int    index = 0;
160     double length = 0.0;
161     double sine = 0.0;
162
163     delta_x = x2 - x1;
164     delta_y = y2 - y1;
165     angle = atan2 (delta_y, delta_x);
166     cosine = cos (angle);
167     sine = sin (angle);
168     length = sqrt ((delta_x * delta_x) + (delta_y * delta_y));
169     /* rotate, scale, then shift */
170     for (index = 0; index < segment_count; ++index)
171     {
172         double temp_x = points [index].x;
173         double temp_y = points [index].y;
174         points [index].x
175             = (length * ((temp_x * cosine) + (temp_y * (-sine)))) + x1;
176         points [index].y
177             = (length * ((temp_x * sine) + (temp_y * cosine))) + y1;
178     }
179 }
180
181 static Bool
182 self_similar_normalized (struct state *st,
183                          int       iterations,
184                          double    x1,
185                          double    y1,
186                          double    x2,
187                          double    y2,
188                          double    maximum_x,
189                          double    maximum_y,
190                          double    minimum_x,
191                          double    minimum_y,
192                          int       segment_count,
193                          Position* points)
194 {
195     if (iterations == 0)
196     {
197         double delta_x = maximum_x - minimum_x;
198         double delta_y = maximum_y - minimum_y;
199         st->color_index = (int)(((double)(st->line_count * st->color_count))
200                             / ((double)st->total_lines));
201         ++st->line_count;
202         XSetForeground (st->dpy, st->context, st->colors [st->color_index].pixel);
203         if (st->plot_maximum_x < x1) st->plot_maximum_x = x1;
204         if (st->plot_maximum_x < x2) st->plot_maximum_x = x2;
205         if (st->plot_maximum_y < y1) st->plot_maximum_y = y1;
206         if (st->plot_maximum_y < y2) st->plot_maximum_y = y2;
207         if (st->plot_minimum_x > x1) st->plot_minimum_x = x1;
208         if (st->plot_minimum_x > x2) st->plot_minimum_x = x2;
209         if (st->plot_minimum_y > y1) st->plot_minimum_y = y1;
210         if (st->plot_minimum_y > y2) st->plot_minimum_y = y2;
211         XDrawLine (st->dpy, st->pixmap, st->context,
212                    (int)(((x1 - minimum_x) / delta_x) * st->width),
213                    (int)(((maximum_y - y1) / delta_y) * st->height),
214                    (int)(((x2 - minimum_x) / delta_x) * st->width),
215                    (int)(((maximum_y - y2) / delta_y) * st->height));
216     }
217     else
218     {
219         int       index = 0;
220         double    next_x = 0.0;
221         double    next_y = 0.0;
222         Position* replacement = (Position*)NULL;
223         double    x = 0.0;
224         double    y = 0.0;
225
226         replacement = (Position*)(malloc (segment_count * sizeof (Segment)));
227         copy_points (segment_count, points, replacement);
228         assert (fabs ((replacement [segment_count - 1].x) - 1.0) < EPSILON);
229         assert (fabs (replacement [segment_count - 1].y) < EPSILON);
230         realign (x1, y1, x2, y2, segment_count, replacement);
231         /* jwz: I don't understand what these assertions are supposed to
232            be detecting, but let's just bail on the fractal instead of
233            crashing. */
234 /*      assert (fabs (x2 - (replacement [segment_count - 1].x)) < EPSILON);
235         assert (fabs (y2 - (replacement [segment_count - 1].y)) < EPSILON);*/
236         if (fabs (x2 - (replacement [segment_count - 1].x)) >= EPSILON ||
237             fabs (y2 - (replacement [segment_count - 1].y)) >= EPSILON) {
238           free (replacement);
239           return False;
240         }
241         x = x1;
242         y = y1;
243         for (index = 0; index < segment_count; ++index)
244         {
245             next_x = replacement [index].x;
246             next_y = replacement [index].y;
247             if (!self_similar_normalized (st, 
248                                      iterations - 1, x, y, next_x, next_y,
249                                      maximum_x, maximum_y,
250                                      minimum_x, minimum_y,
251                                      segment_count, points)) {
252               free(replacement);
253               return False;
254             }
255             x = next_x;
256             y = next_y;
257         }
258         free(replacement);
259     }
260     return True;
261 }
262
263 static void
264 self_similar (struct state *st,
265               Pixmap   pixmap,
266               GC       context,
267               int      width,
268               int      height,
269               int      iterations,
270               double   x1,
271               double   y1,
272               double   x2,
273               double   y2,
274               double   maximum_x,
275               double   maximum_y,
276               double   minimum_x,
277               double   minimum_y,
278               int      segment_count,
279               Segment* segments)
280 {
281     Position* points = (Position*)NULL;
282
283     points = (Position*)(malloc (segment_count * sizeof (Position)));
284     normalized_plot (segment_count, segments, points);
285     assert (fabs ((points [segment_count - 1].x) - 1.0) < EPSILON);
286     assert (fabs (points [segment_count - 1].y) < EPSILON);
287     self_similar_normalized (st, iterations,
288                              x1, y1, x2, y2,
289                              maximum_x, maximum_y,
290                              minimum_x, minimum_y,
291                              segment_count, points);
292     free((void*)points);
293 }
294
295 static
296 double
297 random_double (double base,
298                double limit,
299                double epsilon)
300 {
301     double       range = 0.0;
302     unsigned int steps = 0;
303
304     assert (base < limit);
305     assert (epsilon > 0.0);
306     range = limit - base;
307     steps = (unsigned int)(floor (range / epsilon));
308     return base + ((random () % steps) * epsilon);
309 }
310
311 static void
312 select_2_pattern (Segment* segments)
313 {
314     if ((random () % 2) == 0)
315     {
316         if ((random () % 2) == 0)
317         {
318             segments [0].angle  = -M_PI_4;
319             segments [0].length = M_SQRT2;
320             segments [1].angle  = M_PI_4;
321             segments [1].length = M_SQRT2;
322         }
323         else
324         {
325             segments [0].angle  = M_PI_4;
326             segments [0].length = M_SQRT2;
327             segments [1].angle  = -M_PI_4;
328             segments [1].length = M_SQRT2;
329         }
330     }
331     else
332     {
333         segments [0].angle
334             = random_double (M_PI / 6.0, M_PI / 3.0, M_PI / 180.0);
335         segments [0].length = random_double (0.25, 0.67, 0.001);
336         if ((random () % 2) == 0)
337         {
338             segments [1].angle = -(segments [0].angle);
339             segments [1].length = segments [0].length;
340         }
341         else
342         {
343             segments [1].angle = random_double ((-M_PI) / 3.0,
344                                                 (-M_PI) / 6.0,
345                                                 M_PI / 180.0);
346             segments [1].length = random_double (0.25, 0.67, 0.001);
347         }       
348     }
349 }
350
351 static void
352 select_3_pattern (Segment* segments)
353 {
354     switch (random () % 5)
355     {
356      case 0:
357         if ((random () % 2) == 0)
358         {
359             segments [0].angle  = M_PI_4;
360             segments [0].length = M_SQRT2 / 4.0;
361             segments [1].angle  = -M_PI_4;
362             segments [1].length = M_SQRT2 / 2.0;
363             segments [2].angle  = M_PI_4;
364             segments [2].length = M_SQRT2 / 4.0;
365         }
366         else
367         {
368             segments [0].angle  = -M_PI_4;
369             segments [0].length = M_SQRT2 / 4.0;
370             segments [1].angle  = M_PI_4;
371             segments [1].length = M_SQRT2 / 2.0;
372             segments [2].angle  = -M_PI_4;
373             segments [2].length = M_SQRT2 / 4.0;
374         }
375         break;
376      case 1:
377         if ((random () % 2) == 0)
378         {
379             segments [0].angle  = M_PI / 6.0;
380             segments [0].length = 1.0;
381             segments [1].angle  = -M_PI_2;
382             segments [1].length = 1.0;
383             segments [2].angle  = M_PI / 6.0;
384             segments [2].length = 1.0;
385         }
386         else
387         {
388             segments [0].angle  = -M_PI / 6.0;
389             segments [0].length = 1.0;
390             segments [1].angle  = M_PI_2;
391             segments [1].length = 1.0;
392             segments [2].angle  = -M_PI / 6.0;
393             segments [2].length = 1.0;
394         }
395         break;
396      case 2:
397      case 3:
398      case 4:
399         segments [0].angle
400             = random_double (M_PI / 6.0, M_PI / 3.0, M_PI / 180.0);
401         segments [0].length = random_double (0.25, 0.67, 0.001);
402         segments [1].angle
403             = random_double (-M_PI / 3.0, -M_PI / 6.0, M_PI / 180.0);
404         segments [1].length = random_double (0.25, 0.67, 0.001);
405         if ((random () % 3) == 0)
406         {
407             if ((random () % 2) == 0)
408             {
409                 segments [2].angle = segments [0].angle;
410             }
411             else
412             {
413                 segments [2].angle = -(segments [0].angle);
414             }
415             segments [2].length = segments [0].length;
416         }
417         else
418         {
419             segments [2].angle
420                 = random_double (-M_PI / 3.0, -M_PI / 6.0, M_PI / 180.0);
421             segments [2].length = random_double (0.25, 0.67, 0.001);
422         }
423         break;
424     }
425 }
426
427 static void
428 select_4_pattern (Segment* segments)
429 {
430     switch (random () % 9)
431     {
432      case 0:
433         if ((random () % 2) == 0)
434         {
435             double length = random_double (0.25, 0.50, 0.001);
436
437             segments [0].angle  = 0.0;
438             segments [0].length = 0.5;
439             segments [1].angle  = M_PI_2;
440             segments [1].length = length;
441             segments [2].angle  = -M_PI_2;
442             segments [2].length = length;
443             segments [3].angle  = 0.0;
444             segments [3].length = 0.5;
445         }
446         else
447         {
448             double length = random_double (0.25, 0.50, 0.001);
449
450             segments [0].angle  = 0.0;
451             segments [0].length = 0.5;
452             segments [1].angle  = -M_PI_2;
453             segments [1].length = length;
454             segments [2].angle  = M_PI_2;
455             segments [2].length = length;
456             segments [3].angle  = 0.0;
457             segments [3].length = 0.5;
458         }
459         break;
460      case 1:
461         if ((random () % 2) == 0)
462         {
463             segments [0].angle  = 0.0;
464             segments [0].length = 0.5;
465             segments [1].angle  = M_PI_2;
466             segments [1].length = 0.45;
467             segments [2].angle  = -M_PI_2;
468             segments [2].length = 0.45;
469             segments [3].angle  = 0.0;
470             segments [3].length = 0.5;
471         }
472         else
473         {
474             segments [0].angle  = 0.0;
475             segments [0].length = 0.5;
476             segments [1].angle  = -M_PI_2;
477             segments [1].length = 0.45;
478             segments [2].angle  = M_PI_2;
479             segments [2].length = 0.45;
480             segments [3].angle  = 0.0;
481             segments [3].length = 0.5;
482         }
483         break;
484      case 2:
485         if ((random () % 2) == 0)
486         {
487             segments [0].angle  = 0.0;
488             segments [0].length = 1.0;
489             segments [1].angle  = (5.0 * M_PI) / 12.0;
490             segments [1].length = 1.2;
491             segments [2].angle  = (-5.0 * M_PI) / 12.0;
492             segments [2].length = 1.2;
493             segments [3].angle  = 0.0;
494             segments [3].length = 1.0;
495         }
496         else
497         {
498             segments [0].angle  = 0.0;
499             segments [0].length = 1.0;
500             segments [1].angle  = (-5.0 * M_PI) / 12.0;
501             segments [1].length = 1.2;
502             segments [2].angle  = (5.0 * M_PI) / 12.0;
503             segments [2].length = 1.2;
504             segments [3].angle  = 0.0;
505             segments [3].length = 1.0;
506         }
507         break;
508      case 3:
509         if ((random () % 2) == 0)
510         {
511             double angle
512                 = random_double (M_PI / 4.0,
513                                  M_PI_2,
514                                  M_PI / 180.0);
515
516             segments [0].angle  = 0.0;
517             segments [0].length = 1.0;
518             segments [1].angle  = angle;
519             segments [1].length = 1.2;
520             segments [2].angle  = (-angle);
521             segments [2].length = 1.2;
522             segments [3].angle  = 0.0;
523             segments [3].length = 1.0;
524         }
525         else
526         {
527             double angle
528                 = random_double (M_PI / 4.0,
529                                  M_PI_2,
530                                  M_PI / 180.0);
531
532             segments [0].angle  = 0.0;
533             segments [0].length = 1.0;
534             segments [1].angle  = (-angle);
535             segments [1].length = 1.2;
536             segments [2].angle  = angle;
537             segments [2].length = 1.2;
538             segments [3].angle  = 0.0;
539             segments [3].length = 1.0;
540         }
541         break;
542      case 4:
543         if ((random () % 2) == 0)
544         {
545             double angle
546                 = random_double (M_PI / 4.0,
547                                  M_PI_2,
548                                  M_PI / 180.0);
549
550             segments [0].angle  = 0.0;
551             segments [0].length = 1.0;
552             segments [1].angle  = angle;
553             segments [1].length = 1.2;
554             segments [2].angle  = (-angle);
555             segments [2].length = 1.2;
556             segments [3].angle  = 0.0;
557             segments [3].length = 1.0;
558         }
559         else
560         {
561             double angle
562                 = random_double (M_PI / 4.0,
563                                  M_PI_2,
564                                  M_PI / 180.0);
565
566             segments [0].angle  = 0.0;
567             segments [0].length = 1.0;
568             segments [1].angle  = (-angle);
569             segments [1].length = 1.2;
570             segments [2].angle  = angle;
571             segments [2].length = 1.2;
572             segments [3].angle  = 0.0;
573             segments [3].length = 1.0;
574         }
575         break;
576      case 5:
577         if ((random () % 2) == 0)
578         {
579             double angle
580                 = random_double (M_PI / 4.0,
581                                  M_PI_2,
582                                  M_PI / 180.0);
583             double length = random_double (0.25, 0.50, 0.001);
584
585             segments [0].angle  = 0.0;
586             segments [0].length = 1.0;
587             segments [1].angle  = angle;
588             segments [1].length = length;
589             segments [2].angle  = (-angle);
590             segments [2].length = length;
591             segments [3].angle  = 0.0;
592             segments [3].length = 1.0;
593         }
594         else
595         {
596             double angle
597                 = random_double (M_PI / 4.0,
598                                  M_PI_2,
599                                  M_PI / 180.0);
600             double length = random_double (0.25, 0.50, 0.001);
601
602             segments [0].angle  = 0.0;
603             segments [0].length = 1.0;
604             segments [1].angle  = (-angle);
605             segments [1].length = length;
606             segments [2].angle  = angle;
607             segments [2].length = length;
608             segments [3].angle  = 0.0;
609             segments [3].length = 1.0;
610         }
611         break;
612      case 6:
613      case 7:
614      case 8:
615         segments [0].angle
616             = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
617         segments [0].length = random_double (0.25, 0.50, 0.001);
618         segments [1].angle
619             = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
620         segments [1].length = random_double (0.25, 0.50, 0.001);
621         if ((random () % 3) == 0)
622         {
623             segments [2].angle
624                 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
625             segments [2].length = random_double (0.25, 0.50, 0.001);
626             segments [3].angle
627                 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
628             segments [3].length = random_double (0.25, 0.50, 0.001);
629         }
630         else
631         {
632             if ((random () % 2) == 0)
633             {
634                 segments [2].angle = -(segments [1].angle);
635                 segments [2].length = segments [1].length;
636                 segments [3].angle = -(segments [0].angle);
637                 segments [3].length = segments [0].length;
638             }
639             else
640             {
641                 segments [2].angle = segments [1].angle;
642                 segments [2].length = segments [1].length;
643                 segments [3].angle = segments [0].angle;
644                 segments [3].length = segments [0].length;
645             }
646         }
647         break;
648     }
649 }
650
651 static void
652 select_pattern (int      segment_count,
653                 Segment* segments)
654 {
655     switch (segment_count)
656     {
657      case 2:
658         select_2_pattern (segments);
659         break;
660      case 3:
661         select_3_pattern (segments);
662         break;
663      case 4:
664         select_4_pattern (segments);
665         break;
666      default:
667         fprintf (stderr, "\nBad segment count, must be 2, 3, or 4.\n");
668         exit (1);
669     }
670 }
671
672 #define Y_START (0.5)
673
674 static void *
675 ccurve_init (Display *dpy, Window window)
676 {
677   struct state *st = (struct state *) calloc (1, sizeof(*st));
678     unsigned long int    black           = 0;
679     int                  depth           = 0;
680     XWindowAttributes    hack_attributes;
681     XGCValues            values;
682     unsigned long int    white           = 0;
683
684     st->dpy = dpy;
685     st->window = window;
686
687     st->delay = get_float_resource (st->dpy, "delay", "Integer");
688     st->delay2 = get_float_resource (st->dpy, "pause", "Integer");
689     st->maximum_lines = get_integer_resource (st->dpy, "limit", "Integer");
690     black = BlackPixel (st->dpy, DefaultScreen (st->dpy));
691     white = WhitePixel (st->dpy, DefaultScreen (st->dpy));
692     st->background = black;
693     XGetWindowAttributes (st->dpy, st->window, &hack_attributes);
694     st->width = hack_attributes.width;
695     st->height = hack_attributes.height;
696     depth = hack_attributes.depth;
697     st->color_map = hack_attributes.colormap;
698     st->pixmap = XCreatePixmap (st->dpy, st->window, st->width, st->height, depth);
699     values.foreground = white;
700     values.background = black;
701     st->context = XCreateGC (st->dpy, st->window, GCForeground | GCBackground,
702                          &values);
703     st->color_count = MAXIMUM_COLOR_COUNT;
704     make_color_loop (hack_attributes.screen, hack_attributes.visual,
705                      st->color_map,
706                      0,   1, 1,
707                      120, 1, 1,
708                      240, 1, 1,
709                      st->colors, &st->color_count, True, False);
710     if (st->color_count <= 0)
711     {
712         st->color_count = 1;
713         st->colors [0].red = st->colors [0].green = st->colors [0].blue = 0xFFFF;
714         XAllocColor (st->dpy, st->color_map, &st->colors [0]);
715     }
716
717     st->draw_maximum_x =  1.20;
718     st->draw_maximum_y =  0.525;
719     st->draw_minimum_x = -0.20;
720     st->draw_minimum_y = -0.525;
721     st->draw_x2 = 1.0;
722
723     return st;
724 }
725
726 static unsigned long
727 ccurve_draw (Display *dpy, Window window, void *closure)
728 {
729   struct state *st = (struct state *) closure;
730         static const int lengths [] = { 4, 4, 4, 4, 4, 3, 3, 3, 2 };
731
732         if (st->draw_index == 0)
733           {
734         st->draw_segment_count
735             = lengths [random () % (sizeof (lengths) / sizeof (int))];
736         st->draw_segments
737             = (Segment*)(malloc ((st->draw_segment_count) * sizeof (Segment)));
738         select_pattern (st->draw_segment_count, st->draw_segments);
739         st->draw_iterations = floor (log (st->maximum_lines)
740                                 / log (((double)(st->draw_segment_count))));
741         if ((random () % 3) != 0)
742         {
743             double factor = 0.45;
744             st->draw_x1 += random_double (-factor, factor, 0.001);
745             st->draw_y1 += random_double (-factor, factor, 0.001);
746             st->draw_x2 += random_double (-factor, factor, 0.001);
747             st->draw_y2 += random_double (-factor, factor, 0.001);
748         }
749 /*      background = (random () % 2) ? black : white; */
750
751         }
752
753         /* for (st->draw_index = 0; st->draw_index < st->draw_iterations; ++st->draw_index) */
754         {
755             double delta_x = 0.0;
756             double delta_y = 0.0;
757
758             XSetForeground (st->dpy, st->context, st->background);
759             XFillRectangle (st->dpy, st->pixmap, st->context, 0, 0, st->width, st->height);
760             st->line_count = 0;
761             st->total_lines = (int)(pow ((double)(st->draw_segment_count),
762                                      (double)st->draw_index));
763             st->plot_maximum_x = -1000.00;
764             st->plot_maximum_y = -1000.00;
765             st->plot_minimum_x =  1000.00;
766             st->plot_minimum_y =  1000.00;
767             self_similar (st, st->pixmap, st->context, st->width, st->height, st->draw_index,
768                           st->draw_x1, st->draw_y1, st->draw_x2, st->draw_y2,
769                           st->draw_maximum_x,
770                           st->draw_maximum_y,
771                           st->draw_minimum_x,
772                           st->draw_minimum_y,
773                           st->draw_segment_count, st->draw_segments);
774             delta_x = st->plot_maximum_x - st->plot_minimum_x;
775             delta_y = st->plot_maximum_y - st->plot_minimum_y;
776             st->draw_maximum_x = st->plot_maximum_x + (delta_x * 0.2);
777             st->draw_maximum_y = st->plot_maximum_y + (delta_y * 0.2);
778             st->draw_minimum_x = st->plot_minimum_x - (delta_x * 0.2);
779             st->draw_minimum_y = st->plot_minimum_y - (delta_y * 0.2);
780             delta_x = st->draw_maximum_x - st->draw_minimum_x;
781             delta_y = st->draw_maximum_y - st->draw_minimum_y;
782             if ((delta_y / delta_x) > (((double)st->height) / ((double)st->width)))
783             {
784                 double new_delta_x
785                     = (delta_y * ((double)st->width)) / ((double)st->height);
786                 st->draw_minimum_x -= (new_delta_x - delta_x) / 2.0;
787                 st->draw_maximum_x += (new_delta_x - delta_x) / 2.0;
788             }
789             else
790             {
791                 double new_delta_y
792                     = (delta_x * ((double)st->height)) / ((double)st->width);
793                 st->draw_minimum_y -= (new_delta_y - delta_y) / 2.0;
794                 st->draw_maximum_y += (new_delta_y - delta_y) / 2.0;
795             }
796             XCopyArea (st->dpy, st->pixmap, st->window, st->context, 0, 0, st->width, st->height,
797                        0, 0);
798         }
799         st->draw_index++;
800         /* #### mi->recursion_depth = st->draw_index; */
801
802         if (st->draw_index >= st->draw_iterations)
803           {
804             st->draw_index = 0;
805             free((void*)st->draw_segments);
806             st->draw_segments = 0;
807             return (int) (1000000 * st->delay);
808           }
809         else
810           return (int) (1000000 * st->delay2);
811 }
812
813 static void
814 ccurve_reshape (Display *dpy, Window window, void *closure, 
815                  unsigned int w, unsigned int h)
816 {
817   struct state *st = (struct state *) closure;
818   XWindowAttributes xgwa;
819   st->width = w;
820   st->height = h;
821   XGetWindowAttributes (st->dpy, st->window, &xgwa);
822   XFreePixmap (dpy, st->pixmap);
823   st->pixmap = XCreatePixmap (st->dpy, st->window, st->width, st->height,
824                               xgwa.depth);
825 }
826
827 static Bool
828 ccurve_event (Display *dpy, Window window, void *closure, XEvent *event)
829 {
830   return False;
831 }
832
833 static void
834 ccurve_free (Display *dpy, Window window, void *closure)
835 {
836 }
837
838
839 static const char *ccurve_defaults [] =
840 {
841     ".background:  black",
842     ".foreground:  white",
843     ".delay:      3",
844     ".pause:      0.4",
845     ".limit: 200000",
846 #ifdef USE_IPHONE
847     "*ignoreRotation: True",
848 #endif
849     0
850 };
851
852 static XrmOptionDescRec ccurve_options [] =
853 {
854     { "-delay", ".delay", XrmoptionSepArg, 0 },
855     { "-pause", ".pause", XrmoptionSepArg, 0 },
856     { "-limit", ".limit", XrmoptionSepArg, 0 },
857     { 0, 0, 0, 0 }
858 };
859
860 XSCREENSAVER_MODULE ("CCurve", ccurve)