began for maemo
[xscreensaver] / xscreensaver / hacks / apollonian.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* apollonian --- Apollonian Circles */
3
4 #if 0
5 static const char sccsid[] = "@(#)apollonian.c  5.02 2001/07/01 xlockmore";
6 #endif
7
8 /*-
9  * Copyright (c) 2000, 2001 by Allan R. Wilks <allan@research.att.com>.
10  *
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * radius r = 1 / c (curvature)
24  *
25  * Descartes Circle Theorem: (a, b, c, d are curvatures of tangential circles)
26  * Let a, b, c, d be the curvatures of for mutually (externally) tangent
27  * circles in the plane.  Then
28  * a^2 + b^2 + c^2 + d^2 = (a + b + c + d)^2 / 2
29  *
30  * Complex Descartes Theorem:  If the oriented curvatues and (complex) centers
31  * of an oriented Descrates configuration in the plane are a, b, c, d and
32  * w, x, y, z respectively, then
33  * a^2*w^2 + b^2*x^2 + c^2*y^2 + d^2*z^2 = (aw + bx + cy + dz)^2 / 2
34  * In addition these quantities satisfy
35  * a^2*w + b^2*x + c^2*y + d^2*z = (aw + bx + cy + dz)(a + b + c + d) /  2
36  *
37  * Enumerate root integer Descartes quadruples (a,b,c,d) satisfying the
38  * Descartes condition:
39  *      2(a^2+b^2+c^2+d^2) = (a+b+c+d)^2
40  * i.e., quadruples for which no application of the "pollinate" operator
41  *      z <- 2(a+b+c+d) - 3*z,
42  * where z is in {a,b,c,d}, gives a quad of strictly smaller sum.  This
43  * is equivalent to the condition:
44  *      sum(a,b,c,d) >= 2*max(a,b,c,d)
45  * which, because of the Descartes condition, is equivalent to
46  *      sum(a^2,b^2,c^2,d^2) >= 2*max(a,b,c,d)^2
47  *
48  *
49  * Todo:
50  * Add a small font
51  *
52  * Revision History:
53  * 25-Jun-2001: Converted from C and Postscript code by David Bagley 
54  *              Original code by Allan R. Wilks <allan@research.att.com>.
55  *
56  * From Circle Math Science News April 21, 2001 VOL. 254-255
57  * http://www.sciencenews.org/20010421/toc.asp
58  * Apollonian Circle Packings Assorted papers from Ronald L Graham,
59  * Jeffrey Lagarias, Colin Mallows, Allan Wilks, Catherine Yan
60  *      http://front.math.ucdavis.edu/math.NT/0009113
61  *      http://front.math.ucdavis.edu/math.MG/0101066
62  *      http://front.math.ucdavis.edu/math.MG/0010298
63  *      http://front.math.ucdavis.edu/math.MG/0010302
64  *      http://front.math.ucdavis.edu/math.MG/0010324
65  */
66
67 #ifdef STANDALONE
68 # define MODE_apollonian
69 # define DEFAULTS       "*delay:   1000000 \n" \
70                                         "*count:   64      \n" \
71                                         "*cycles:  20      \n" \
72                                         "*ncolors: 64      \n"
73 # define refresh_apollonian 0
74 # define reshape_apollonian 0
75 # define apollonian_handle_event 0
76 # include "xlockmore.h"         /* in xscreensaver distribution */
77 # include "erase.h"
78 #else /* STANDALONE */
79 # include "xlock.h"             /* in xlockmore distribution */
80 #endif /* STANDALONE */
81
82 #ifdef MODE_apollonian
83
84 #define DEF_ALTGEOM  "True"
85 #define DEF_LABEL  "True"
86
87 static Bool altgeom;
88 static Bool label;
89
90 static XrmOptionDescRec opts[] =
91 {
92         {"-altgeom", ".apollonian.altgeom", XrmoptionNoArg, "on"},
93         {"+altgeom", ".apollonian.altgeom", XrmoptionNoArg, "off"},
94         {"-label", ".apollonian.label", XrmoptionNoArg, "on"},
95         {"+label", ".apollonian.label", XrmoptionNoArg, "off"},
96 };
97 static argtype vars[] =
98 {
99         {&altgeom, "altgeom", "AltGeom", DEF_ALTGEOM, t_Bool},
100         {&label,   "label",   "Label",   DEF_LABEL,   t_Bool},
101 };
102 static OptionStruct desc[] =
103 {
104         {"-/+altgeom", "turn on/off alternate geometries (off euclidean space, on includes spherical and hyperbolic)"},
105         {"-/+label", "turn on/off alternate space and number labeling"},
106 };
107
108 ENTRYPOINT ModeSpecOpt apollonian_opts =
109 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
110
111 #ifdef DOFONT
112 extern XFontStruct *getFont(Display * display);
113 #endif
114
115 #ifdef USE_MODULES
116 ModStruct   apollonian_description =
117 {"apollonian", "init_apollonian", "draw_apollonian", "release_apollonian",
118  "init_apollonian", "init_apollonian", (char *) NULL, &apollonian_opts,
119  1000000, 64, 20, 1, 64, 1.0, "",
120  "Shows Apollonian Circles", 0, NULL};
121
122 #endif
123
124 typedef struct {
125         int a, b, c, d;
126 } apollonian_quadruple;
127
128 typedef struct {
129         double e;       /* euclidean bend */
130         double s;       /* spherical bend */
131         double h;       /* hyperbolic bend */
132         double x, y;    /* euclidean bend times euclidean position */
133 } circle;
134 enum space {
135   euclidean = 0, spherical, hyperbolic
136 };
137
138 static const char * space_string[] = {
139   "euclidean",
140   "spherical",
141   "hyperbolic"
142 };
143
144 /*
145 Generate Apollonian packing starting with a quadruple of circles.
146 The four input lines each contain the 5-tuple (e,s,h,x,y) representing
147 the circle with radius 1/e and center (x/e,y/e).  The s and h is propagated
148 like e, x and y, but can differ from e so as to represent different
149 geometries, spherical and hyperbolic, respectively.  The "standard" picture,
150 for example (-1, 2, 2, 3), can be labeled for the three geometries.
151 Origins of circles z1, z2, z3, z4
152 a * z1 = 0
153 b * z2 = (a+b)/a
154 c * z3 = (q123 + a * i)^2/(a*(a+b)) where q123 = sqrt(a*b+a*c+b*c)
155 d * z4 = (q124 + a * i)^2/(a*(a+b)) where q124 = q123 - a - b
156 If (e,x,y) represents the Euclidean circle (1/e,x/e,y/e) (so that e is
157 the label in the standard picture) then the "spherical label" is
158 (e^2+x^2+y^2-1)/(2*e) (an integer!)  and the "hyperbolic label", is 
159 calulated by h + s = e.
160 */
161 static circle examples[][4] = {
162 { /* double semi-bounded */
163         { 0, 0, 0,   0,  1},
164         { 0, 0, 0,   0, -1},
165         { 1, 1, 1,  -1,  0},
166         { 1, 1, 1,   1,  0}
167 },
168 #if 0
169 { /* standard */
170         {-1, 0, -1,   0,  0},
171         { 2, 1,  1,   1,  0},
172         { 2, 1,  1,  -1,  0},
173         { 3, 2,  1,   0,  2}
174 },
175 { /* next simplest */
176         {-2, -1, -1,   0.0,  0},
177         { 3,  2,  1,   0.5,  0},
178         { 6,  3,  3,  -2.0,  0},
179         { 7,  4,  3,  -1.5,  2}
180 },
181 { /*  */
182         {-3, -2, -1,         0.0,  0},
183         { 4,  3,  1,   1.0 / 3.0,  0},
184         {12,  7,  5,        -3.0,  0},
185         {13,  8,  5,  -8.0 / 3.0,  2}
186 },
187 { /* Mickey */
188         {-3, -2, -1,         0.0,  0},
189         { 5,  4,  1,   2.0 / 3.0,  0},
190         { 8,  5,  3,  -4.0 / 3.0, -1},
191         { 8,  5,  3,  -4.0 / 3.0,  1}
192 },
193 { /*  */
194         {-4, -3, -1,   0.00,  0},
195         { 5,  4,  1,   0.25,  0},
196         {20, 13,  7,  -4.00,  0},
197         {21, 14,  7,  -3.75,  2}
198 },
199 { /* Mickey2 */
200         {-4, -2, -2,    0.0,  0},
201         { 8,  4,  4,    1.0,  0},
202         { 9,  5,  4,  -0.75, -1},
203         { 9,  5,  4,  -0.75,  1}
204 },
205 { /* Mickey3 */
206         {-5,  -4, -1,   0.0,  0},
207         { 7,   6,  1,   0.4,  0},
208         {18,  13,  5,  -2.4, -1},
209         {18,  13,  5,  -2.4,  1}
210 },
211 { /*  */
212         {-6, -5, -1,          0.0,  0},
213         { 7,  6,  1,    1.0 / 6.0,  0},
214         {42, 31, 11,         -6.0,  0},
215         {43, 32, 11,  -35.0 / 6.0,  2}
216 },
217 { /*  */
218         {-6, -3, -3,         0.0,  0},
219         {10,  5,  5,   2.0 / 3.0,  0},
220         {15,  8,  7,        -1.5,  0},
221         {19, 10,  9,  -5.0 / 6.0,  2}
222 },
223 { /* asymmetric */
224         {-6, -5, -1,           0.0,  0.0},
225         {11, 10,  1,     5.0 / 6.0,  0.0},
226         {14, 11,  3,  -16.0 / 15.0, -0.8},
227         {15, 12,  3,          -0.9,  1.2} 
228 },
229 #endif
230 /* Non integer stuff */
231 #define DELTA 2.154700538 /* ((3+2*sqrt(3))/3) */
232 { /* 3 fold symmetric bounded (x, y calculated later) */
233         {   -1,    -1,    -1,   0.0,  0.0},
234         {DELTA, DELTA, DELTA,   1.0,  0.0},
235         {DELTA, DELTA, DELTA,   1.0, -1.0},
236         {DELTA, DELTA, DELTA,  -1.0,  1.0} 
237 },
238 { /* semi-bounded (x, y calculated later) */
239 #define ALPHA 2.618033989 /* ((3+sqrt(5))/2) */
240         {              1.0,               1.0,               1.0,   0,  0},
241         {              0.0,               0.0,               0.0,   0, -1},
242         {1.0/(ALPHA*ALPHA), 1.0/(ALPHA*ALPHA), 1.0/(ALPHA*ALPHA),  -1,  0},
243         {        1.0/ALPHA,         1.0/ALPHA,         1.0/ALPHA,  -1,  0}
244 },
245 { /* unbounded (x, y calculated later) */
246 /* #define PHI 1.618033989 *//* ((1+sqrt(5))/2) */
247 #define BETA 2.890053638 /* (PHI+sqrt(PHI)) */
248         {                 1.0,                  1.0,                  1.0,  0,  0},
249         {1.0/(BETA*BETA*BETA), 1.0/(BETA*BETA*BETA), 1.0/(BETA*BETA*BETA),  1,  0},
250         {     1.0/(BETA*BETA),      1.0/(BETA*BETA),      1.0/(BETA*BETA),  1,  0},
251         {            1.0/BETA,             1.0/BETA,             1.0/BETA,  1,  0}
252 }
253 };
254
255 #define PREDEF_CIRCLE_GAMES  (sizeof (examples) / (4 * sizeof (circle)))
256
257 #if 0
258 Euclidean
259 0, 0, 1, 1
260 -1, 2, 2, 3
261 -2, 3, 6, 7
262 -3, 5, 8, 8
263 -4, 8, 9, 9
264 -3, 4, 12, 13
265 -6, 11, 14, 15
266  -5, 7, 18, 18
267  -6, 10, 15, 19
268  -7, 12, 17, 20
269  -4, 5, 20, 21
270  -9, 18, 19, 22
271  -8, 13, 21, 24
272 Spherical
273 0, 1, 1, 2
274  -1, 2, 3, 4
275  -2, 4, 5, 5
276  -2, 3, 7, 8
277 Hyperbolic
278 -1, 1, 1, 1
279  0, 0, 1, 3
280  -2, 3, 5, 6
281  -3, 6, 6, 7
282 #endif
283
284 typedef struct {
285         int         size;
286         XPoint      offset;
287         int         geometry;
288         circle      c1, c2, c3, c4;
289         int         color_offset;
290         int         count;
291         Bool        label, altgeom;
292         apollonian_quadruple  *quad;
293 #ifdef DOFONT
294         XFontStruct *font;
295 #endif
296         int         time;
297         int         game;
298 #ifdef STANDALONE
299   eraser_state *eraser;
300 #endif
301 } apollonianstruct;
302
303 static apollonianstruct *apollonians = (apollonianstruct *) NULL;
304
305 #define FONT_HEIGHT 19
306 #define FONT_WIDTH 15
307 #define FONT_LENGTH 20
308 #define MAX_CHAR 10
309 #define K       2.15470053837925152902  /* 1+2/sqrt(3) */
310 #define MAXBEND 100 /* Do not want configurable by user since it will take too
311         much time if increased. */
312
313 static int
314 gcd(int a, int b)
315 {
316         int r;
317
318         while (b) {
319                 r = a % b;
320                 a = b;
321                 b = r;
322         }
323         return a;
324 }
325
326 static int
327 isqrt(int n)
328 {
329         int y;
330
331         if (n < 0)
332                 return -1;
333         y = (int) (sqrt((double) n) + 0.5);
334         return ((n == y*y) ? y : -1);
335 }
336
337 static void
338 dquad(int n, apollonian_quadruple *quad)
339 {
340         int a, b, c, d;
341         int counter = 0, B, C;
342         
343         for (a = 0; a < MAXBEND; a++) {
344                 B = (int) (K * a);
345                 for (b = a + 1; b <= B; b++) {
346                         C = (int) (((a + b) * (a + b)) / (4.0 * (b - a)));
347                         for (c = b; c <= C; c++) {
348                                 d = isqrt(b*c-a*(b+c));
349                                 if (d >= 0 && (gcd(a,gcd(b,c)) <= 1)) {
350                                         quad[counter].a = -a;
351                                         quad[counter].b = b;
352                                         quad[counter].c = c;
353                                         quad[counter].d = -a+b+c-2*d;
354                                         if (++counter >= n) {
355                                                 return;
356                                         }
357                                 }
358                         }
359                 }
360         }
361         (void) printf("found only %d below maximum bend of %d\n",
362                 counter, MAXBEND);
363         for (; counter < n; counter++) {
364                 quad[counter].a = -1; 
365                 quad[counter].b = 2;
366                 quad[counter].c = 2;
367                 quad[counter].d = 3;
368         }
369         return;
370 }
371
372 /*
373  * Given a Descartes quadruple of bends (a,b,c,d), with a<0, find a
374  * quadruple of circles, represented by (bend,bend*x,bend*y), such
375  * that the circles have the given bends and the bends times the
376  * centers are integers.
377  *
378  * This just performs an exaustive search, assuming that the outer
379  * circle has center in the unit square.
380  *
381  * It is always sufficient to look in {(x,y):0<=y<=x<=1/2} for the
382  * center of the outer circle, but this may not lead to a packing
383  * that can be labelled with integer spherical and hyperbolic labels.
384  * To effect the smaller search, replace FOR(a) with
385  *
386  *      for (pa = ea/2; pa <= 0; pa++) for (qa = pa; qa <= 0; qa++)
387  */
388
389 #define For(v,l,h)      for (v = l; v <= h; v++)
390 #define FOR(z)          For(p##z,lop##z,hip##z) For(q##z,loq##z,hiq##z)
391 #define H(z)            ((e##z*e##z+p##z*p##z+q##z*q##z)%2)
392 #define UNIT(z)         ((abs(e##z)-1)*(abs(e##z)-1) >= p##z*p##z+q##z*q##z)
393 #define T(z,w)          is_tangent(e##z,p##z,q##z,e##w,p##w,q##w)
394 #define LO(r,z)         lo##r##z = iceil(e##z*(r##a+1),ea)-1
395 #define HI(r,z)         hi##r##z = iflor(e##z*(r##a-1),ea)-1
396 #define B(z)            LO(p,z); HI(p,z); LO(q,z); HI(q,z)
397
398 static int
399 is_quad(int a, int b, int c, int d)
400 {
401         int s;
402
403         s = a+b+c+d;
404         return 2*(a*a+b*b+c*c+d*d) == s*s;
405 }
406
407 static Bool
408 is_tangent(int e1, int p1, int q1, int e2, int p2, int q2)
409 {
410         int dx, dy, s;
411
412         dx = p1*e2 - p2*e1;
413         dy = q1*e2 - q2*e1;
414         s = e1 + e2;
415         return dx*dx + dy*dy == s*s;
416 }
417
418 static int
419 iflor(int a, int b)
420 {
421         int q;
422
423         if (b == 0) {
424                 (void) printf("iflor: b = 0\n");
425                 return 0;
426         }
427         if (a%b == 0)
428                 return a/b;
429         q = abs(a)/abs(b);
430         return ((a<0)^(b<0)) ? -q-1 : q;
431 }
432
433 static int
434 iceil(int a, int b)
435 {
436         int q;
437
438         if (b == 0) {
439                 (void) printf("iceil: b = 0\n");
440                 return 0;
441         }
442         if (a%b == 0)
443                 return a/b;
444         q = abs(a)/abs(b);
445         return ((a<0)^(b<0)) ? -q : 1+q;
446 }
447
448 static double
449 geom(int geometry, int e, int p, int q)
450 {
451         int g = (geometry == spherical) ? -1 :
452                 (geometry == hyperbolic) ? 1 : 0;
453
454         if (g)
455                 return (e*e + (1.0 - p*p - q*q) * g) / (2.0*e);
456         (void) printf("geom: g = 0\n");
457         return e;
458 }
459
460 static void
461 cquad(circle *c1, circle *c2, circle *c3, circle *c4)
462 {
463         int ea, eb, ec, ed;
464         int pa, pb, pc, pd;
465         int qa, qb, qc, qd;
466         int lopa, lopb, lopc, lopd;
467         int hipa, hipb, hipc, hipd;
468         int loqa, loqb, loqc, loqd;
469         int hiqa, hiqb, hiqc, hiqd;
470
471         ea = (int) c1->e;
472         eb = (int) c2->e;
473         ec = (int) c3->e;
474         ed = (int) c4->e;
475         if (ea >= 0)
476                 (void) printf("ea = %d\n", ea);
477         if (!is_quad(ea,eb,ec,ed))
478                 (void) printf("Error not quad %d %d %d %d\n", ea, eb, ec, ed);
479         lopa = loqa = ea;
480         hipa = hiqa = 0;
481         FOR(a) {
482                 B(b); B(c); B(d);
483                 if (H(a) && UNIT(a)) FOR(b) {
484                         if (H(b) && T(a,b)) FOR(c) {
485                                 if (H(c) && T(a,c) && T(b,c)) FOR(d) {
486                                   if (H(d) && T(a,d) && T(b,d) && T(c,d)) {
487                                     c1->s = geom(spherical, ea, pa, qa);
488                                     c1->h = geom(hyperbolic, ea, pa, qa);
489                                     c2->s = geom(spherical, eb, pb, qb);
490                                     c2->h = geom(hyperbolic, eb, pb, qb);
491                                     c3->s = geom(spherical, ec, pc, qc);
492                                     c3->h = geom(hyperbolic, ec, pc, qc);
493                                     c4->s = geom(spherical, ed, pd, qd);
494                                     c4->h = geom(hyperbolic, ed, pd, qd);
495                                   }
496                                 }
497                         }
498                 }
499         }
500 }
501
502 static void
503 p(ModeInfo *mi, circle c)
504 {
505         apollonianstruct *cp = &apollonians[MI_SCREEN(mi)];
506         char string[10];
507         double g, e;
508         int g_width;
509
510 #ifdef DEBUG
511         (void) printf("c.e=%g c.s=%g c.h=%g  c.x=%g c.y=%g\n",
512                 c.e, c.s, c.h, c.x, c.y);
513 #endif
514         g = (cp->geometry == spherical) ? c.s : (cp->geometry == hyperbolic) ?
515                 c.h : c.e;
516         if (c.e < 0.0) {
517                 if (g < 0.0)
518                         g = -g;
519                 if (MI_NPIXELS(mi) <= 2)
520                         XSetForeground(MI_DISPLAY(mi), MI_GC(mi),
521                                 MI_WHITE_PIXEL(mi));
522                 else
523                         XSetForeground(MI_DISPLAY(mi), MI_GC(mi),
524                                 MI_PIXEL(mi, ((int) ((g + cp->color_offset) *
525                                         g)) % MI_NPIXELS(mi)));
526                 XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
527                         ((int) (cp->size * (-cp->c1.e) * (c.x - 1.0) /
528                                 (-2.0 * c.e) + cp->size / 2.0 + cp->offset.x)),
529                         ((int) (cp->size * (-cp->c1.e) * (c.y - 1.0) /
530                                 (-2.0 * c.e) + cp->size / 2.0 + cp->offset.y)),
531                         (int) (cp->c1.e * cp->size / c.e),
532                         (int) (cp->c1.e * cp->size / c.e), 0, 23040);
533                 if (!cp->label) {
534 #ifdef DEBUG
535                         (void) printf("%g\n", -g);
536 #endif
537                         return;
538                 }
539                 (void) sprintf(string, "%g", (g == 0.0) ? 0 : -g);
540                 if (cp->size >= 10 * FONT_WIDTH) {
541                   /* hard code these to corners */
542                   XDrawString(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
543                         ((int) (cp->size * c.x / (2.0 * c.e))) + cp->offset.x,
544                         ((int) (cp->size * c.y / (2.0 * c.e))) + FONT_HEIGHT,
545                         string, (g == 0.0) ? 1 : ((g < 10.0) ? 2 :
546                                 ((g < 100.0) ? 3 : 4)));
547                 }
548                 if (cp->altgeom && MI_HEIGHT(mi) >= 30 * FONT_WIDTH) {
549                   XDrawString(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
550                         ((int) (cp->size * c.x / (2.0 * c.e) + cp->offset.x)),
551                         ((int) (cp->size * c.y / (2.0 * c.e) + MI_HEIGHT(mi) -
552                         FONT_HEIGHT / 2)), (char *) space_string[cp->geometry],
553                         strlen(space_string[cp->geometry]));
554                 }
555                 return;
556         }
557         if (MI_NPIXELS(mi) <= 2)
558                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
559         else
560                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi),
561                         MI_PIXEL(mi, ((int) ((g + cp->color_offset) * g)) %
562                                 MI_NPIXELS(mi)));
563         if (c.e == 0.0) {
564                 if (c.x == 0.0 && c.y != 0.0) {
565                         XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
566                                 0, (int) ((c.y + 1.0) * cp->size / 2.0 + cp->offset.y),
567                                 MI_WIDTH(mi),
568                                 (int) ((c.y + 1.0) * cp->size / 2.0 + cp->offset.y));
569                 } else if (c.y == 0.0 && c.x != 0.0) {
570                         XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
571                                 (int) ((c.x + 1.0) * cp->size / 2.0 + cp->offset.x), 0,
572                                 (int) ((c.x + 1.0) * cp->size / 2.0 + cp->offset.x),
573                                 MI_HEIGHT(mi));
574                 }
575                 return;
576         }
577         e = (cp->c1.e >= 0.0) ? 1.0 : -cp->c1.e;
578         XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
579                 ((int) (cp->size * e * (c.x - 1.0) / (2.0 * c.e) +
580                         cp->size / 2.0 + cp->offset.x)),
581                 ((int) (cp->size * e * (c.y - 1.0) / (2.0 * c.e) +
582                         cp->size / 2.0 + cp->offset.y)),
583                 (int) (e * cp->size / c.e), (int) (e * cp->size / c.e),
584                 0, 23040);
585         if (!cp->label) {
586 #ifdef DEBUG
587                 (void) printf("%g\n", g);
588 #endif
589                 return;
590         }
591         if (MI_NPIXELS(mi) <= 2)
592                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
593         else
594                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi),
595                         MI_PIXEL(mi, ((int) ((g + cp->color_offset) * g) +
596                                 MI_NPIXELS(mi) / 2) % MI_NPIXELS(mi)));
597         g_width = (g < 10.0) ? 1: ((g < 100.0) ? 2 : 3);
598         if (c.e < e * cp->size / (FONT_LENGTH + 5 * g_width) && g < 1000.0) {
599                 (void) sprintf(string, "%g", g);
600                 XDrawString(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
601                         ((int) (cp->size * e * c.x / (2.0 * c.e) +
602                                 cp->size / 2.0 + cp->offset.x)) -
603                                 g_width * FONT_WIDTH / 2,
604                         ((int) (cp->size * e * c.y / (2.0 * c.e) +
605                                 cp->size / 2.0 + cp->offset.y)) +
606                                 FONT_HEIGHT / 2,
607                         string, g_width);
608         }
609 }
610
611 #define BIG 7
612 static void
613 f(ModeInfo *mi, circle c1, circle c2, circle c3, circle c4)
614 {
615         apollonianstruct *cp = &apollonians[MI_SCREEN(mi)];
616         int e = (int) ((cp->c1.e >= 0.0) ? 1.0 : -cp->c1.e);
617         circle c;
618
619         c.e = 2*(c1.e+c2.e+c3.e) - c4.e;
620         c.s = 2*(c1.s+c2.s+c3.s) - c4.s;
621         c.h = 2*(c1.h+c2.h+c3.h) - c4.h;
622         c.x = 2*(c1.x+c2.x+c3.x) - c4.x;
623         c.y = 2*(c1.y+c2.y+c3.y) - c4.y;
624         if (c.e > cp->size * e || c.x / c.e > BIG || c.y / c.e > BIG ||
625             c.x / c.e < -BIG || c.y / c.e < -BIG)
626                 return;
627         p(mi, c);
628         f(mi, c2, c3, c, c1);
629         f(mi, c1, c3, c, c2);
630         f(mi, c1, c2, c, c3);
631 }
632
633 ENTRYPOINT void
634 free_apollonian (Display *display, apollonianstruct *cp)
635 {
636         if (cp->quad != NULL) {
637                 (void) free((void *) cp->quad);
638                 cp->quad = (apollonian_quadruple *) NULL;
639         }
640 #ifdef DOFONT
641         if (cp->gc != None) {
642                 XFreeGC(display, cp->gc);
643                 cp->gc = None;
644         }
645         if (cp->font != None) {
646                 XFreeFont(display, cp->font);
647                 cp->font = None;
648         }
649 #endif
650 }
651
652 #ifndef DEBUG
653 static void
654 randomize_c(int randomize, circle * c)
655 {
656   if (randomize / 2) {
657     double temp;
658
659     temp = c->x;
660     c->x = c->y;
661     c->y = temp;
662   }
663   if (randomize % 2) {
664     c->x = -c->x;
665     c->y = -c->y;
666   }
667 }
668 #endif
669
670 ENTRYPOINT void
671 init_apollonian (ModeInfo * mi)
672 {
673         apollonianstruct *cp;
674         int i;
675
676         if (apollonians == NULL) {
677                 if ((apollonians = (apollonianstruct *) calloc(MI_NUM_SCREENS(mi),
678                                              sizeof (apollonianstruct))) == NULL)
679                         return;
680         }
681         cp = &apollonians[MI_SCREEN(mi)];
682
683         cp->size = MAX(MIN(MI_WIDTH(mi), MI_HEIGHT(mi)) - 1, 1);
684         cp->offset.x = (MI_WIDTH(mi) - cp->size) / 2;
685         cp->offset.y = (MI_HEIGHT(mi) - cp->size) / 2;
686         cp->color_offset = NRAND(MI_NPIXELS(mi));
687
688 #ifdef DOFONT
689         if (cp->font == None) {
690                 if ((cp->font = getFont(MI_DISPLAY(mi))) == None)
691                         return False;
692         }
693 #endif
694         cp->label = label;
695         cp->altgeom = cp->label && altgeom;
696
697         if (cp->quad == NULL) {
698                 cp->count = ABS(MI_COUNT(mi));
699                 if ((cp->quad = (apollonian_quadruple *) malloc(cp->count *
700                         sizeof (apollonian_quadruple))) == NULL) {
701                         return;
702                 }
703                 dquad(cp->count, cp->quad);
704         }
705         cp->game = NRAND(PREDEF_CIRCLE_GAMES + cp->count);
706         cp->geometry = (cp->game && cp->altgeom) ? NRAND(3) : 0;
707
708         if (cp->game < PREDEF_CIRCLE_GAMES) {   
709                 cp->c1 = examples[cp->game][0];
710                 cp->c2 = examples[cp->game][1];
711                 cp->c3 = examples[cp->game][2];
712                 cp->c4 = examples[cp->game][3];
713                 /* do not label non int */
714                 cp->label = cp->label && (cp->c4.e == (int) cp->c4.e);
715         } else { /* uses results of dquad, all int */
716                 i = cp->game - PREDEF_CIRCLE_GAMES;
717                 cp->c1.e = cp->quad[i].a;
718                 cp->c2.e = cp->quad[i].b;
719                 cp->c3.e = cp->quad[i].c;
720                 cp->c4.e = cp->quad[i].d;
721                 if (cp->geometry)
722                         cquad(&(cp->c1), &(cp->c2), &(cp->c3), &(cp->c4));
723         }
724         cp->time = 0;
725 #ifndef STANDALONE
726         MI_CLEARWINDOW(mi);
727 #endif
728         if (cp->game != 0) {
729                 double q123;
730
731                 if (cp->c1.e == 0.0 || cp->c1.e == -cp->c2.e)
732                         return;
733                 cp->c1.x = 0.0;
734                 cp->c1.y = 0.0;
735                 cp->c2.x = -(cp->c1.e + cp->c2.e) / cp->c1.e;
736                 cp->c2.y = 0;
737                 q123 = sqrt(cp->c1.e * cp->c2.e + cp->c1.e * cp->c3.e +
738                         cp->c2.e * cp->c3.e);
739 #ifdef DEBUG
740                 (void) printf("q123 = %g, ", q123);
741 #endif
742                 cp->c3.x = (cp->c1.e * cp->c1.e - q123 * q123) / (cp->c1.e *
743                         (cp->c1.e + cp->c2.e));
744                 cp->c3.y = -2.0 * q123 / (cp->c1.e + cp->c2.e);
745                 q123 = -cp->c1.e - cp->c2.e + q123;
746                 cp->c4.x = (cp->c1.e * cp->c1.e - q123 * q123) / (cp->c1.e *
747                         (cp->c1.e + cp->c2.e));
748                 cp->c4.y = -2.0 * q123 / (cp->c1.e + cp->c2.e);
749 #ifdef DEBUG
750                 (void) printf("q124 = %g\n", q123);
751                 (void) printf("%g %g %g %g %g %g %g %g\n",
752                         cp->c1.x, cp->c1.y, cp->c2.x, cp->c2.y,
753                         cp->c3.x, cp->c3.y, cp->c4.x, cp->c4.y);
754 #endif
755         }
756 #ifndef DEBUG
757         if (LRAND() & 1) {
758                 cp->c3.y = -cp->c3.y;
759                 cp->c4.y = -cp->c4.y;
760         }
761         i = NRAND(4);
762         randomize_c(i, &(cp->c1));
763         randomize_c(i, &(cp->c2));
764         randomize_c(i, &(cp->c3));
765         randomize_c(i, &(cp->c4));
766 #endif 
767 }
768
769 ENTRYPOINT void
770 draw_apollonian (ModeInfo * mi)
771 {
772         apollonianstruct *cp;
773
774         if (apollonians == NULL)
775                 return;
776         cp = &apollonians[MI_SCREEN(mi)];
777
778 #ifdef STANDALONE
779     if (cp->eraser) {
780       cp->eraser = erase_window (MI_DISPLAY(mi), MI_WINDOW(mi), cp->eraser);
781       return;
782     }
783 #endif
784
785         MI_IS_DRAWN(mi) = True;
786
787         if (cp->time < 5) {
788                 switch (cp->time) {
789                 case 0:
790                         p(mi, cp->c1);
791                         p(mi, cp->c2);
792                         p(mi, cp->c3);
793                         p(mi, cp->c4);
794                         break;
795                 case 1:
796                         f(mi, cp->c1, cp->c2, cp->c3, cp->c4);
797                         break;
798                 case 2:
799                         f(mi, cp->c1, cp->c2, cp->c4, cp->c3);
800                         break;
801                 case 3:
802                         f(mi, cp->c1, cp->c3, cp->c4, cp->c2);
803                         break;
804                 case 4:
805                         f(mi, cp->c2, cp->c3, cp->c4, cp->c1);
806                 }
807         }
808         if (++cp->time > MI_CYCLES(mi))
809       {
810 #ifdef STANDALONE
811         cp->eraser = erase_window (MI_DISPLAY(mi), MI_WINDOW(mi), cp->eraser);
812 #endif /* STANDALONE */
813                 init_apollonian(mi);
814       }
815 }
816
817 ENTRYPOINT void
818 release_apollonian (ModeInfo * mi)
819 {
820         if (apollonians != NULL) {
821                 int         screen;
822
823                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
824                         free_apollonian(MI_DISPLAY(mi), &apollonians[screen]);
825                 (void) free((void *) apollonians);
826                 apollonians = (apollonianstruct *) NULL;
827         }
828 }
829
830 XSCREENSAVER_MODULE ("Apollonian", apollonian)
831
832 #endif /* MODE_apollonian */