From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / hypercube.c
1 /* xscreensaver, Copyright (c) 1992-2008 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * This code derived from TI Explorer Lisp code by Joe Keane, Fritz Mueller,
12  * and Jamie Zawinski.
13  */
14
15 #include <math.h>
16 #include "screenhack.h"
17
18 #define POINT_COUNT 16
19 #define LINE_COUNT 32
20
21 #define ANGLE_SCALE 0.001
22
23 struct line_info
24 {
25   char li_ip;
26   char li_iq;
27   char li_color;
28   char li_pad;
29 };
30
31 struct point_state
32 {
33   short old_x, old_y;
34   short new_x, new_y;
35 };
36
37 struct hyper_state
38 {
39   char hs_stop;
40   char hs_icon;
41   char hs_resize;
42   char hs_redraw;
43   Display *hs_display;
44   Window hs_window;
45   float hs_two_observer_z;
46   float hs_offset_x;
47   float hs_offset_y;
48   float hs_unit_scale;
49   int hs_delay;
50   GC hs_color_gcs[8];
51   GC black_gc;
52 #if 0
53   double hs_angle_xy;
54   double hs_angle_xz;
55   double hs_angle_yz;
56   double hs_angle_xw;
57   double hs_angle_yw;
58   double hs_angle_zw;
59 #endif
60   double hs_cos_xy, hs_sin_xy;
61   double hs_cos_xz, hs_sin_xz;
62   double hs_cos_yz, hs_sin_yz;
63   double hs_cos_xw, hs_sin_xw;
64   double hs_cos_yw, hs_sin_yw;
65   double hs_cos_zw, hs_sin_zw;
66   double hs_ref_ax, hs_ref_ay, hs_ref_az, hs_ref_aw;
67   double hs_ref_bx, hs_ref_by, hs_ref_bz, hs_ref_bw;
68   double hs_ref_cx, hs_ref_cy, hs_ref_cz, hs_ref_cw;
69   double hs_ref_dx, hs_ref_dy, hs_ref_dz, hs_ref_dw;
70   struct point_state hs_points[POINT_COUNT];
71   int roted;
72 };
73
74 static const struct line_info line_table[LINE_COUNT];
75
76 static const char *hypercube_defaults[] =
77 {
78   "*observer-z: 3.0",
79   "*delay: 10000",
80   "*xy: 3",
81   "*xz: 5",
82   "*yw: 10",
83   "*yz: 0",
84   "*xw: 0",
85   "*zw: 0",
86   ".background: black",
87   ".foreground: white",
88   "*fpsSolid:   true",
89   "*color0:     magenta",
90   "*color3:     #FF0093",
91   "*color1:     yellow",
92   "*color2:     #FF9300",
93   "*color4:     green",
94   "*color7:     #00FFD0",
95   "*color5:     #8080FF",
96   "*color6:     #00D0FF",
97
98   0
99 };
100
101 static XrmOptionDescRec hypercube_options [] =
102 {
103   { "-color0",          ".color0",      XrmoptionSepArg, 0 },
104   { "-color1",          ".color1",      XrmoptionSepArg, 0 },
105   { "-color2",          ".color2",      XrmoptionSepArg, 0 },
106   { "-color3",          ".color3",      XrmoptionSepArg, 0 },
107   { "-color4",          ".color4",      XrmoptionSepArg, 0 },
108   { "-color5",          ".color5",      XrmoptionSepArg, 0 },
109   { "-color6",          ".color6",      XrmoptionSepArg, 0 },
110   { "-color7",          ".color7",      XrmoptionSepArg, 0 },
111
112   { "-xw",              ".xw",          XrmoptionSepArg, 0 },
113   { "-xy",              ".xy",          XrmoptionSepArg, 0 },
114   { "-xz",              ".xz",          XrmoptionSepArg, 0 },
115   { "-yw",              ".yw",          XrmoptionSepArg, 0 },
116   { "-yz",              ".yz",          XrmoptionSepArg, 0 },
117   { "-zw",              ".zw",          XrmoptionSepArg, 0 },
118
119   { "-observer-z",      ".observer-z",  XrmoptionSepArg, 0 },
120   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
121   { 0, 0, 0, 0 }
122 };
123
124
125 static void set_sizes (struct hyper_state *hs, int width, int height);
126
127 static void *
128 hypercube_init (Display *dpy, Window win)
129 {
130   XGCValues gcv;
131   Colormap cmap;
132   unsigned long bg_pixel;
133   int delay;
134   float observer_z;
135
136   struct hyper_state *hs = (struct hyper_state *) calloc (1, sizeof(*hs));
137   hs->hs_display = dpy;
138   hs->hs_window = win;
139
140   observer_z = get_float_resource (dpy, "observer-z", "Float");
141   if (observer_z < 1.125)
142     observer_z = 1.125;
143   /* hs->hs_observer_z = observer_z; */
144   hs->hs_two_observer_z = 2.0 * observer_z;
145
146   {
147     XWindowAttributes wa;
148     XGetWindowAttributes (dpy, win, &wa);
149     cmap = wa.colormap;
150     set_sizes (hs, wa.width, wa.height);
151   }
152
153   delay = get_integer_resource (dpy, "delay", "Integer");
154   hs->hs_delay = delay;
155
156   bg_pixel = get_pixel_resource (dpy, cmap, "background", "Background");
157
158   if (mono_p)
159     {
160       GC black_gc;
161       unsigned long fg_pixel;
162       GC white_gc;
163
164       gcv.function = GXcopy;
165       gcv.foreground = bg_pixel;
166       black_gc = XCreateGC (dpy, win, GCForeground|GCFunction, &gcv);
167       fg_pixel = get_pixel_resource (dpy, cmap, "foreground", "Foreground");
168       gcv.foreground = fg_pixel ^ bg_pixel;
169       white_gc = XCreateGC (dpy, win, GCForeground|GCFunction, &gcv);
170       hs->hs_color_gcs[0] = black_gc;
171       hs->hs_color_gcs[1] = white_gc;
172     }
173   else
174     {
175       int col;
176
177       gcv.function = GXcopy;
178
179       gcv.foreground = get_pixel_resource (dpy, cmap,
180                                            "background", "Background");
181       hs->black_gc = XCreateGC (dpy, win, GCForeground|GCFunction, &gcv);
182
183       for (col = 0; col < 8; col++)
184         {
185           char buffer[16];
186           unsigned long fg_pixel;
187           GC color_gc;
188
189           sprintf (buffer, "color%d", col);
190           fg_pixel = get_pixel_resource (dpy, cmap, buffer, "Foreground");
191           gcv.foreground = fg_pixel;
192           color_gc = XCreateGC (dpy, win, GCForeground|GCFunction, &gcv);
193           hs->hs_color_gcs[col] = color_gc;
194         }
195     }
196
197   hs->hs_ref_ax = 1.0, hs->hs_ref_ay = 0.0, hs->hs_ref_az = 0.0, hs->hs_ref_aw = 0.0;
198   hs->hs_ref_bx = 0.0, hs->hs_ref_by = 1.0, hs->hs_ref_bz = 0.0, hs->hs_ref_bw = 0.0;
199   hs->hs_ref_cx = 0.0, hs->hs_ref_cy = 0.0, hs->hs_ref_cz = 1.0, hs->hs_ref_cw = 0.0;
200   hs->hs_ref_dx = 0.0, hs->hs_ref_dy = 0.0, hs->hs_ref_dz = 0.0, hs->hs_ref_dw = 1.0;
201
202   {
203   double xy;
204   double xz;
205   double yz;
206   double xw;
207   double yw;
208   double zw;
209   double cos_xy, sin_xy;
210   double cos_xz, sin_xz;
211   double cos_yz, sin_yz;
212   double cos_xw, sin_xw;
213   double cos_yw, sin_yw;
214   double cos_zw, sin_zw;
215
216   xy = get_float_resource (dpy, "xy", "Float") * ANGLE_SCALE;
217   xz = get_float_resource (dpy, "xz", "Float") * ANGLE_SCALE;
218   yz = get_float_resource (dpy, "yz", "Float") * ANGLE_SCALE;
219   xw = get_float_resource (dpy, "xw", "Float") * ANGLE_SCALE;
220   yw = get_float_resource (dpy, "yw", "Float") * ANGLE_SCALE;
221   zw = get_float_resource (dpy, "zw", "Float") * ANGLE_SCALE;
222
223   cos_xy = cos (xy), sin_xy = sin (xy);
224   hs->hs_cos_xy = cos_xy, hs->hs_sin_xy = sin_xy;
225   cos_xz = cos (xz), sin_xz = sin (xz);
226   hs->hs_cos_xz = cos_xz, hs->hs_sin_xz = sin_xz;
227   cos_yz = cos (yz), sin_yz = sin (yz);
228   hs->hs_cos_yz = cos_yz, hs->hs_sin_yz = sin_yz;
229   cos_xw = cos (xw), sin_xw = sin (xw);
230   hs->hs_cos_xw = cos_xw, hs->hs_sin_xw = sin_xw;
231   cos_yw = cos (yw), sin_yw = sin (yw);
232   hs->hs_cos_yw = cos_yw, hs->hs_sin_yw = sin_yw;
233   cos_zw = cos (zw), sin_zw = sin (zw);
234   hs->hs_cos_zw = cos_zw, hs->hs_sin_zw = sin_zw;
235   }
236
237   return hs;
238 }
239
240
241 static unsigned long
242 hypercube_draw (Display *dpy, Window window, void *closure)
243 {
244   struct hyper_state *hs = (struct hyper_state *) closure;
245   int this_delay = hs->hs_delay;
246
247 #ifdef HAVE_JWXYZ       /* Don't second-guess Quartz's double-buffering */
248   XClearWindow (dpy, window);
249 #endif
250
251     {
252       int icon;
253       int resize;
254       char moved[POINT_COUNT];
255       int redraw;
256       int stop;
257       int delay;
258
259       icon = hs->hs_icon;
260       resize = hs->hs_resize;
261       if (icon || !(hs->roted | resize))
262         goto skip1;
263
264       {
265         float observer_z;
266         float unit_scale;
267         float offset_x;
268         float offset_y;
269         double az, bz, cz, dz;
270         double sum_z;
271         double ax, bx, cx, dx;
272         double sum_x;
273         double ay, by, cy, dy;
274         double sum_y;
275         struct point_state *ps;
276         int old_x;
277         int old_y;
278         double mul;
279         double xf;
280         double yf;
281         int new_x;
282         int new_y;
283         int mov;
284
285
286 #define compute(as,bs,cs,ds,i) \
287   az = hs->hs_ref_az; bz = hs->hs_ref_bz; cz = hs->hs_ref_cz; dz = hs->hs_ref_dz; \
288   ax = hs->hs_ref_ax; bx = hs->hs_ref_bx; cx = hs->hs_ref_cx; dx = hs->hs_ref_dx; \
289   ay = hs->hs_ref_ay; by = hs->hs_ref_by; cy = hs->hs_ref_cy; dy = hs->hs_ref_dy; \
290   sum_z = as az bs bz cs cz ds dz; \
291   observer_z = hs->hs_two_observer_z; \
292   unit_scale = hs->hs_unit_scale; \
293   sum_x = as ax bs bx cs cx ds dx; \
294   sum_y = as ay bs by cs cy ds dy; \
295   ps = &hs->hs_points[i]; \
296   mul = unit_scale / (observer_z - sum_z); \
297   offset_x = hs->hs_offset_x; \
298   offset_y = hs->hs_offset_y; \
299   old_x = ps->new_x; \
300   old_y = ps->new_y; \
301   xf = sum_x * mul + offset_x; \
302   yf = sum_y * mul + offset_y; \
303   new_x = (int)rint(xf); \
304   new_y = (int)rint(yf); \
305   ps->old_x = old_x; \
306   ps->old_y = old_y; \
307   ps->new_x = new_x; \
308   ps->new_y = new_y; \
309   mov = old_x != new_x || old_y != new_y; \
310   moved[i] = mov;
311
312         compute (-, -, -, -, 0);
313         compute (-, -, -, +, 1);
314         compute (-, -, +, -, 2);
315         compute (-, -, +, +, 3);
316         compute (-, +, -, -, 4);
317         compute (-, +, -, +, 5);
318         compute (-, +, +, -, 6);
319         compute (-, +, +, +, 7);
320         compute (+, -, -, -, 8);
321         compute (+, -, -, +, 9);
322         compute (+, -, +, -, 10);
323         compute (+, -, +, +, 11);
324         compute (+, +, -, -, 12);
325         compute (+, +, -, +, 13);
326         compute (+, +, +, -, 14);
327         compute (+, +, +, +, 15);
328       }
329
330     skip1:
331       icon = hs->hs_icon;
332       redraw = hs->hs_redraw;
333       if (icon || !(hs->roted | redraw))
334         goto skip2;
335
336       {
337         int lc;
338         const struct line_info *lip;
339         int mono;
340         Window win;
341
342         lc = LINE_COUNT;
343         lip = &line_table[0];
344         mono = mono_p;
345         win = hs->hs_window;
346
347         while (--lc >= 0)
348           {
349             int ip;
350             int iq;
351             int col;
352             struct point_state *sp;
353             struct point_state *sq;
354             int mov_p;
355             int mov_q;
356             GC erase_gc;
357             GC draw_gc;
358             int p_x;
359             int p_y;
360             int q_x;
361             int q_y;
362
363             ip = lip->li_ip;
364             iq = lip->li_iq;
365             col = lip->li_color;
366             lip++;
367             mov_p = moved[ip];
368             mov_q = moved[iq];
369             if (!(redraw | mov_p | mov_q))
370               continue;
371
372             sp = &hs->hs_points[ip];
373             sq = &hs->hs_points[iq];
374
375             if (mono)
376               {
377                 erase_gc = hs->hs_color_gcs[0];
378                 draw_gc = hs->hs_color_gcs[1];
379               }
380             else
381               {
382                 erase_gc = hs->black_gc;
383                 draw_gc = hs->hs_color_gcs[col];
384               }
385
386             if (!redraw)
387               {
388                 p_x = sp->old_x;
389                 p_y = sp->old_y;
390                 q_x = sq->old_x;
391                 q_y = sq->old_y;
392                 XDrawLine (dpy, win, erase_gc, p_x, p_y, q_x, q_y);
393               }
394
395             p_x = sp->new_x;
396             p_y = sp->new_y;
397             q_x = sq->new_x;
398             q_y = sq->new_y;
399             XDrawLine (dpy, win, draw_gc, p_x, p_y, q_x, q_y);
400           }
401       }
402
403     skip2:
404       stop = hs->hs_stop;
405       hs->roted = 0;
406       if (stop)
407         goto skip3;
408
409       hs->roted = 1;
410
411       {
412         double cos_a;
413         double sin_a;
414         double old_u;
415         double old_v;
416         double new_u;
417         double new_v;
418
419  /* If you get error messages about the following forms, and you think you're
420     using an ANSI C conforming compiler, then you're mistaken.  Possibly you're
421     mixing an ANSI compiler with a non-ANSI preprocessor, or vice versa.
422     Regardless, your system is broken; it's not a bug in this program.
423   */
424 #if defined(__STDC__) || defined(__ANSI_CPP__)
425
426 #define rotate(name,dim0,dim1) \
427   old_u = hs->hs_ref_##name##dim0; \
428   old_v = hs->hs_ref_##name##dim1; \
429   new_u = old_u * cos_a + old_v * sin_a; \
430   new_v = old_v * cos_a - old_u * sin_a; \
431   hs->hs_ref_##name##dim0 = new_u; \
432   hs->hs_ref_##name##dim1 = new_v;
433
434 #define rotates(dim0,dim1) \
435   if (hs->hs_sin_##dim0##dim1 != 0) { \
436     cos_a = hs->hs_cos_##dim0##dim1; \
437     sin_a = hs->hs_sin_##dim0##dim1; \
438     rotate(a,dim0,dim1); \
439     rotate(b,dim0,dim1); \
440     rotate(c,dim0,dim1); \
441     rotate(d,dim0,dim1); \
442   }
443
444 #else /* !__STDC__, courtesy of Andreas Luik <luik@isa.de> */
445
446 #define rotate(name,dim0,dim1) \
447   old_u = hs->hs_ref_/**/name/**/dim0; \
448   old_v = hs->hs_ref_/**/name/**/dim1; \
449   new_u = old_u * cos_a + old_v * sin_a; \
450   new_v = old_v * cos_a - old_u * sin_a; \
451   hs->hs_ref_/**/name/**/dim0 = new_u; \
452   hs->hs_ref_/**/name/**/dim1 = new_v;
453
454 #define rotates(dim0,dim1) \
455   if (hs->hs_sin_/**/dim0/**/dim1 != 0) { \
456     cos_a = hs->hs_cos_/**/dim0/**/dim1; \
457     sin_a = hs->hs_sin_/**/dim0/**/dim1; \
458     rotate(a,dim0,dim1); \
459     rotate(b,dim0,dim1); \
460     rotate(c,dim0,dim1); \
461     rotate(d,dim0,dim1); \
462   }
463
464 #endif /* !__STDC__ */
465
466         rotates (x,y);
467         rotates (x,z);
468         rotates (y,z);
469         rotates (x,w);
470         rotates (y,w);
471         rotates (z,w);
472       }
473
474     skip3:
475       /* stop = hs->hs_stop; */
476       delay = hs->hs_delay;
477       if (stop && this_delay < 10000)
478         this_delay = 10000;
479     }
480   return this_delay;
481 }
482
483
484 static Bool
485 hypercube_event (Display *dpy, Window window, void *closure, XEvent *e)
486 {
487   struct hyper_state *hs = (struct hyper_state *) closure;
488   if (e->type == ButtonPress && e->xbutton.button == 2)
489     {
490       hs->hs_stop = !hs->hs_stop;
491       return True;
492     }
493   return False;
494 }
495
496 static void
497 hypercube_reshape (Display *dpy, Window window, void *closure,
498                    unsigned int w, unsigned int h)
499 {
500   struct hyper_state *hs = (struct hyper_state *) closure;
501   set_sizes (hs, w, h);
502   XClearWindow (dpy, window);
503 }
504
505
506 static void
507 set_sizes (struct hyper_state *hs, int width, int height)
508 {
509   double observer_z;
510   int min_dim;
511   double var;
512   double offset_x;
513   double offset_y;
514   double unit_scale;
515
516   observer_z = 0.5 * hs->hs_two_observer_z;
517   min_dim = width < height ? width : height;
518   var = sqrt(observer_z * observer_z - 1.0);
519   offset_x = 0.5 * (double)(width - 1);
520   offset_y = 0.5 * (double)(height - 1);
521   unit_scale = 0.4 * min_dim * var;
522   hs->hs_offset_x = (float)offset_x;
523   hs->hs_offset_y = (float)offset_y;
524   hs->hs_unit_scale = (float)unit_scale;
525 }
526
527
528 /* data */
529
530 static const struct line_info line_table[LINE_COUNT] =
531 {
532     { 0, 1, 0, },
533     { 0, 2, 0, },
534     { 1, 3, 0, },
535     { 2, 3, 0, },
536     { 4, 5, 1, },
537     { 4, 6, 1, },
538     { 5, 7, 1, },
539     { 6, 7, 1, },
540     { 0, 4, 4, },
541     { 0, 8, 4, },
542     { 4, 12, 4, },
543     { 8, 12, 4, },
544     { 1, 5, 5, },
545     { 1, 9, 5, },
546     { 5, 13, 5, },
547     { 9, 13, 5, },
548     { 2, 6, 6, },
549     { 2, 10, 6, },
550     { 6, 14, 6, },
551     { 10, 14, 6, },
552     { 3, 7, 7, },
553     { 3, 11, 7, },
554     { 7, 15, 7, },
555     { 11, 15, 7, },
556     { 8, 9, 2, },
557     { 8, 10, 2, },
558     { 9, 11, 2, },
559     { 10, 11, 2, },
560     { 12, 13, 3, },
561     { 12, 14, 3, },
562     { 13, 15, 3, },
563     { 14, 15, 3, },
564 };
565
566 static void
567 hypercube_free (Display *dpy, Window window, void *closure)
568 {
569 }
570
571 XSCREENSAVER_MODULE ("HyperCube", hypercube)