1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* hyper --- spinning hypercubes, not just for tesseracts any more */
3
4 #if 0
5 static const char sccsid[] = "@(#)hyper.c 5.00 2000/11/01 xlockmore";
6
7 #endif
8
9 /*-
10 * hyper.c (nee multidico)
11 * Copyright (C) 1992,1998 John Heidemann <johnh AT isi.edu>
12 *
13 * Permission to use, copy, modify, distribute, and sell this software and its
14 * documentation for any purpose is hereby granted without fee, provided that
15 * the above copyright notice appear in all copies and that both that
16 * copyright notice and this permission notice appear in supporting
17 * documentation. No representations are made about the suitability of this
18 * software for any purpose. It is provided "as is" without express or
19 * implied warranty.
20 *
21 ***
22 *
23 * hyper (then called multidico) was originally written for
24 * xscreensaver in 1992. Unfortunately it didn't make it into that
25 * code because Jamie Zawinski was implementing his own tesseract and
26 * preferred his version (optimized for speed) to my version
27 * (with support for more than 4 dimesnions).
28 *
29 * In 1998 I finally got around to porting it to xlockmore and sending
30 * it off.
31 *
32 * (The implementation is independent of jwz's---I started with ico.)
33 *
34 * Editor's Note... (DAB)
35 * Removed all original Color stuff. Replaced it so it could do xoring
36 * effectively.... Took move_line from jwz's version.... to make it legal...
37 *
38 * Copyright (c) 1992 by Jamie Zawinski
39 *
40 * Permission to use, copy, modify, and distribute this software and its
41 * documentation for any purpose and without fee is hereby granted,
42 * provided that the above copyright notice appear in all copies and that
43 * both that copyright notice and this permission notice appear in
44 * supporting documentation.
45 *
46 * This file is provided AS IS with no warranties of any kind. The author
47 * shall have no liability with respect to the infringement of copyrights,
48 * trade secrets or any patents by this file or any part thereof. In no
49 * event will the author be liable for any lost revenue or profits or
50 * other special, indirect and consequential damages.
51 *
52 * Check out A.K. Dewdney's "Computer Recreations", Scientific American
53 * Magazine" Apr 1986 pp 14-25 for more info.
54 * Idea on 3d taken from there but does not work yet. Also a small number of
55 * random chosen planes drawn may be nice.
56 *
57 * Revision History:
58 * 01-Nov-2000: Added "3d" support from Scientific American
59 * 04-Aug-1998: Added "3d" support from Scientific American
60 */
61
62 #ifdef STANDALONE
63 #define MODE_hyper
64 #define DEFAULTS "*delay: 100000 \n" \
65 "*count: -6 \n" \
66 "*cycles: 300 \n" \
67 "*ncolors: 200 \n" \
68 "*use3d: False \n" \
69 "*delta3d: 1.5 \n" \
70 "*right3d: red \n" \
71 "*left3d: blue \n" \
72 "*both3d: magenta \n" \
73 "*none3d: black \n" \
74 "*debug: false \n" \
75
76 # define free_hyper 0
77 # define reshape_hyper 0
78 # define hyper_handle_event 0
79 #include "xlockmore.h" /* in xscreensaver distribution */
80 #else /* STANDALONE */
81 #include "xlock.h" /* in xlockmore distribution */
82 #endif /* STANDALONE */
83
84 #ifdef MODE_hyper
85
86 static Bool random_start;
87 static Bool show_axes;
88 static Bool show_planes;
89 static int spin_delay;
90
91 extern XFontStruct *getFont(Display * display);
92
93 static XrmOptionDescRec opts[] =
94 {
95 {(char *) "-randomstart", (char *) ".hyper.randomStart", XrmoptionNoArg, (caddr_t) "on"},
96 {(char *) "+randomstart", (char *) ".hyper.randomStart", XrmoptionNoArg, (caddr_t) "off"},
97 {(char *) "-showaxes", (char *) ".hyper.showAxes", XrmoptionNoArg, (caddr_t) "on"},
98 {(char *) "+showaxes", (char *) ".hyper.showAxes", XrmoptionNoArg, (caddr_t) "off"},
99 {(char *) "-showplanes", (char *) ".hyper.showPlanes", XrmoptionNoArg, (caddr_t) "on"},
100 {(char *) "+showplanes", (char *) ".hyper.showPlanes", XrmoptionNoArg, (caddr_t) "off"},
101 {(char *) "-spindelay", (char *) ".hyper.spinDelay", XrmoptionSepArg, 0},
102 };
103 static argtype vars[] =
104 {
105 {(void *) & random_start, (char *) "randomStart", (char *) "RandomStart", (char *) "True", t_Bool},
106 {(void *) & show_axes, (char *) "showAxes", (char *) "ShowAxes", (char *) "True", t_Bool},
107 {(void *) & show_planes, (char *) "showPlanes", (char *) "ShowPlanes", (char *) "False", t_Bool},
108 {(void *) & spin_delay, (char *) "spinDelay", (char *) "SpinDelay", (char *) "2", t_Int},
109 };
110 static OptionStruct desc[] =
111 {
112 {(char *) "-/+randomstart", (char *) "turn on/off beginning with random rotations"},
113 {(char *) "-/+showaxes", (char *) "turn on/off showing the axes"},
114 {(char *) "-/+showplanes", (char *) "turn on/off showing the planes"},
115 {(char *) "-spindelay num", (char *) "delay in seconds before changing spin speed"},
116 };
117
118 ENTRYPOINT ModeSpecOpt hyper_opts =
119 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
120
121 #ifdef USE_MODULES
122 ModStruct hyper_description =
123 {"hyper", "init_hyper", "draw_hyper", "release_hyper",
124 "refresh_hyper", "change_hyper", (char *) NULL, &hyper_opts,
125 100000, -6, 300, 1, 64, 1.0, "",
126 "Shows spinning n-dimensional hypercubes", 0, NULL};
127
128 #endif
129
130 #ifdef DEBUG
131 #include <assert.h>
132 #endif
133
134 typedef struct {
135 double x, y;
136 } dpoint;
137
138 typedef double vector; /* Was an array of size (MAX_D + 1) */
139 typedef double matrix; /* Was a double array of size (MAX_D + 1) ^ 2 */
140
141 #define MIN_D 2
142 #define MAX_D 10 /* Memory use goes up exponentially */
143
144 /* Normally DELTA3D is not in degrees, but it works here, so why fight it? */
145 #define DELDEG (MI_DELTA3D(mi)*M_PI/180.0)
146
147 typedef struct {
148 int from, to;
149 long color;
150 } line_segment;
151
152 typedef struct {
153 int a, b, c, d;
154 long color;
155 } plane_section;
156
157
158 typedef struct hyper {
159 GC gc;
160 Bool redrawing;
161 Bool painted;
162 XFontStruct *font;
163
164 int show_axes;
165 int show_planes;
166
167 int maxx, maxy;
168 int delay;
169 int spinDelay;
170 Bool normxor;
171
172 /*
173 * Data storage.
174 */
175 int num_d; /* number of dimensions */
176 int num_mat;
177 int num_matmat;
178
179 /* there are C(num_d,2) planes */
180 int num_planes; /* #define max_planes 40 */
181 XPoint *rotation_planes;
182
183 /* Formerly max_planes arrays */
184 double *rotations; /* where we are in each dimension */
185 double *d_rotations; /* change in rotation */
186 double *dd_rotations; /* change in change in rotation */
187 int *cdd_rotations; /* how many turns to apply dd_rotations */
188 matrix *Trotations;
189 matrix *Trotationsleft;
190
191 matrix *Tall;
192 matrix *Tallleft;
193
194 int num_points;
195 vector *points;
196 vector *pointsleft;
197 int num_lines;
198 line_segment *lines;
199 plane_section *planes;
200
201 int point_set; /* which bank of points are we using */
202 XPoint *xpoints[2];
203 XPoint *xpointsleft[2];
204
205 int num_axis_points;
206
207 /* Formerly an array of (MAX_D + 1) */
208 int *axis_points;
209
210 /*
211 * Inter-step state:
212 */
213 int this_set;
214 Bool stationary;
215 } hyperstruct;
216
217
218 static hyperstruct *hypers = (hyperstruct *) NULL;
219
220 #define allocarray(P,T,N) if((P=(T*)malloc((N)*sizeof(T)))==NULL) \
221 {free_hyper_screen(display,hp);return False;}
222 #define callocarray(P,T,N) if((P=(T*)calloc(N,sizeof(T)))==NULL) \
223 {free_hyper_screen(display,hp);return False;}
224
225 /*
226 * Matrix handling & 3d transformation routines
227 */
228
229 static void
MatMult(matrix * a,matrix * b,matrix * c,int n)230 MatMult(matrix * a, matrix * b, matrix * c, int n)
231 /* c = a * b for n x n matricies */
232 {
233 register int i, j, k;
234 double temp;
235
236 /* Strassen' method... what's that? */
237 for (i = 0; i < n; i++) /* go through a's rows */
238 for (j = 0; j < n; j++) { /* go through b's columns */
239 temp = 0.0;
240 for (k = 0; k < n; k++)
241 temp += a[i * n + k] * b[k * n + j];
242 c[i * n + j] = temp;
243 }
244 }
245
246 static void
MatVecMult(matrix * a,vector * b,vector * c,int n)247 MatVecMult(matrix * a, vector * b, vector * c, int n)
248 /* c = a * b for a n x n, b&c 1 x n */
249 {
250 register int i, k;
251 double temp;
252
253 for (i = 0; i < n; i++) { /* go through a's rows */
254 temp = 0.0;
255 for (k = 0; k < n; k++)
256 temp += a[i * n + k] * b[k];
257 c[i] = temp;
258 }
259 }
260
261 static void
MatCopy(matrix * a,matrix * b,int n)262 MatCopy(matrix * a, matrix * b, int n)
263 /* b <- a */
264 {
265 register int i, j;
266
267 for (i = 0; i < n; i++)
268 for (j = 0; j < n; j++)
269 b[i * n + j] = a[i * n + j];
270 }
271
272 static void
MatZero(matrix * a,int n)273 MatZero(matrix * a, int n)
274 /* a = 0 */
275 {
276 register int i, j;
277
278 for (i = 0; i < n; i++)
279 for (j = 0; j < n; j++)
280 a[i * n + j] = 0.0;
281 }
282
283 static void
MatIdent(matrix * a,int n)284 MatIdent(matrix * a, int n)
285 /* a = I */
286 {
287 register int i;
288
289 MatZero(a, n);
290 for (i = 0; i < n; i++)
291 a[i * n + i] = 1.0;
292 }
293
294 static void
ZeroMultiCounter(int * c,int n)295 ZeroMultiCounter(int *c, int n)
296 {
297 int i;
298
299 for (i = 0; i < n; i++)
300 c[i] = 0;
301 }
302
303
304 static int
RealIncMultiCounter(int * c,int n,int place)305 RealIncMultiCounter(int *c, int n, int place)
306 {
307 #define BASE 2
308 if (place >= n)
309 return 0;
310 else if (++c[place] >= BASE) {
311 c[place] = 0;
312 return RealIncMultiCounter(c, n, place + 1);
313 } else
314 return 1;
315 }
316
317
318 static int
IncMultiCounter(int * c,int n)319 IncMultiCounter(int *c, int n)
320 {
321 return RealIncMultiCounter(c, n, 0);
322 }
323
324
325 static int
figure_num_points(int d)326 figure_num_points(int d)
327 {
328 return 1 << d; /* (int)pow(2.0, (double)d); */
329 }
330
331 static int
figure_num_lines(int d)332 figure_num_lines(int d)
333 {
334 return d * figure_num_points(d - 1);
335 }
336
337 static int
figure_num_planes(int d)338 figure_num_planes(int d)
339 {
340 return ((d - 1) * d) / 2;
341 }
342
343 static void
free_hyper_stuff(hyperstruct * hp)344 free_hyper_stuff(hyperstruct * hp)
345 {
346 if (hp->axis_points) {
347 free(hp->axis_points);
348 hp->axis_points = (int *) NULL;
349 }
350 if (hp->points) {
351 free(hp->points);
352 hp->points = (vector *) NULL;
353 }
354 if (hp->pointsleft) {
355 free(hp->pointsleft);
356 hp->pointsleft = (vector *) NULL;
357 }
358 if (hp->lines) {
359 XFree(hp->lines);
360 hp->lines = (line_segment *) NULL;
361 }
362 if (hp->planes) {
363 XFree(hp->planes);
364 hp->planes = (plane_section *) NULL;
365 }
366 if (hp->rotation_planes) {
367 XFree(hp->rotation_planes);
368 hp->rotation_planes = (XPoint *) NULL;
369 }
370 if (hp->rotations) {
371 free(hp->rotations);
372 hp->rotations = (double *) NULL;
373 }
374 if (hp->d_rotations) {
375 free(hp->d_rotations);
376 hp->d_rotations = (double *) NULL;
377 }
378 if (hp->dd_rotations) {
379 free(hp->dd_rotations);
380 hp->dd_rotations = (double *) NULL;
381 }
382 if (hp->cdd_rotations) {
383 free(hp->cdd_rotations);
384 hp->cdd_rotations = (int *) NULL;
385 }
386 if (hp->Trotations) {
387 free(hp->Trotations);
388 hp->Trotations = (matrix *) NULL;
389 }
390 if (hp->Trotationsleft) {
391 free(hp->Trotationsleft);
392 hp->Trotationsleft = (matrix *) NULL;
393 }
394 if (hp->Tall) {
395 free(hp->Tall);
396 hp->Tall = (matrix *) NULL;
397 }
398 if (hp->Tallleft) {
399 free(hp->Tallleft);
400 hp->Tallleft = (matrix *) NULL;
401 }
402 if (hp->xpoints[0]) {
403 XFree(hp->xpoints[0]);
404 hp->xpoints[0] = (XPoint *) NULL;
405 }
406 if (hp->xpoints[1]) {
407 XFree(hp->xpoints[1]);
408 hp->xpoints[1] = (XPoint *) NULL;
409 }
410 if (hp->xpointsleft[0]) {
411 XFree(hp->xpointsleft[0]);
412 hp->xpointsleft[0] = (XPoint *) NULL;
413 }
414 if (hp->xpointsleft[1]) {
415 XFree(hp->xpointsleft[1]);
416 hp->xpointsleft[1] = (XPoint *) NULL;
417 }
418 }
419
420 static void
free_hyper_screen(Display * display,hyperstruct * hp)421 free_hyper_screen(Display *display, hyperstruct *hp)
422 {
423 if (hp == NULL) {
424 return;
425 }
426 if (hp->gc != None) {
427 XFreeGC(display, hp->gc);
428 hp->gc = None;
429 }
430 if (hp->font != None) {
431 XFreeFont(display, hp->font);
432 hp->font = None;
433 }
434 free_hyper_stuff(hp);
435 hp = NULL;
436 }
437
438 static Bool
figure_points(ModeInfo * mi)439 figure_points(ModeInfo * mi)
440 {
441 Display *display = MI_DISPLAY(mi);
442 hyperstruct *hp = &hypers[MI_SCREEN(mi)];
443 int i, j, k, n, d, pix = 0;
444 int *c;
445
446 /*
447 * First, figure out the points.
448 */
449 hp->num_points = figure_num_points(hp->num_d);
450 allocarray(hp->points, vector, hp->num_points * hp->num_mat);
451 allocarray(hp->xpoints[0], XPoint, hp->num_points);
452 allocarray(hp->xpoints[1], XPoint, hp->num_points);
453 if (MI_IS_USE3D(mi)) {
454 allocarray(hp->pointsleft, vector, hp->num_points * hp->num_mat);
455 allocarray(hp->xpointsleft[0], XPoint, hp->num_points);
456 allocarray(hp->xpointsleft[1], XPoint, hp->num_points);
457 }
458 allocarray(c, int, hp->num_points); /* will be lost, sigh */
459
460 ZeroMultiCounter(c, hp->num_d);
461 n = 0;
462 do {
463 for (i = 0; i < hp->num_d; i++) {
464 hp->points[n * hp->num_mat + i] = c[i];
465 if (MI_IS_USE3D(mi))
466 hp->pointsleft[n * hp->num_mat + i] = c[i];
467 }
468 n++;
469 } while (IncMultiCounter(c, hp->num_d));
470 free(c);
471 #ifdef DEBUG
472 assert(hp->num_points == n);
473 #endif
474 /*
475 * Next connect them.
476 * We could do this more intelligently, but why bother?
477 *
478 * Connect points that differ by only one coordinate.
479 */
480 if (MI_NPIXELS(mi) > 2)
481 pix = NRAND(MI_NPIXELS(mi));
482
483 hp->num_lines = figure_num_lines(hp->num_d);
484 allocarray(hp->lines, line_segment, hp->num_lines);
485 for (n = i = 0; i < hp->num_points; i++) {
486 for (j = i + 1; j < hp->num_points; j++) {
487 for (d = k = 0; k < hp->num_d; k++) {
488 if (hp->points[i * hp->num_mat + k] != hp->points[j * hp->num_mat + k])
489 d++;
490 }
491 if (d == 1) {
492 hp->lines[n].from = i;
493 hp->lines[n].to = j;
494 /* (void) printf ("from %x to %x ", i, j); */
495 if (MI_NPIXELS(mi) > 2) {
496 hp->lines[n].color = MI_PIXEL(mi, pix);
497 if (++pix >= MI_NPIXELS(mi))
498 pix = 0;
499 } else
500 hp->lines[n].color = MI_WHITE_PIXEL(mi);
501 n++;
502 }
503 }
504 }
505 #ifdef DEBUG
506 assert(hp->num_lines == n);
507 #endif
508
509 /*
510 * Now determine the planes of rotation.
511 */
512 hp->num_planes = figure_num_planes(hp->num_d);
513 hp->show_planes = show_planes;
514
515 if (hp->show_planes) {
516 allocarray(hp->planes, plane_section, hp->num_planes);
517
518 /* Keeping it simple and just drawing planes that touch the
519 * axes. Still not that simple, have to figure out which pt c is
520 * furthest away and draw it first... yuck.
521 */
522
523 for (n = i = 0; i < hp->num_d; i++) {
524 for (j = i + 1; j < hp->num_d; j++) {
525 hp->planes[n].a = 0;
526 hp->planes[n].b = 1 << i;
527 hp->planes[n].d = 1 << j;
528 hp->planes[n].c = hp->planes[n].b + hp->planes[n].d;
529 /*(void) printf ("a %d, b %d, c %d, d %d\n",
530 0, 1 << i, (1 << i) + (1 << j), 1 << j);*/
531 if (MI_NPIXELS(mi) > 2) {
532 hp->planes[n].color = MI_PIXEL(mi, pix);
533 if (++pix >= MI_NPIXELS(mi))
534 pix = 0;
535 } else
536 hp->planes[n].color = MI_WHITE_PIXEL(mi);
537 n++;
538 }
539 }
540 }
541
542 allocarray(hp->axis_points, int, hp->num_d + 1);
543
544 allocarray(hp->rotations, double, hp->num_planes);
545 callocarray(hp->d_rotations, double, hp->num_planes);
546 callocarray(hp->dd_rotations, double, hp->num_planes);
547 callocarray(hp->cdd_rotations, int, hp->num_planes);
548
549 allocarray(hp->Trotations, matrix, hp->num_planes * hp->num_matmat);
550 allocarray(hp->Tall, matrix, hp->num_matmat);
551
552 if (MI_IS_USE3D(mi)) {
553 allocarray(hp->Trotationsleft, matrix, hp->num_planes * hp->num_matmat);
554 allocarray(hp->Tallleft, matrix, hp->num_matmat);
555 }
556 allocarray(hp->rotation_planes, XPoint, hp->num_planes);
557
558 for (n = i = 0; i < hp->num_d; i++)
559 for (j = i + 1; j < hp->num_d; j++) {
560 hp->rotation_planes[n].x = i;
561 hp->rotation_planes[n].y = j;
562 n++;
563 }
564 #ifdef DEBUG
565 assert(hp->num_planes == n);
566 #endif
567 /*
568 * Potential random initial rotations.
569 */
570 #define FRAC (1024*16)
571 if (random_start) {
572 for (i = 0; i < hp->num_planes; i++)
573 hp->rotations[i] = 2.0 * NRAND(FRAC) * M_PI / FRAC;
574 }
575 return True;
576 }
577
578 static void
figure_axis_points(hyperstruct * hp)579 figure_axis_points(hyperstruct * hp)
580 {
581 int i, j, num_set;
582
583 hp->show_axes = show_axes;
584 for (hp->num_axis_points = i = 0; i < hp->num_points; i++) {
585 for (num_set = j = 0; j < hp->num_d; j++)
586 if (hp->points[i * hp->num_mat + j] != 0.0)
587 num_set++;
588 if (num_set <= 1)
589 hp->axis_points[hp->num_axis_points++] = i;
590 }
591 }
592
593 static Bool
init_x_stuff(ModeInfo * mi)594 init_x_stuff(ModeInfo * mi)
595 {
596 hyperstruct *hp = &hypers[MI_SCREEN(mi)];
597 XGCValues gcv;
598
599 hp->maxx = MI_WIDTH(mi);
600 hp->maxy = MI_HEIGHT(mi);
601
602
603 hp->spinDelay = 10 * spin_delay;
604 if (hp->spinDelay < 0)
605 hp->spinDelay = 0;
606
607 free_hyper_stuff(hp);
608 hp->num_d = MI_COUNT(mi);
609 if (hp->num_d < -MAX_D)
610 hp->num_d = -MAX_D;
611 if (hp->num_d > MAX_D)
612 hp->num_d = MAX_D;
613 else if (hp->num_d < -MIN_D) {
614 hp->num_d = NRAND(-hp->num_d - MIN_D + 1) + MIN_D;
615 } else if (hp->num_d < MIN_D)
616 hp->num_d = MIN_D;
617
618 hp->num_mat = hp->num_d + 1;
619 hp->num_matmat = hp->num_mat * hp->num_mat;
620
621 if (hp->font == None) {
622 if ((hp->font = getFont(MI_DISPLAY(mi))) == None)
623 return False;
624 }
625 if (MI_NPIXELS(mi) <= 2 || MI_IS_USE3D(mi))
626 hp->normxor = False;
627 if ((hp->gc == None) && (MI_NPIXELS(mi) <= 2 || !MI_IS_USE3D(mi))) {
628 long options;
629
630 if (hp->normxor) {
631 options = GCForeground | GCFont | GCFunction;
632 gcv.foreground = MI_WHITE_PIXEL(mi) ^ MI_BLACK_PIXEL(mi);
633 gcv.function = GXxor;
634 } else {
635 options = GCForeground | GCBackground | GCFont;
636 gcv.foreground = MI_WHITE_PIXEL(mi);
637 gcv.background = MI_BLACK_PIXEL(mi);
638 }
639 gcv.font = hp->font->fid;
640 if ((hp->gc = XCreateGC(MI_DISPLAY(mi), MI_WINDOW(mi), options,
641 &gcv)) == None)
642 return False;
643 }
644 return True;
645 }
646
647 static void
move_line(ModeInfo * mi,int from,int to,int set,long color)648 move_line(ModeInfo * mi, int from, int to, int set, long color)
649 {
650 Display *display = MI_DISPLAY(mi);
651 Window window = MI_WINDOW(mi);
652 GC gc = MI_GC(mi);
653 hyperstruct *hp = &hypers[MI_SCREEN(mi)];
654
655 if (MI_NPIXELS(mi) <= 2 || !MI_IS_USE3D(mi))
656 gc = hp->gc;
657 if (hp->normxor) {
658 XSetForeground(display, gc, color);
659 if (!hp->redrawing)
660 XDrawLine(display, window, gc,
661 hp->xpoints[!set][from].x, hp->xpoints[!set][from].y,
662 hp->xpoints[!set][to].x, hp->xpoints[!set][to].y);
663 XDrawLine(display, window, gc,
664 hp->xpoints[set][from].x, hp->xpoints[set][from].y,
665 hp->xpoints[set][to].x, hp->xpoints[set][to].y);
666 } else {
667 if (!hp->redrawing) {
668 if (MI_IS_INSTALL(mi) && MI_IS_USE3D(mi))
669 XSetForeground(display, gc, MI_NONE_COLOR(mi));
670 else
671 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
672 XDrawLine(display, window, gc,
673 hp->xpoints[set][from].x, hp->xpoints[set][from].y,
674 hp->xpoints[set][to].x, hp->xpoints[set][to].y);
675 if (MI_IS_USE3D(mi))
676 XDrawLine(display, window, gc,
677 hp->xpointsleft[set][from].x, hp->xpointsleft[set][from].y,
678 hp->xpointsleft[set][to].x, hp->xpointsleft[set][to].y);
679 }
680 if (MI_IS_USE3D(mi)) {
681 if (MI_IS_INSTALL(mi)) {
682 XSetFunction(display, gc, GXor);
683 }
684 XSetForeground(display, gc, MI_LEFT_COLOR(mi));
685 } else if (MI_NPIXELS(mi) <= 2)
686 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
687 else
688 XSetForeground(display, gc, color);
689 XDrawLine(display, window, gc,
690 hp->xpoints[!set][from].x, hp->xpoints[!set][from].y,
691 hp->xpoints[!set][to].x, hp->xpoints[!set][to].y);
692 if (MI_IS_USE3D(mi)) {
693 XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
694 XDrawLine(display, window, gc,
695 hp->xpointsleft[!set][from].x, hp->xpointsleft[!set][from].y,
696 hp->xpointsleft[!set][to].x, hp->xpointsleft[!set][to].y);
697 if (MI_IS_INSTALL(mi)) {
698 XSetFunction(display, gc, GXcopy);
699 }
700 }
701 }
702 }
703
704 static void
move_plane(ModeInfo * mi,int a,int b,int c,int d,int set,long color)705 move_plane(ModeInfo * mi, int a, int b, int c, int d, int set, long color)
706 {
707 Display *display = MI_DISPLAY(mi);
708 Window window = MI_WINDOW(mi);
709 GC gc = MI_GC(mi);
710 hyperstruct *hp = &hypers[MI_SCREEN(mi)];
711 XPoint rect[4];
712
713 if (MI_NPIXELS(mi) <= 2 || !MI_IS_USE3D(mi))
714 gc = hp->gc;
715 if (hp->normxor) {
716 XSetForeground(display, gc, color);
717 if (!hp->redrawing) {
718 rect[0].x = hp->xpoints[!set][a].x;
719 rect[0].y = hp->xpoints[!set][a].y;
720 rect[1].x = hp->xpoints[!set][b].x;
721 rect[1].y = hp->xpoints[!set][b].y;
722 rect[2].x = hp->xpoints[!set][c].x;
723 rect[2].y = hp->xpoints[!set][c].y;
724 rect[3].x = hp->xpoints[!set][d].x;
725 rect[3].y = hp->xpoints[!set][d].y;
726 XFillPolygon(display, window, gc, rect, 4, Convex, CoordModeOrigin);
727 }
728 rect[0].x = hp->xpoints[set][a].x;
729 rect[0].y = hp->xpoints[set][a].y;
730 rect[1].x = hp->xpoints[set][b].x;
731 rect[1].y = hp->xpoints[set][b].y;
732 rect[2].x = hp->xpoints[set][c].x;
733 rect[2].y = hp->xpoints[set][c].y;
734 rect[3].x = hp->xpoints[set][d].x;
735 rect[3].y = hp->xpoints[set][d].y;
736 XFillPolygon(display, window, gc, rect, 4, Convex, CoordModeOrigin);
737 } else {
738 if (!hp->redrawing) {
739 if (MI_IS_INSTALL(mi) && MI_IS_USE3D(mi))
740 XSetForeground(display, gc, MI_NONE_COLOR(mi));
741 else
742 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
743 rect[0].x = hp->xpoints[set][a].x;
744 rect[0].y = hp->xpoints[set][a].y;
745 rect[1].x = hp->xpoints[set][b].x;
746 rect[1].y = hp->xpoints[set][b].y;
747 rect[2].x = hp->xpoints[set][c].x;
748 rect[2].y = hp->xpoints[set][c].y;
749 rect[3].x = hp->xpoints[set][d].x;
750 rect[3].y = hp->xpoints[set][d].y;
751 XFillPolygon(display, window, gc, rect, 4, Convex, CoordModeOrigin);
752 if (MI_IS_USE3D(mi)) {
753 rect[0].x = hp->xpointsleft[set][a].x;
754 rect[0].y = hp->xpointsleft[set][a].y;
755 rect[1].x = hp->xpointsleft[set][b].x;
756 rect[1].y = hp->xpointsleft[set][b].y;
757 rect[2].x = hp->xpointsleft[set][c].x;
758 rect[2].y = hp->xpointsleft[set][c].y;
759 rect[3].x = hp->xpointsleft[set][d].x;
760 rect[3].y = hp->xpointsleft[set][d].y;
761 XFillPolygon(display, window, gc, rect, 4, Convex, CoordModeOrigin);
762 }
763 }
764 if (MI_IS_USE3D(mi)) {
765 if (MI_IS_INSTALL(mi)) {
766 XSetFunction(display, gc, GXor);
767 }
768 XSetForeground(display, gc, MI_LEFT_COLOR(mi));
769 } else if (MI_NPIXELS(mi) <= 2)
770 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
771 else
772 XSetForeground(display, gc, color);
773 rect[0].x = hp->xpoints[!set][a].x;
774 rect[0].y = hp->xpoints[!set][a].y;
775 rect[1].x = hp->xpoints[!set][b].x;
776 rect[1].y = hp->xpoints[!set][b].y;
777 rect[2].x = hp->xpoints[!set][c].x;
778 rect[2].y = hp->xpoints[!set][c].y;
779 rect[3].x = hp->xpoints[!set][d].x;
780 rect[3].y = hp->xpoints[!set][d].y;
781 XFillPolygon(display, window, gc, rect, 4, Convex, CoordModeOrigin);
782 if (MI_IS_USE3D(mi)) {
783 XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
784 rect[0].x = hp->xpointsleft[!set][a].x;
785 rect[0].y = hp->xpointsleft[!set][a].y;
786 rect[1].x = hp->xpointsleft[!set][b].x;
787 rect[1].y = hp->xpointsleft[!set][b].y;
788 rect[2].x = hp->xpointsleft[!set][c].x;
789 rect[2].y = hp->xpointsleft[!set][c].y;
790 rect[3].x = hp->xpointsleft[!set][d].x;
791 rect[3].y = hp->xpointsleft[!set][d].y;
792 XFillPolygon(display, window, gc, rect, 4, Convex, CoordModeOrigin);
793 if (MI_IS_INSTALL(mi)) {
794 XSetFunction(display, gc, GXcopy);
795 }
796 }
797 }
798 }
799
800 static void
move_number(ModeInfo * mi,int axis,char * string,int set,long color)801 move_number(ModeInfo * mi, int axis, char *string, int set, long color)
802 {
803 Display *display = MI_DISPLAY(mi);
804 Window window = MI_WINDOW(mi);
805 GC gc = MI_GC(mi);
806 hyperstruct *hp = &hypers[MI_SCREEN(mi)];
807
808 if (MI_NPIXELS(mi) <= 2 || !MI_IS_USE3D(mi))
809 gc = hp->gc;
810 if (hp->normxor) {
811 XSetForeground(display, gc, color);
812 if (!hp->redrawing)
813 (void) XDrawString(display, window, gc,
814 hp->xpoints[!set][axis].x, hp->xpoints[!set][axis].y,
815 string, strlen(string));
816 (void) XDrawString(display, window, gc,
817 hp->xpoints[set][axis].x, hp->xpoints[set][axis].y,
818 string, strlen(string));
819 } else {
820 if (!hp->redrawing) {
821 if (MI_IS_INSTALL(mi) && MI_IS_USE3D(mi))
822 XSetForeground(display, gc, MI_NONE_COLOR(mi));
823 else
824 XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
825 (void) XDrawString(display, window, gc,
826 hp->xpoints[set][axis].x, hp->xpoints[set][axis].y,
827 string, strlen(string));
828 if (MI_IS_USE3D(mi))
829 (void) XDrawString(display, window, gc,
830 hp->xpointsleft[set][axis].x, hp->xpointsleft[set][axis].y,
831 string, strlen(string));
832 }
833 if (MI_IS_USE3D(mi)) {
834 if (MI_IS_INSTALL(mi)) {
835 XSetFunction(display, gc, GXor);
836 }
837 XSetForeground(display, gc, MI_LEFT_COLOR(mi));
838 } else if (MI_NPIXELS(mi) <= 2)
839 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
840 else
841 XSetForeground(display, gc, color);
842 (void) XDrawString(display, window, gc,
843 hp->xpoints[!set][axis].x, hp->xpoints[!set][axis].y,
844 string, strlen(string));
845 if (MI_IS_USE3D(mi)) {
846 XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
847 (void) XDrawString(display, window, gc,
848 hp->xpointsleft[!set][axis].x, hp->xpointsleft[!set][axis].y,
849 string, strlen(string));
850 if (MI_IS_INSTALL(mi)) {
851 XSetFunction(display, gc, GXcopy);
852 }
853 }
854 }
855 }
856
857 static void
draw_hyper_step(ModeInfo * mi,int set)858 draw_hyper_step(ModeInfo * mi, int set)
859 {
860 hyperstruct *hp = &hypers[MI_SCREEN(mi)];
861 int i;
862 char tmps[11];
863
864 MI_IS_DRAWN(mi) = True;
865
866 if (!hp->stationary || hp->redrawing) {
867 for (i = 0; i < hp->num_lines; i++) {
868 move_line(mi, hp->lines[i].from, hp->lines[i].to,
869 set, hp->lines[i].color);
870 }
871 if (hp->show_planes) {
872 for (i = 0; i < hp->num_planes; i++) {
873 move_plane(mi, hp->planes[i].a, hp->planes[i].b, hp->planes[i].c,
874 hp->planes[i].d, set, hp->planes[i].color);
875 }
876 }
877 if (hp->show_axes) {
878 for (i = 0; i < hp->num_axis_points; i++) {
879 (void) sprintf(tmps, "%d", i);
880 move_number(mi, hp->axis_points[i], tmps, set, MI_WHITE_PIXEL(mi));
881 }
882 }
883 }
884 }
885
886 static void
move_hyper(ModeInfo * mi)887 move_hyper(ModeInfo * mi)
888 {
889 hyperstruct *hp = &hypers[MI_SCREEN(mi)];
890 int i;
891
892 /* NEEDSWORK: These should be resources */
893 #define default_cdd_rotation 10
894 #define default_dd_rotation (M_PI/1024.0)
895 int axis;
896 int faster;
897
898 hp->stationary = False;
899 if (hp->spinDelay-- <= 0) {
900
901 hp->spinDelay = 10 * spin_delay;
902 /*
903 * Change rotation?
904 *
905 * 66% chance if stationary, 33% if not.
906 */
907 hp->stationary = True;
908 for (i = 0; i < hp->num_planes; i++)
909 if (hp->d_rotations[i] != 0.0 || hp->cdd_rotations[i]) {
910 hp->stationary = False;
911 break;
912 }
913 if (NRAND(3) < 1 + hp->stationary) {
914 /* Change! But what? */
915 axis = NRAND(hp->num_planes);
916
917 /*
918 * And how much? 33% chance faster, 66% slower.
919 * If stopped, slower doesn't start it moving
920 * unless we're stationary.
921 */
922 hp->cdd_rotations[axis] = default_cdd_rotation +
923 NRAND(7) - 3;
924 faster = (NRAND(3) < 1);
925 if (faster || hp->dd_rotations[axis] != 0.0 || hp->stationary)
926 hp->dd_rotations[axis] = default_dd_rotation *
927 (hp->dd_rotations[axis] >= 0.0 ? 1 : -1) *
928 (faster ? 1 : -1);
929 if (MI_IS_DEBUG(mi))
930 (void) fprintf(stderr,
931 "axis %d made %s\n", axis, faster ? "faster" : "slower");
932 }
933 }
934 /*
935 * Rotate.
936 */
937 for (i = 0; i < hp->num_planes; i++) {
938 if (hp->cdd_rotations[i]) {
939 hp->cdd_rotations[i]--;
940 hp->d_rotations[i] += hp->dd_rotations[i];
941 }
942 hp->rotations[i] += hp->d_rotations[i];
943 }
944 }
945
946 static Bool
calc_transformation(ModeInfo * mi)947 calc_transformation(ModeInfo * mi)
948 {
949 hyperstruct *hp = &hypers[MI_SCREEN(mi)];
950 double cosa, sina;
951 double cosb = 0.0, sinb = 0.0;
952 int p1, p2;
953 matrix *Ttmp;
954 matrix *Tpre, *Tuser, *Tuserleft = (matrix *) NULL;
955 matrix *Tpretranspose, *Tscale, *Tposttranspose;
956 int i;
957 dpoint scale, range, offset;
958 double scale_used;
959
960 Ttmp = (matrix *) malloc(hp->num_matmat * sizeof (matrix));
961 Tpre = (matrix *) malloc(hp->num_matmat * sizeof (matrix));
962 Tuser = (matrix *) malloc(hp->num_matmat * sizeof (matrix));
963 Tpretranspose = (matrix *) malloc(hp->num_matmat * sizeof (matrix));
964 Tscale = (matrix *) malloc(hp->num_matmat * sizeof (matrix));
965 Tposttranspose = (matrix *) malloc(hp->num_matmat * sizeof (matrix));
966 if ((Ttmp == NULL) || (Tpre == NULL) || (Tuser == NULL) ||
967 (Tpretranspose == NULL) || (Tscale == NULL) ||
968 (Tposttranspose == NULL)) {
969 if (Ttmp != NULL)
970 free(Ttmp);
971 if (Tpre != NULL)
972 free(Tpre);
973 if (Tuser != NULL)
974 free(Tuser);
975 if (Tpretranspose != NULL)
976 free(Tpretranspose);
977 if (Tscale != NULL)
978 free(Tscale);
979 if (Tposttranspose != NULL)
980 free(Tposttranspose);
981 return False;
982 }
983 if (MI_IS_USE3D(mi)) {
984 if ((Tuserleft = (matrix *) malloc(hp->num_matmat *
985 sizeof (matrix))) == NULL) {
986 free(Ttmp);
987 free(Tpre);
988 free(Tuser);
989 free(Tpretranspose);
990 free(Tscale);
991 free(Tposttranspose);
992 return False;
993 }
994 }
995
996 /*
997 * Adjust the data.
998 * Since the data goes from 0->1 on each axis,
999 * we shift it by -0.5 here to center the figure.
1000 */
1001 MatIdent(Tpre, hp->num_mat);
1002 for (i = 0; i < hp->num_d; i++) {
1003 Tpre[i * hp->num_mat + hp->num_d] = -0.5;
1004 }
1005
1006 /*
1007 * Figure the rotation.
1008 */
1009 MatIdent(Tuser, hp->num_mat);
1010 if (MI_IS_USE3D(mi)) {
1011 MatIdent(Tuserleft, hp->num_mat);
1012 }
1013 for (i = 0; i < hp->num_planes; i++) {
1014 p1 = hp->rotation_planes[i].x;
1015 p2 = hp->rotation_planes[i].y;
1016 if (MI_IS_USE3D(mi)) {
1017 sinb = sin(hp->rotations[i] - DELDEG);
1018 cosb = cos(hp->rotations[i] - DELDEG);
1019 sina = sin(hp->rotations[i] + DELDEG);
1020 cosa = cos(hp->rotations[i] + DELDEG);
1021 } else {
1022 sina = sin(hp->rotations[i]);
1023 cosa = cos(hp->rotations[i]);
1024 }
1025
1026 MatIdent(&hp->Trotations[i * hp->num_matmat], hp->num_mat);
1027 hp->Trotations[i * hp->num_matmat + p1 * hp->num_mat + p1] =
1028 hp->Trotations[i * hp->num_matmat + p2 * hp->num_mat + p2] = cosa;
1029 hp->Trotations[i * hp->num_matmat + p1 * hp->num_mat + p2] = sina;
1030 hp->Trotations[i * hp->num_matmat + p2 * hp->num_mat + p1] = -sina;
1031 MatMult(&hp->Trotations[i * hp->num_matmat], Tuser, Ttmp, hp->num_mat);
1032 MatCopy(Ttmp, Tuser, hp->num_mat);
1033
1034 if (MI_IS_USE3D(mi)) {
1035 MatIdent(&hp->Trotationsleft[i * hp->num_matmat], hp->num_mat);
1036 hp->Trotationsleft[i * hp->num_matmat + p1 * hp->num_mat + p1] =
1037 hp->Trotationsleft[i * hp->num_matmat + p2 * hp->num_mat + p2] = cosb;
1038 hp->Trotationsleft[i * hp->num_matmat + p1 * hp->num_mat + p2] = sinb;
1039 hp->Trotationsleft[i * hp->num_matmat + p2 * hp->num_mat + p1] = -sinb;
1040 MatMult(&hp->Trotationsleft[i * hp->num_matmat], Tuserleft, Ttmp, hp->num_mat);
1041 MatCopy(Ttmp, Tuserleft, hp->num_mat);
1042 }
1043 }
1044
1045 /* Now calculate the scaling matrix */
1046 #if 1
1047 /*
1048 * Calculate the best scale of the two axes.
1049 * Multiply by 0.9 to use 90% of the display.
1050 * Divide by the sqrt(2.0) because it's biggest when
1051 * rotated by 45 degrees.
1052 *
1053 * This principle generalizes to sqrt(hp->num_d).
1054 */
1055 #define border_width (0.05)
1056 range.x = sqrt((double) hp->num_d);
1057 range.y = range.x;
1058 scale.x = (1.0 - 2 * border_width) * hp->maxx / range.x;
1059 scale.y = (1.0 - 2 * border_width) * hp->maxy / range.y;
1060 scale_used = ((scale.x < scale.y) ? scale.x : scale.y);
1061 offset.x = hp->maxx / 2.0;
1062 offset.y = hp->maxy / 2.0;
1063
1064 /*
1065 * Setup & compute the matrices.
1066 */
1067 MatIdent(Tpretranspose, hp->num_mat);
1068 Tpretranspose[0 * hp->num_mat + hp->num_d] = 0;
1069 Tpretranspose[1 * hp->num_mat + hp->num_d] = 0;
1070
1071 MatIdent(Tscale, hp->num_mat);
1072 Tscale[0 * hp->num_mat + 0] = scale_used;
1073 Tscale[1 * hp->num_mat + 1] = -scale_used;
1074
1075 MatIdent(Tposttranspose, hp->num_mat);
1076 Tposttranspose[0 * hp->num_mat + hp->num_d] = offset.x;
1077 Tposttranspose[1 * hp->num_mat + hp->num_d] = offset.y;
1078
1079 MatMult(Tscale, Tpretranspose, Ttmp, hp->num_mat);
1080 MatMult(Tposttranspose, Ttmp, Tscale, hp->num_mat);
1081 #else
1082 MatIdent(Tscale, hp->num_mat);
1083 #endif
1084 free(Tpretranspose);
1085 free(Tposttranspose);
1086
1087 /*
1088 * Put it all together.
1089 */
1090 MatMult(Tuser, Tpre, Ttmp, hp->num_mat);
1091 MatMult(Tscale, Ttmp, hp->Tall, hp->num_mat);
1092 free(Tuser);
1093 if (MI_IS_USE3D(mi)) {
1094 MatMult(Tuserleft, Tpre, Ttmp, hp->num_mat);
1095 MatMult(Tscale, Ttmp, hp->Tallleft, hp->num_mat);
1096 free(Tuserleft);
1097 }
1098 free(Tpre);
1099 free(Ttmp);
1100 free(Tscale);
1101 return True;
1102 }
1103
1104
1105 static Bool
translate_point(hyperstruct * hp,matrix * Tall,vector * real,XPoint * screen_image)1106 translate_point(hyperstruct * hp, matrix * Tall, vector * real, XPoint * screen_image)
1107 {
1108 vector *image;
1109
1110 if ((image = (vector *) malloc(hp->num_mat * sizeof (vector))) == NULL)
1111 return False;
1112 MatVecMult(Tall, real, image, hp->num_mat);
1113 screen_image->x = (short) image[0];
1114 screen_image->y = (short) image[1];
1115
1116 free(image);
1117 return True;
1118 }
1119
1120 static Bool
translate_points(ModeInfo * mi,int set)1121 translate_points(ModeInfo * mi, int set)
1122 {
1123 hyperstruct *hp = &hypers[MI_SCREEN(mi)];
1124 int i;
1125
1126 if (!calc_transformation(mi))
1127 return False;
1128 for (i = 0; i < hp->num_points; i++) {
1129 if (!translate_point(hp, hp->Tall, &hp->points[i * hp->num_mat],
1130 &hp->xpoints[set][i]))
1131 return False;
1132 if (MI_IS_USE3D(mi)) {
1133 if (!translate_point(hp, hp->Tallleft,
1134 &hp->pointsleft[i * hp->num_mat],
1135 &hp->xpointsleft[set][i]))
1136 return False;
1137 }
1138 }
1139 return True;
1140 }
1141
1142 ENTRYPOINT void
refresh_hyper(ModeInfo * mi)1143 refresh_hyper(ModeInfo * mi)
1144 {
1145 hyperstruct *hp = &hypers[MI_SCREEN(mi)];
1146
1147 if (!hp->painted) {
1148 MI_CLEARWINDOW(mi);
1149 hp->redrawing = True;
1150 draw_hyper_step(mi, hp->this_set);
1151 hp->redrawing = False;
1152 hp->painted = True;
1153 }
1154 }
1155
1156 ENTRYPOINT void
init_hyper(ModeInfo * mi)1157 init_hyper(ModeInfo * mi)
1158 {
1159 Display *display = MI_DISPLAY(mi);
1160 int i;
1161 hyperstruct *hp;
1162
1163 MI_INIT(mi, hypers);
1164 hp = &hypers[MI_SCREEN(mi)];
1165
1166 if (!init_x_stuff(mi)) {
1167 free_hyper_screen(display, hp);
1168 return;
1169 }
1170
1171 if (!figure_points(mi)) {
1172 free_hyper_screen(display, hp);
1173 return;
1174 }
1175
1176 /*
1177 * Fix the d+1 coord of all points.
1178 */
1179 for (i = 0; i < hp->num_points; i++) {
1180 hp->points[i * hp->num_mat + hp->num_d] = 1;
1181 if (MI_IS_USE3D(mi))
1182 hp->pointsleft[i * hp->num_mat + hp->num_d] = 1;
1183 }
1184
1185 figure_axis_points(hp);
1186
1187 hp->this_set = 0;
1188 if (!translate_points(mi, !hp->this_set)) {
1189 free_hyper_screen(display, hp);
1190 return;
1191 }
1192 if (!translate_points(mi, hp->this_set)) {
1193 free_hyper_screen(display, hp);
1194 return;
1195 }
1196 refresh_hyper(mi);
1197 hp->painted = True;
1198 hp->stationary = True;
1199 }
1200
1201
1202 ENTRYPOINT void
draw_hyper(ModeInfo * mi)1203 draw_hyper(ModeInfo * mi)
1204 {
1205 hyperstruct *hp;
1206
1207 if (hypers == NULL)
1208 return;
1209 hp = &hypers[MI_SCREEN(mi)];
1210 if (hp->axis_points == NULL)
1211 return;
1212
1213 hp->painted = False;
1214 draw_hyper_step(mi, hp->this_set);
1215
1216 /* Set up next place */
1217 move_hyper(mi);
1218 if (!translate_points(mi, hp->this_set)) {
1219 free_hyper_screen(MI_DISPLAY(mi), hp);
1220 return;
1221 }
1222 if (!hp->stationary)
1223 hp->this_set = !hp->this_set;
1224 }
1225
1226 ENTRYPOINT void
release_hyper(ModeInfo * mi)1227 release_hyper(ModeInfo * mi)
1228 {
1229 if (hypers != NULL) {
1230 int screen;
1231
1232 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1233 free_hyper_screen(MI_DISPLAY(mi), &hypers[screen]);
1234 free(hypers);
1235 hypers = (hyperstruct *) NULL;
1236 }
1237 }
1238
1239
1240 #ifndef STANDALONE
1241 ENTRYPOINT void
change_hyper(ModeInfo * mi)1242 change_hyper(ModeInfo * mi)
1243 {
1244 hyperstruct *hp = &hypers[MI_SCREEN(mi)];
1245
1246 /* make it change */
1247 hp->spinDelay = 0;
1248 }
1249 #endif
1250
1251 XSCREENSAVER_MODULE ("Hyper", hyper)
1252
1253 #endif /* MODE_hyper */
1254