http://ftp.x.org/contrib/applications/xscreensaver-3.18.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 static int                  color_count = 0;
54 static int                  color_index = 0;
55 static Colormap             color_map = (Colormap)NULL;
56 static XColor               colors [MAXIMUM_COLOR_COUNT];
57 static int                  line_count = 0;
58 static int                  maximum_lines = 0;
59 static double               plot_maximum_x = -1000.00;
60 static double               plot_maximum_y = -1000.00;
61 static double               plot_minimum_x =  1000.00;
62 static double               plot_minimum_y =  1000.00;
63 static int                  total_lines = 0;
64
65 /* normalize alters the sequence to go from (0,0) to (1,0) */
66 static
67 void
68 normalized_plot (int       segment_count,
69                  Segment*  segments,
70                  Position* points)
71 {
72     double   angle = 0.0;
73     double   cosine = 0.0;
74     int      index = 0;
75     double   length = 0.0;
76     double   sine = 0.0;
77     double   x = 0.0;
78     double   y = 0.0;
79
80     for (index = 0; index < segment_count; ++index)
81     {
82         Segment* segment = segments + index;
83         double length = segment->length;
84         double angle = segment->angle;
85
86         x += length * cos (angle);
87         y += length * sin (angle);
88         points [index].x = x;
89         points [index].y = y;
90     }
91     angle = -(atan2 (y, x));
92     cosine = cos (angle);
93     sine = sin (angle);
94     length = sqrt ((x * x) + (y * y));
95     /* rotate and scale */
96     for (index = 0; index < segment_count; ++index)
97     {
98         double temp_x = points [index].x;
99         double temp_y = points [index].y;
100         points [index].x = ((temp_x * cosine) + (temp_y * (-sine))) / length;
101         points [index].y = ((temp_x * sine) + (temp_y * cosine)) / length;
102     }
103 }
104
105 static
106 void
107 copy_points (int       segment_count,
108              Position* source,
109              Position* target)
110 {
111     int      index = 0;
112
113     for (index = 0; index < segment_count; ++index)
114     {
115         target [index] = source [index];
116     }
117 }
118
119 static
120 void
121 realign (double    x1,
122          double    y1,
123          double    x2,
124          double    y2,
125          int       segment_count,
126          Position* points)
127 {
128     double angle = 0.0;
129     double cosine = 0.0;
130     double delta_x = 0.0;
131     double delta_y = 0.0;
132     int    index = 0;
133     double length = 0.0;
134     double sine = 0.0;
135
136     delta_x = x2 - x1;
137     delta_y = y2 - y1;
138     angle = atan2 (delta_y, delta_x);
139     cosine = cos (angle);
140     sine = sin (angle);
141     length = sqrt ((delta_x * delta_x) + (delta_y * delta_y));
142     /* rotate, scale, then shift */
143     for (index = 0; index < segment_count; ++index)
144     {
145         double temp_x = points [index].x;
146         double temp_y = points [index].y;
147         points [index].x
148             = (length * ((temp_x * cosine) + (temp_y * (-sine)))) + x1;
149         points [index].y
150             = (length * ((temp_x * sine) + (temp_y * cosine))) + y1;
151     }
152 }
153
154 static
155 void
156 self_similar_normalized (Display*  display,
157                          Pixmap    pixmap,
158                          GC        context,
159                          int       width,
160                          int       height,
161                          int       iterations,
162                          double    x1,
163                          double    y1,
164                          double    x2,
165                          double    y2,
166                          double    maximum_x,
167                          double    maximum_y,
168                          double    minimum_x,
169                          double    minimum_y,
170                          int       segment_count,
171                          Position* points)
172 {
173     if (iterations == 0)
174     {
175         double delta_x = maximum_x - minimum_x;
176         double delta_y = maximum_y - minimum_y;
177         color_index = (int)(((double)(line_count * color_count))
178                             / ((double)total_lines));
179         ++line_count;
180         XSetForeground (display, context, colors [color_index].pixel);
181         if (plot_maximum_x < x1) plot_maximum_x = x1;
182         if (plot_maximum_x < x2) plot_maximum_x = x2;
183         if (plot_maximum_y < y1) plot_maximum_y = y1;
184         if (plot_maximum_y < y2) plot_maximum_y = y2;
185         if (plot_minimum_x > x1) plot_minimum_x = x1;
186         if (plot_minimum_x > x2) plot_minimum_x = x2;
187         if (plot_minimum_y > y1) plot_minimum_y = y1;
188         if (plot_minimum_y > y2) plot_minimum_y = y2;
189         XDrawLine (display, pixmap, context,
190                    (int)(((x1 - minimum_x) / delta_x) * width),
191                    (int)(((maximum_y - y1) / delta_y) * height),
192                    (int)(((x2 - minimum_x) / delta_x) * width),
193                    (int)(((maximum_y - y2) / delta_y) * height));
194     }
195     else
196     {
197         int       index = 0;
198         double    next_x = 0.0;
199         double    next_y = 0.0;
200         Position* replacement = (Position*)NULL;
201         double    x = 0.0;
202         double    y = 0.0;
203
204         replacement = (Position*)(alloca (segment_count * sizeof (Segment)));
205         copy_points (segment_count, points, replacement);
206         assert (fabs ((replacement [segment_count - 1].x) - 1.0) < EPSILON);
207         assert (fabs (replacement [segment_count - 1].y) < EPSILON);
208         realign (x1, y1, x2, y2, segment_count, replacement);
209         assert (fabs (x2 - (replacement [segment_count - 1].x)) < EPSILON);
210         assert (fabs (y2 - (replacement [segment_count - 1].y)) < EPSILON);
211         x = x1;
212         y = y1;
213         for (index = 0; index < segment_count; ++index)
214         {
215             next_x = replacement [index].x;
216             next_y = replacement [index].y;
217             self_similar_normalized (display, pixmap, context, width, height,
218                                      iterations - 1, x, y, next_x, next_y,
219                                      maximum_x, maximum_y,
220                                      minimum_x, minimum_y,
221                                      segment_count, points);
222             x = next_x;
223             y = next_y;
224         }
225     }
226 }
227
228 static
229 void
230 self_similar (Display* display,
231               Pixmap   pixmap,
232               GC       context,
233               int      width,
234               int      height,
235               int      iterations,
236               double   x1,
237               double   y1,
238               double   x2,
239               double   y2,
240               double   maximum_x,
241               double   maximum_y,
242               double   minimum_x,
243               double   minimum_y,
244               int      segment_count,
245               Segment* segments)
246 {
247     Position* points = (Position*)NULL;
248
249     points = (Position*)(alloca (segment_count * sizeof (Position)));
250     normalized_plot (segment_count, segments, points);
251     assert (fabs ((points [segment_count - 1].x) - 1.0) < EPSILON);
252     assert (fabs (points [segment_count - 1].y) < EPSILON);
253     self_similar_normalized (display, pixmap, context,
254                              width, height, iterations,
255                              x1, y1, x2, y2,
256                              maximum_x, maximum_y,
257                              minimum_x, minimum_y,
258                              segment_count, points);
259 }
260
261 static
262 double
263 random_double (double base,
264                double limit,
265                double epsilon)
266 {
267     double       range = 0.0;
268     unsigned int steps = 0;
269
270     assert (base < limit);
271     assert (epsilon > 0.0);
272     range = limit - base;
273     steps = (unsigned int)(floor (range / epsilon));
274     return base + ((random () % steps) * epsilon);
275 }
276
277 static
278 void
279 select_2_pattern (Segment* segments)
280 {
281     if ((random () % 2) == 0)
282     {
283         if ((random () % 2) == 0)
284         {
285             segments [0].angle  = -M_PI_4;
286             segments [0].length = M_SQRT2;
287             segments [1].angle  = M_PI_4;
288             segments [1].length = M_SQRT2;
289         }
290         else
291         {
292             segments [0].angle  = M_PI_4;
293             segments [0].length = M_SQRT2;
294             segments [1].angle  = -M_PI_4;
295             segments [1].length = M_SQRT2;
296         }
297     }
298     else
299     {
300         segments [0].angle
301             = random_double (M_PI / 6.0, M_PI / 3.0, M_PI / 180.0);
302         segments [0].length = random_double (0.25, 0.67, 0.001);
303         if ((random () % 2) == 0)
304         {
305             segments [1].angle = -(segments [0].angle);
306             segments [1].length = segments [0].length;
307         }
308         else
309         {
310             segments [1].angle = random_double ((-M_PI) / 3.0,
311                                                 (-M_PI) / 6.0,
312                                                 M_PI / 180.0);
313             segments [1].length = random_double (0.25, 0.67, 0.001);
314         }       
315     }
316 }
317
318 static
319 void
320 select_3_pattern (Segment* segments)
321 {
322     switch (random () % 5)
323     {
324      case 0:
325         if ((random () % 2) == 0)
326         {
327             segments [0].angle  = M_PI_4;
328             segments [0].length = M_SQRT2 / 4.0;
329             segments [1].angle  = -M_PI_4;
330             segments [1].length = M_SQRT2 / 2.0;
331             segments [2].angle  = M_PI_4;
332             segments [2].length = M_SQRT2 / 4.0;
333         }
334         else
335         {
336             segments [0].angle  = -M_PI_4;
337             segments [0].length = M_SQRT2 / 4.0;
338             segments [1].angle  = M_PI_4;
339             segments [1].length = M_SQRT2 / 2.0;
340             segments [2].angle  = -M_PI_4;
341             segments [2].length = M_SQRT2 / 4.0;
342         }
343         break;
344      case 1:
345         if ((random () % 2) == 0)
346         {
347             segments [0].angle  = M_PI / 6.0;
348             segments [0].length = 1.0;
349             segments [1].angle  = -M_PI_2;
350             segments [1].length = 1.0;
351             segments [2].angle  = M_PI / 6.0;
352             segments [2].length = 1.0;
353         }
354         else
355         {
356             segments [0].angle  = -M_PI / 6.0;
357             segments [0].length = 1.0;
358             segments [1].angle  = M_PI_2;
359             segments [1].length = 1.0;
360             segments [2].angle  = -M_PI / 6.0;
361             segments [2].length = 1.0;
362         }
363         break;
364      case 2:
365      case 3:
366      case 4:
367         segments [0].angle
368             = random_double (M_PI / 6.0, M_PI / 3.0, M_PI / 180.0);
369         segments [0].length = random_double (0.25, 0.67, 0.001);
370         segments [1].angle
371             = random_double (-M_PI / 3.0, -M_PI / 6.0, M_PI / 180.0);
372         segments [1].length = random_double (0.25, 0.67, 0.001);
373         if ((random () % 3) == 0)
374         {
375             if ((random () % 2) == 0)
376             {
377                 segments [2].angle = segments [0].angle;
378             }
379             else
380             {
381                 segments [2].angle = -(segments [0].angle);
382             }
383             segments [2].length = segments [0].length;
384         }
385         else
386         {
387             segments [2].angle
388                 = random_double (-M_PI / 3.0, -M_PI / 6.0, M_PI / 180.0);
389             segments [2].length = random_double (0.25, 0.67, 0.001);
390         }
391         break;
392     }
393 }
394
395 static
396 void
397 select_4_pattern (Segment* segments)
398 {
399     switch (random () % 9)
400     {
401      case 0:
402         if ((random () % 2) == 0)
403         {
404             double length = random_double (0.25, 0.50, 0.001);
405
406             segments [0].angle  = 0.0;
407             segments [0].length = 0.5;
408             segments [1].angle  = M_PI_2;
409             segments [1].length = length;
410             segments [2].angle  = -M_PI_2;
411             segments [2].length = length;
412             segments [3].angle  = 0.0;
413             segments [3].length = 0.5;
414         }
415         else
416         {
417             double length = random_double (0.25, 0.50, 0.001);
418
419             segments [0].angle  = 0.0;
420             segments [0].length = 0.5;
421             segments [1].angle  = -M_PI_2;
422             segments [1].length = length;
423             segments [2].angle  = M_PI_2;
424             segments [2].length = length;
425             segments [3].angle  = 0.0;
426             segments [3].length = 0.5;
427         }
428         break;
429      case 1:
430         if ((random () % 2) == 0)
431         {
432             segments [0].angle  = 0.0;
433             segments [0].length = 0.5;
434             segments [1].angle  = M_PI_2;
435             segments [1].length = 0.45;
436             segments [2].angle  = -M_PI_2;
437             segments [2].length = 0.45;
438             segments [3].angle  = 0.0;
439             segments [3].length = 0.5;
440         }
441         else
442         {
443             segments [0].angle  = 0.0;
444             segments [0].length = 0.5;
445             segments [1].angle  = -M_PI_2;
446             segments [1].length = 0.45;
447             segments [2].angle  = M_PI_2;
448             segments [2].length = 0.45;
449             segments [3].angle  = 0.0;
450             segments [3].length = 0.5;
451         }
452         break;
453      case 2:
454         if ((random () % 2) == 0)
455         {
456             segments [0].angle  = 0.0;
457             segments [0].length = 1.0;
458             segments [1].angle  = (5.0 * M_PI) / 12.0;
459             segments [1].length = 1.2;
460             segments [2].angle  = (-5.0 * M_PI) / 12.0;
461             segments [2].length = 1.2;
462             segments [3].angle  = 0.0;
463             segments [3].length = 1.0;
464         }
465         else
466         {
467             segments [0].angle  = 0.0;
468             segments [0].length = 1.0;
469             segments [1].angle  = (-5.0 * M_PI) / 12.0;
470             segments [1].length = 1.2;
471             segments [2].angle  = (5.0 * M_PI) / 12.0;
472             segments [2].length = 1.2;
473             segments [3].angle  = 0.0;
474             segments [3].length = 1.0;
475         }
476         break;
477      case 3:
478         if ((random () % 2) == 0)
479         {
480             double angle
481                 = random_double (M_PI / 4.0,
482                                  M_PI_2,
483                                  M_PI / 180.0);
484
485             segments [0].angle  = 0.0;
486             segments [0].length = 1.0;
487             segments [1].angle  = angle;
488             segments [1].length = 1.2;
489             segments [2].angle  = (-angle);
490             segments [2].length = 1.2;
491             segments [3].angle  = 0.0;
492             segments [3].length = 1.0;
493         }
494         else
495         {
496             double angle
497                 = random_double (M_PI / 4.0,
498                                  M_PI_2,
499                                  M_PI / 180.0);
500
501             segments [0].angle  = 0.0;
502             segments [0].length = 1.0;
503             segments [1].angle  = (-angle);
504             segments [1].length = 1.2;
505             segments [2].angle  = angle;
506             segments [2].length = 1.2;
507             segments [3].angle  = 0.0;
508             segments [3].length = 1.0;
509         }
510         break;
511      case 4:
512         if ((random () % 2) == 0)
513         {
514             double angle
515                 = random_double (M_PI / 4.0,
516                                  M_PI_2,
517                                  M_PI / 180.0);
518
519             segments [0].angle  = 0.0;
520             segments [0].length = 1.0;
521             segments [1].angle  = angle;
522             segments [1].length = 1.2;
523             segments [2].angle  = (-angle);
524             segments [2].length = 1.2;
525             segments [3].angle  = 0.0;
526             segments [3].length = 1.0;
527         }
528         else
529         {
530             double angle
531                 = random_double (M_PI / 4.0,
532                                  M_PI_2,
533                                  M_PI / 180.0);
534
535             segments [0].angle  = 0.0;
536             segments [0].length = 1.0;
537             segments [1].angle  = (-angle);
538             segments [1].length = 1.2;
539             segments [2].angle  = angle;
540             segments [2].length = 1.2;
541             segments [3].angle  = 0.0;
542             segments [3].length = 1.0;
543         }
544         break;
545      case 5:
546         if ((random () % 2) == 0)
547         {
548             double angle
549                 = random_double (M_PI / 4.0,
550                                  M_PI_2,
551                                  M_PI / 180.0);
552             double length = random_double (0.25, 0.50, 0.001);
553
554             segments [0].angle  = 0.0;
555             segments [0].length = 1.0;
556             segments [1].angle  = angle;
557             segments [1].length = length;
558             segments [2].angle  = (-angle);
559             segments [2].length = length;
560             segments [3].angle  = 0.0;
561             segments [3].length = 1.0;
562         }
563         else
564         {
565             double angle
566                 = random_double (M_PI / 4.0,
567                                  M_PI_2,
568                                  M_PI / 180.0);
569             double length = random_double (0.25, 0.50, 0.001);
570
571             segments [0].angle  = 0.0;
572             segments [0].length = 1.0;
573             segments [1].angle  = (-angle);
574             segments [1].length = length;
575             segments [2].angle  = angle;
576             segments [2].length = length;
577             segments [3].angle  = 0.0;
578             segments [3].length = 1.0;
579         }
580         break;
581      case 6:
582      case 7:
583      case 8:
584         segments [0].angle
585             = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
586         segments [0].length = random_double (0.25, 0.50, 0.001);
587         segments [1].angle
588             = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
589         segments [1].length = random_double (0.25, 0.50, 0.001);
590         if ((random () % 3) == 0)
591         {
592             segments [2].angle
593                 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
594             segments [2].length = random_double (0.25, 0.50, 0.001);
595             segments [3].angle
596                 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
597             segments [3].length = random_double (0.25, 0.50, 0.001);
598         }
599         else
600         {
601             if ((random () % 2) == 0)
602             {
603                 segments [2].angle = -(segments [1].angle);
604                 segments [2].length = segments [1].length;
605                 segments [3].angle = -(segments [0].angle);
606                 segments [3].length = segments [0].length;
607             }
608             else
609             {
610                 segments [2].angle = segments [1].angle;
611                 segments [2].length = segments [1].length;
612                 segments [3].angle = segments [0].angle;
613                 segments [3].length = segments [0].length;
614             }
615         }
616         break;
617     }
618 }
619
620 static void
621 select_pattern (int      segment_count,
622                 Segment* segments)
623 {
624     switch (segment_count)
625     {
626      case 2:
627         select_2_pattern (segments);
628         break;
629      case 3:
630         select_3_pattern (segments);
631         break;
632      case 4:
633         select_4_pattern (segments);
634         break;
635      default:
636         fprintf (stderr, "\nBad segment count, must be 2, 3, or 4.\n");
637         exit (1);
638     }
639 }
640
641 char *progclass = "Ccurve";
642
643 char *defaults [] =
644 {
645     ".delay:      1",
646     ".pause:      3",
647     ".limit: 200000",
648     0
649 };
650
651 XrmOptionDescRec options [] =
652 {
653     { "-delay", ".delay", XrmoptionSepArg, 0 },
654     { "-pause", ".pause", XrmoptionSepArg, 0 },
655     { "-limit", ".limit", XrmoptionSepArg, 0 },
656     { 0, 0, 0, 0 }
657 };
658
659 #define Y_START (0.5)
660
661 void
662 screenhack (Display* display,
663             Window   window)
664 {
665     unsigned long int    background      = 0;
666     unsigned long int    black           = 0;
667     GC                   context;
668     int                  delay           = 0;
669     int                  depth           = 0;
670     Pixmap               pixmap           = (Pixmap)NULL;
671     XWindowAttributes    hack_attributes;
672     int                  height          = 0;
673     int                  iterations      = 0;
674     int                  pause           = 0;
675     XGCValues            values;
676     unsigned long int    white           = 0;
677     int                  width           = 0;
678
679     delay = get_integer_resource ("delay", "Integer");
680     pause = get_integer_resource ("pause", "Integer");
681     maximum_lines = get_integer_resource ("limit", "Integer");
682     black = BlackPixel (display, DefaultScreen (display));
683     white = WhitePixel (display, DefaultScreen (display));
684     background = black;
685     XGetWindowAttributes (display, window, &hack_attributes);
686     width = hack_attributes.width;
687     height = hack_attributes.height;
688     depth = hack_attributes.depth;
689     color_map = hack_attributes.colormap;
690     pixmap = XCreatePixmap (display, window, width, height, depth);
691     values.foreground = white;
692     values.background = black;
693     context = XCreateGC (display, window, GCForeground | GCBackground,
694                          &values);
695     color_count = MAXIMUM_COLOR_COUNT;
696     make_color_loop (display, color_map,
697                      0,   1, 1,
698                      120, 1, 1,
699                      240, 1, 1,
700                      colors, &color_count, True, False);
701     if (color_count <= 0)
702     {
703         color_count = 1;
704         colors [0].red = colors [0].green = colors [0].blue = 0xFFFF;
705         XAllocColor (display, color_map, &colors [0]);
706     }
707
708     while (1)
709     {
710         int    index = 0;
711         double maximum_x =  1.20;
712         double maximum_y =  0.525;
713         double minimum_x = -0.20;
714         double minimum_y = -0.525;
715         static int lengths [] = { 4, 4, 4, 4, 4, 3, 3, 3, 2 };
716         int segment_count = 0;
717         Segment* segments = (Segment*)NULL;
718         double x1 = 0.0;
719         double y1 = 0.0;
720         double x2 = 1.0;
721         double y2 = 0.0;
722
723         segment_count
724             = lengths [random () % (sizeof (lengths) / sizeof (int))];
725         segments
726             = (Segment*)(alloca ((segment_count) * sizeof (Segment)));
727         select_pattern (segment_count, segments);
728         iterations = floor (log (maximum_lines)
729                                 / log (((double)(segment_count))));
730         if ((random () % 3) != 0)
731         {
732             double factor = 0.45;
733             x1 += random_double (-factor, factor, 0.001);
734             y1 += random_double (-factor, factor, 0.001);
735             x2 += random_double (-factor, factor, 0.001);
736             y2 += random_double (-factor, factor, 0.001);
737         }
738 /*      background = (random () % 2) ? black : white; */
739         for (index = 0; index < iterations; ++index)
740         {
741             double delta_x = 0.0;
742             double delta_y = 0.0;
743
744             XSetForeground (display, context, background);
745             XFillRectangle (display, pixmap, context, 0, 0, width, height);
746             line_count = 0;
747             total_lines = (int)(pow ((double)(segment_count),
748                                      (double)index));
749             plot_maximum_x = -1000.00;
750             plot_maximum_y = -1000.00;
751             plot_minimum_x =  1000.00;
752             plot_minimum_y =  1000.00;
753             self_similar (display, pixmap, context, width, height, index,
754                           x1, y1, x2, y2,
755                           maximum_x,
756                           maximum_y,
757                           minimum_x,
758                           minimum_y,
759                           segment_count, segments);
760             delta_x = plot_maximum_x - plot_minimum_x;
761             delta_y = plot_maximum_y - plot_minimum_y;
762             maximum_x = plot_maximum_x + (delta_x * 0.2);
763             maximum_y = plot_maximum_y + (delta_y * 0.2);
764             minimum_x = plot_minimum_x - (delta_x * 0.2);
765             minimum_y = plot_minimum_y - (delta_y * 0.2);
766             delta_x = maximum_x - minimum_x;
767             delta_y = maximum_y - minimum_y;
768             if ((delta_y / delta_x) > (((double)height) / ((double)width)))
769             {
770                 double new_delta_x
771                     = (delta_y * ((double)width)) / ((double)height);
772                 minimum_x -= (new_delta_x - delta_x) / 2.0;
773                 maximum_x += (new_delta_x - delta_x) / 2.0;
774             }
775             else
776             {
777                 double new_delta_y
778                     = (delta_x * ((double)height)) / ((double)width);
779                 minimum_y -= (new_delta_y - delta_y) / 2.0;
780                 maximum_y += (new_delta_y - delta_y) / 2.0;
781             }
782             XCopyArea (display, pixmap, window, context, 0, 0, width, height,
783                        0, 0);
784             if (delay) sleep (delay);
785             screenhack_handle_events (display);
786         }
787         if (pause) sleep (pause);
788         erase_full_window (display, window);
789     }
790 }