From http://www.jwz.org/xscreensaver/xscreensaver-5.37.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
258       icon = hs->hs_icon;
259       resize = hs->hs_resize;
260       if (icon || !(hs->roted | resize))
261         goto skip1;
262
263       {
264         float observer_z;
265         float unit_scale;
266         float offset_x;
267         float offset_y;
268         double az, bz, cz, dz;
269         double sum_z;
270         double ax, bx, cx, dx;
271         double sum_x;
272         double ay, by, cy, dy;
273         double sum_y;
274         struct point_state *ps;
275         int old_x;
276         int old_y;
277         double mul;
278         double xf;
279         double yf;
280         int new_x;
281         int new_y;
282         int mov;
283
284
285 #define compute(as,bs,cs,ds,i) \
286   az = hs->hs_ref_az; bz = hs->hs_ref_bz; cz = hs->hs_ref_cz; dz = hs->hs_ref_dz; \
287   ax = hs->hs_ref_ax; bx = hs->hs_ref_bx; cx = hs->hs_ref_cx; dx = hs->hs_ref_dx; \
288   ay = hs->hs_ref_ay; by = hs->hs_ref_by; cy = hs->hs_ref_cy; dy = hs->hs_ref_dy; \
289   sum_z = as az bs bz cs cz ds dz; \
290   observer_z = hs->hs_two_observer_z; \
291   unit_scale = hs->hs_unit_scale; \
292   sum_x = as ax bs bx cs cx ds dx; \
293   sum_y = as ay bs by cs cy ds dy; \
294   ps = &hs->hs_points[i]; \
295   mul = unit_scale / (observer_z - sum_z); \
296   offset_x = hs->hs_offset_x; \
297   offset_y = hs->hs_offset_y; \
298   old_x = ps->new_x; \
299   old_y = ps->new_y; \
300   xf = sum_x * mul + offset_x; \
301   yf = sum_y * mul + offset_y; \
302   new_x = (int)rint(xf); \
303   new_y = (int)rint(yf); \
304   ps->old_x = old_x; \
305   ps->old_y = old_y; \
306   ps->new_x = new_x; \
307   ps->new_y = new_y; \
308   mov = old_x != new_x || old_y != new_y; \
309   moved[i] = mov;
310
311         compute (-, -, -, -, 0);
312         compute (-, -, -, +, 1);
313         compute (-, -, +, -, 2);
314         compute (-, -, +, +, 3);
315         compute (-, +, -, -, 4);
316         compute (-, +, -, +, 5);
317         compute (-, +, +, -, 6);
318         compute (-, +, +, +, 7);
319         compute (+, -, -, -, 8);
320         compute (+, -, -, +, 9);
321         compute (+, -, +, -, 10);
322         compute (+, -, +, +, 11);
323         compute (+, +, -, -, 12);
324         compute (+, +, -, +, 13);
325         compute (+, +, +, -, 14);
326         compute (+, +, +, +, 15);
327       }
328
329     skip1:
330       icon = hs->hs_icon;
331       redraw = hs->hs_redraw;
332       if (icon || !(hs->roted | redraw))
333         goto skip2;
334
335       {
336         int lc;
337         const struct line_info *lip;
338         int mono;
339         Window win;
340
341         lc = LINE_COUNT;
342         lip = &line_table[0];
343         mono = mono_p;
344         win = hs->hs_window;
345
346         while (--lc >= 0)
347           {
348             int ip;
349             int iq;
350             int col;
351             struct point_state *sp;
352             struct point_state *sq;
353             int mov_p;
354             int mov_q;
355             GC erase_gc;
356             GC draw_gc;
357             int p_x;
358             int p_y;
359             int q_x;
360             int q_y;
361
362             ip = lip->li_ip;
363             iq = lip->li_iq;
364             col = lip->li_color;
365             lip++;
366             mov_p = moved[ip];
367             mov_q = moved[iq];
368             if (!(redraw | mov_p | mov_q))
369               continue;
370
371             sp = &hs->hs_points[ip];
372             sq = &hs->hs_points[iq];
373
374             if (mono)
375               {
376                 erase_gc = hs->hs_color_gcs[0];
377                 draw_gc = hs->hs_color_gcs[1];
378               }
379             else
380               {
381                 erase_gc = hs->black_gc;
382                 draw_gc = hs->hs_color_gcs[col];
383               }
384
385             if (!redraw)
386               {
387                 p_x = sp->old_x;
388                 p_y = sp->old_y;
389                 q_x = sq->old_x;
390                 q_y = sq->old_y;
391                 XDrawLine (dpy, win, erase_gc, p_x, p_y, q_x, q_y);
392               }
393
394             p_x = sp->new_x;
395             p_y = sp->new_y;
396             q_x = sq->new_x;
397             q_y = sq->new_y;
398             XDrawLine (dpy, win, draw_gc, p_x, p_y, q_x, q_y);
399           }
400       }
401
402     skip2:
403       stop = hs->hs_stop;
404       hs->roted = 0;
405       if (stop)
406         goto skip3;
407
408       hs->roted = 1;
409
410       {
411         double cos_a;
412         double sin_a;
413         double old_u;
414         double old_v;
415         double new_u;
416         double new_v;
417
418  /* If you get error messages about the following forms, and you think you're
419     using an ANSI C conforming compiler, then you're mistaken.  Possibly you're
420     mixing an ANSI compiler with a non-ANSI preprocessor, or vice versa.
421     Regardless, your system is broken; it's not a bug in this program.
422   */
423 #if defined(__STDC__) || defined(__ANSI_CPP__)
424
425 #define rotate(name,dim0,dim1) \
426   old_u = hs->hs_ref_##name##dim0; \
427   old_v = hs->hs_ref_##name##dim1; \
428   new_u = old_u * cos_a + old_v * sin_a; \
429   new_v = old_v * cos_a - old_u * sin_a; \
430   hs->hs_ref_##name##dim0 = new_u; \
431   hs->hs_ref_##name##dim1 = new_v;
432
433 #define rotates(dim0,dim1) \
434   if (hs->hs_sin_##dim0##dim1 != 0) { \
435     cos_a = hs->hs_cos_##dim0##dim1; \
436     sin_a = hs->hs_sin_##dim0##dim1; \
437     rotate(a,dim0,dim1); \
438     rotate(b,dim0,dim1); \
439     rotate(c,dim0,dim1); \
440     rotate(d,dim0,dim1); \
441   }
442
443 #else /* !__STDC__, courtesy of Andreas Luik <luik@isa.de> */
444
445 #define rotate(name,dim0,dim1) \
446   old_u = hs->hs_ref_/**/name/**/dim0; \
447   old_v = hs->hs_ref_/**/name/**/dim1; \
448   new_u = old_u * cos_a + old_v * sin_a; \
449   new_v = old_v * cos_a - old_u * sin_a; \
450   hs->hs_ref_/**/name/**/dim0 = new_u; \
451   hs->hs_ref_/**/name/**/dim1 = new_v;
452
453 #define rotates(dim0,dim1) \
454   if (hs->hs_sin_/**/dim0/**/dim1 != 0) { \
455     cos_a = hs->hs_cos_/**/dim0/**/dim1; \
456     sin_a = hs->hs_sin_/**/dim0/**/dim1; \
457     rotate(a,dim0,dim1); \
458     rotate(b,dim0,dim1); \
459     rotate(c,dim0,dim1); \
460     rotate(d,dim0,dim1); \
461   }
462
463 #endif /* !__STDC__ */
464
465         rotates (x,y);
466         rotates (x,z);
467         rotates (y,z);
468         rotates (x,w);
469         rotates (y,w);
470         rotates (z,w);
471       }
472
473     skip3:
474       /* stop = hs->hs_stop; */
475       if (stop && this_delay < 10000)
476         this_delay = 10000;
477     }
478   return this_delay;
479 }
480
481
482 static Bool
483 hypercube_event (Display *dpy, Window window, void *closure, XEvent *e)
484 {
485   struct hyper_state *hs = (struct hyper_state *) closure;
486   if (e->type == ButtonPress && e->xbutton.button == 2)
487     {
488       hs->hs_stop = !hs->hs_stop;
489       return True;
490     }
491   return False;
492 }
493
494 static void
495 hypercube_reshape (Display *dpy, Window window, void *closure,
496                    unsigned int w, unsigned int h)
497 {
498   struct hyper_state *hs = (struct hyper_state *) closure;
499   set_sizes (hs, w, h);
500   XClearWindow (dpy, window);
501 }
502
503
504 static void
505 set_sizes (struct hyper_state *hs, int width, int height)
506 {
507   double observer_z;
508   int min_dim;
509   double var;
510   double offset_x;
511   double offset_y;
512   double unit_scale;
513
514   observer_z = 0.5 * hs->hs_two_observer_z;
515   min_dim = width < height ? width : height;
516   var = sqrt(observer_z * observer_z - 1.0);
517   offset_x = 0.5 * (double)(width - 1);
518   offset_y = 0.5 * (double)(height - 1);
519   unit_scale = 0.4 * min_dim * var;
520   hs->hs_offset_x = (float)offset_x;
521   hs->hs_offset_y = (float)offset_y;
522   hs->hs_unit_scale = (float)unit_scale;
523 }
524
525
526 /* data */
527
528 static const struct line_info line_table[LINE_COUNT] =
529 {
530     { 0, 1, 0, },
531     { 0, 2, 0, },
532     { 1, 3, 0, },
533     { 2, 3, 0, },
534     { 4, 5, 1, },
535     { 4, 6, 1, },
536     { 5, 7, 1, },
537     { 6, 7, 1, },
538     { 0, 4, 4, },
539     { 0, 8, 4, },
540     { 4, 12, 4, },
541     { 8, 12, 4, },
542     { 1, 5, 5, },
543     { 1, 9, 5, },
544     { 5, 13, 5, },
545     { 9, 13, 5, },
546     { 2, 6, 6, },
547     { 2, 10, 6, },
548     { 6, 14, 6, },
549     { 10, 14, 6, },
550     { 3, 7, 7, },
551     { 3, 11, 7, },
552     { 7, 15, 7, },
553     { 11, 15, 7, },
554     { 8, 9, 2, },
555     { 8, 10, 2, },
556     { 9, 11, 2, },
557     { 10, 11, 2, },
558     { 12, 13, 3, },
559     { 12, 14, 3, },
560     { 13, 15, 3, },
561     { 14, 15, 3, },
562 };
563
564 static void
565 hypercube_free (Display *dpy, Window window, void *closure)
566 {
567 }
568
569 XSCREENSAVER_MODULE ("HyperCube", hypercube)