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