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