ftp://ftp.jp.xemacs.org/pub/NetBSD/packages/distfiles/xscreensaver-4.15.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 | ButtonPressMask);
178     
179     width = wa.width;
180     height = wa.height;
181     cmap = wa.colormap;
182     set_sizes (hs, width, height);
183   }
184
185   delay = get_integer_resource ("delay", "Integer");
186   hs->hs_delay = delay;
187
188   bg_pixel = get_pixel_resource ("background", "Background", dpy, cmap);
189
190   if (mono_p)
191     {
192       GC black_gc;
193       unsigned long fg_pixel;
194       GC white_gc;
195
196       gcv.function = GXcopy;
197       gcv.foreground = bg_pixel;
198       black_gc = XCreateGC (dpy, win, GCForeground|GCFunction, &gcv);
199       fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
200       gcv.foreground = fg_pixel ^ bg_pixel;
201       white_gc = XCreateGC (dpy, win, GCForeground|GCFunction, &gcv);
202       hs->hs_color_gcs[0] = black_gc;
203       hs->hs_color_gcs[1] = white_gc;
204     }
205   else
206     {
207       int col;
208
209       gcv.function = GXxor;
210       for (col = 0; col < 8; col++)
211         {
212           char buffer[16];
213           unsigned long fg_pixel;
214           GC color_gc;
215
216           sprintf (buffer, "color%d", col);
217           fg_pixel = get_pixel_resource (buffer, "Foreground", dpy, cmap);
218           gcv.foreground = fg_pixel ^ bg_pixel;
219           color_gc = XCreateGC (dpy, win, GCForeground|GCFunction, &gcv);
220           hs->hs_color_gcs[col] = color_gc;
221         }
222     }
223
224   hs->hs_ref_ax = 1.0, hs->hs_ref_ay = 0.0, hs->hs_ref_az = 0.0, hs->hs_ref_aw = 0.0;
225   hs->hs_ref_bx = 0.0, hs->hs_ref_by = 1.0, hs->hs_ref_bz = 0.0, hs->hs_ref_bw = 0.0;
226   hs->hs_ref_cx = 0.0, hs->hs_ref_cy = 0.0, hs->hs_ref_cz = 1.0, hs->hs_ref_cw = 0.0;
227   hs->hs_ref_dx = 0.0, hs->hs_ref_dy = 0.0, hs->hs_ref_dz = 0.0, hs->hs_ref_dw = 1.0;
228
229   {
230   double xy;
231   double xz;
232   double yz;
233   double xw;
234   double yw;
235   double zw;
236   double cos_xy, sin_xy;
237   double cos_xz, sin_xz;
238   double cos_yz, sin_yz;
239   double cos_xw, sin_xw;
240   double cos_yw, sin_yw;
241   double cos_zw, sin_zw;
242
243   xy = get_float_resource ("xy", "Float") * ANGLE_SCALE;
244   xz = get_float_resource ("xz", "Float") * ANGLE_SCALE;
245   yz = get_float_resource ("yz", "Float") * ANGLE_SCALE;
246   xw = get_float_resource ("xw", "Float") * ANGLE_SCALE;
247   yw = get_float_resource ("yw", "Float") * ANGLE_SCALE;
248   zw = get_float_resource ("zw", "Float") * ANGLE_SCALE;
249
250   cos_xy = cos (xy), sin_xy = sin (xy);
251   hs->hs_cos_xy = cos_xy, hs->hs_sin_xy = sin_xy;
252   cos_xz = cos (xz), sin_xz = sin (xz);
253   hs->hs_cos_xz = cos_xz, hs->hs_sin_xz = sin_xz;
254   cos_yz = cos (yz), sin_yz = sin (yz);
255   hs->hs_cos_yz = cos_yz, hs->hs_sin_yz = sin_yz;
256   cos_xw = cos (xw), sin_xw = sin (xw);
257   hs->hs_cos_xw = cos_xw, hs->hs_sin_xw = sin_xw;
258   cos_yw = cos (yw), sin_yw = sin (yw);
259   hs->hs_cos_yw = cos_yw, hs->hs_sin_yw = sin_yw;
260   cos_zw = cos (zw), sin_zw = sin (zw);
261   hs->hs_cos_zw = cos_zw, hs->hs_sin_zw = sin_zw;
262   }
263 }
264
265
266 static void
267 hyper (struct hyper_state *hs)
268 {
269   int roted;
270
271   roted = 0;
272
273   for (;;)
274     {
275       int icon;
276       int resize;
277       char moved[POINT_COUNT];
278       int redraw;
279       int stop;
280       int delay;
281
282       check_events (hs);
283
284       icon = hs->hs_icon;
285       resize = hs->hs_resize;
286       if (icon || !(roted | resize))
287         goto skip1;
288
289       {
290         float observer_z;
291         float unit_scale;
292         float offset_x;
293         float offset_y;
294         double az, bz, cz, dz;
295         double sum_z;
296         double ax, bx, cx, dx;
297         double sum_x;
298         double ay, by, cy, dy;
299         double sum_y;
300         struct point_state *ps;
301         int old_x;
302         int old_y;
303         double mul;
304         double xf;
305         double yf;
306         int new_x;
307         int new_y;
308         int mov;
309
310
311 #define compute(as,bs,cs,ds,i) \
312   az = hs->hs_ref_az; bz = hs->hs_ref_bz; cz = hs->hs_ref_cz; dz = hs->hs_ref_dz; \
313   ax = hs->hs_ref_ax; bx = hs->hs_ref_bx; cx = hs->hs_ref_cx; dx = hs->hs_ref_dx; \
314   ay = hs->hs_ref_ay; by = hs->hs_ref_by; cy = hs->hs_ref_cy; dy = hs->hs_ref_dy; \
315   sum_z = as az bs bz cs cz ds dz; \
316   observer_z = hs->hs_two_observer_z; \
317   unit_scale = hs->hs_unit_scale; \
318   sum_x = as ax bs bx cs cx ds dx; \
319   sum_y = as ay bs by cs cy ds dy; \
320   ps = &hs->hs_points[i]; \
321   mul = unit_scale / (observer_z - sum_z); \
322   offset_x = hs->hs_offset_x; \
323   offset_y = hs->hs_offset_y; \
324   old_x = ps->new_x; \
325   old_y = ps->new_y; \
326   xf = sum_x * mul + offset_x; \
327   yf = sum_y * mul + offset_y; \
328   new_x = (int)rint(xf); \
329   new_y = (int)rint(yf); \
330   ps->old_x = old_x; \
331   ps->old_y = old_y; \
332   ps->new_x = new_x; \
333   ps->new_y = new_y; \
334   mov = old_x != new_x || old_y != new_y; \
335   moved[i] = mov;
336
337         compute (-, -, -, -, 0);
338         compute (-, -, -, +, 1);
339         compute (-, -, +, -, 2);
340         compute (-, -, +, +, 3);
341         compute (-, +, -, -, 4);
342         compute (-, +, -, +, 5);
343         compute (-, +, +, -, 6);
344         compute (-, +, +, +, 7);
345         compute (+, -, -, -, 8);
346         compute (+, -, -, +, 9);
347         compute (+, -, +, -, 10);
348         compute (+, -, +, +, 11);
349         compute (+, +, -, -, 12);
350         compute (+, +, -, +, 13);
351         compute (+, +, +, -, 14);
352         compute (+, +, +, +, 15);
353       }
354
355     skip1:
356       icon = hs->hs_icon;
357       redraw = hs->hs_redraw;
358       if (icon || !(roted | redraw))
359         goto skip2;
360
361       {
362         int lc;
363         const struct line_info *lip;
364         int mono;
365         Display *dpy;
366         Window win;
367
368         lc = LINE_COUNT;
369         lip = &line_table[0];
370         mono = mono_p;
371         dpy = hs->hs_display;
372         win = hs->hs_window;
373
374         while (--lc >= 0)
375           {
376             int ip;
377             int iq;
378             int col;
379             struct point_state *sp;
380             struct point_state *sq;
381             int mov_p;
382             int mov_q;
383             GC erase_gc;
384             GC draw_gc;
385             int p_x;
386             int p_y;
387             int q_x;
388             int q_y;
389
390             ip = lip->li_ip;
391             iq = lip->li_iq;
392             col = lip->li_color;
393             lip++;
394             mov_p = moved[ip];
395             mov_q = moved[iq];
396             if (!(redraw | mov_p | mov_q))
397               continue;
398
399             sp = &hs->hs_points[ip];
400             sq = &hs->hs_points[iq];
401
402             if (mono)
403               {
404                 erase_gc = hs->hs_color_gcs[0];
405                 draw_gc = hs->hs_color_gcs[1];
406               }
407             else
408               {
409                 GC color_gc;
410
411                 color_gc = hs->hs_color_gcs[col];
412                 erase_gc = color_gc;
413                 draw_gc = color_gc;
414               }
415
416             if (!redraw)
417               {
418                 p_x = sp->old_x;
419                 p_y = sp->old_y;
420                 q_x = sq->old_x;
421                 q_y = sq->old_y;
422                 XDrawLine (dpy, win, erase_gc, p_x, p_y, q_x, q_y);
423               }
424
425             p_x = sp->new_x;
426             p_y = sp->new_y;
427             q_x = sq->new_x;
428             q_y = sq->new_y;
429             XDrawLine (dpy, win, draw_gc, p_x, p_y, q_x, q_y);
430           }
431
432         XFlush (dpy);
433       }
434
435     skip2:
436       stop = hs->hs_stop;
437       roted = 0;
438       if (stop)
439         goto skip3;
440
441       roted = 1;
442
443       {
444         double cos_a;
445         double sin_a;
446         double old_u;
447         double old_v;
448         double new_u;
449         double new_v;
450
451  /* If you get error messages about the following forms, and you think you're
452     using an ANSI C conforming compiler, then you're mistaken.  Possibly you're
453     mixing an ANSI compiler with a non-ANSI preprocessor, or vice versa.
454     Regardless, your system is broken; it's not a bug in this program.
455   */
456 #if defined(__STDC__) || defined(__ANSI_CPP__)
457
458 #define rotate(name,dim0,dim1) \
459   old_u = hs->hs_ref_##name##dim0; \
460   old_v = hs->hs_ref_##name##dim1; \
461   new_u = old_u * cos_a + old_v * sin_a; \
462   new_v = old_v * cos_a - old_u * sin_a; \
463   hs->hs_ref_##name##dim0 = new_u; \
464   hs->hs_ref_##name##dim1 = new_v;
465
466 #define rotates(dim0,dim1) \
467   if (hs->hs_sin_##dim0##dim1 != 0) { \
468     cos_a = hs->hs_cos_##dim0##dim1; \
469     sin_a = hs->hs_sin_##dim0##dim1; \
470     rotate(a,dim0,dim1); \
471     rotate(b,dim0,dim1); \
472     rotate(c,dim0,dim1); \
473     rotate(d,dim0,dim1); \
474   }
475
476 #else /* !__STDC__, courtesy of Andreas Luik <luik@isa.de> */
477
478 #define rotate(name,dim0,dim1) \
479   old_u = hs->hs_ref_/**/name/**/dim0; \
480   old_v = hs->hs_ref_/**/name/**/dim1; \
481   new_u = old_u * cos_a + old_v * sin_a; \
482   new_v = old_v * cos_a - old_u * sin_a; \
483   hs->hs_ref_/**/name/**/dim0 = new_u; \
484   hs->hs_ref_/**/name/**/dim1 = new_v;
485
486 #define rotates(dim0,dim1) \
487   if (hs->hs_sin_/**/dim0/**/dim1 != 0) { \
488     cos_a = hs->hs_cos_/**/dim0/**/dim1; \
489     sin_a = hs->hs_sin_/**/dim0/**/dim1; \
490     rotate(a,dim0,dim1); \
491     rotate(b,dim0,dim1); \
492     rotate(c,dim0,dim1); \
493     rotate(d,dim0,dim1); \
494   }
495
496 #endif /* !__STDC__ */
497
498         rotates (x,y);
499         rotates (x,z);
500         rotates (y,z);
501         rotates (x,w);
502         rotates (y,w);
503         rotates (z,w);
504       }
505
506     skip3:
507       /* stop = hs->hs_stop; */
508       delay = hs->hs_delay;
509       if (stop && delay < 10000)
510         delay = 10000;
511       if (delay > 0)
512         usleep (delay);
513     }
514 }
515
516
517 static void
518 check_events (struct hyper_state *hs)
519 {
520   Display *dpy;
521   int count;
522   int ic;
523   int resize;
524   Window win;
525   int redraw;
526
527   dpy = hs->hs_display;
528   count = XEventsQueued (dpy, QueuedAfterReading);
529   ic = count;
530   hs->hs_resize = 0;
531   hs->hs_redraw = 0;
532
533   while (--ic >= 0)
534     {
535       XEvent    e;
536
537       XNextEvent (dpy, &e);
538
539       switch (e.type)
540         {
541         case Expose:
542           hs->hs_icon = 0;
543           hs->hs_redraw = 1;
544           break;
545
546         case ConfigureNotify:
547           hs->hs_icon = 0;
548           hs->hs_resize = 1;
549           hs->hs_redraw = 1;
550           break;
551
552         case ButtonPress:
553           switch (e.xbutton.button)
554             {
555             case 2:
556               hs->hs_stop = !hs->hs_stop;
557               break;
558             default:
559               break;
560             }
561           break;
562
563         case UnmapNotify:
564           hs->hs_icon = 1;
565           hs->hs_redraw = 0;
566           break;
567
568         default:
569           screenhack_handle_event(dpy, &e);
570           break;
571         }
572     }
573
574   resize = hs->hs_resize;
575   win = hs->hs_window;
576   if (resize)
577     {
578       XWindowAttributes wa;
579       int width;
580       int height;
581
582       XGetWindowAttributes (dpy, win, &wa);
583       width = wa.width;
584       height = wa.height;
585       set_sizes (&hyper_state, width, height);
586     }
587
588   redraw = hs->hs_redraw;
589   if (redraw)
590     XClearWindow (dpy, win);
591 }
592
593
594 static void
595 set_sizes (struct hyper_state *hs, int width, int height)
596 {
597   double observer_z;
598   int min_dim;
599   double var;
600   double offset_x;
601   double offset_y;
602   double unit_scale;
603
604   observer_z = 0.5 * hs->hs_two_observer_z;
605   min_dim = width < height ? width : height;
606   var = sqrt(observer_z * observer_z - 1.0);
607   offset_x = 0.5 * (double)(width - 1);
608   offset_y = 0.5 * (double)(height - 1);
609   unit_scale = 0.4 * min_dim * var;
610   hs->hs_offset_x = (float)offset_x;
611   hs->hs_offset_y = (float)offset_y;
612   hs->hs_unit_scale = (float)unit_scale;
613 }
614
615
616 /* data */
617
618 static const struct line_info line_table[LINE_COUNT] =
619 {
620     { 0, 1, 0, },
621     { 0, 2, 0, },
622     { 1, 3, 0, },
623     { 2, 3, 0, },
624     { 4, 5, 1, },
625     { 4, 6, 1, },
626     { 5, 7, 1, },
627     { 6, 7, 1, },
628     { 0, 4, 4, },
629     { 0, 8, 4, },
630     { 4, 12, 4, },
631     { 8, 12, 4, },
632     { 1, 5, 5, },
633     { 1, 9, 5, },
634     { 5, 13, 5, },
635     { 9, 13, 5, },
636     { 2, 6, 6, },
637     { 2, 10, 6, },
638     { 6, 14, 6, },
639     { 10, 14, 6, },
640     { 3, 7, 7, },
641     { 3, 11, 7, },
642     { 7, 15, 7, },
643     { 11, 15, 7, },
644     { 8, 9, 2, },
645     { 8, 10, 2, },
646     { 9, 11, 2, },
647     { 10, 11, 2, },
648     { 12, 13, 3, },
649     { 12, 14, 3, },
650     { 13, 15, 3, },
651     { 14, 15, 3, },
652 };
653