1 /* hilbert, Copyright (c) 2011 Jamie Zawinski <jwz@jwz.org>
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
11 * 2D and 3D Hilbert space-filling curves.
13 * Inspired by "Visualizing Hilbert Curves" by Nelson Max, 1998:
14 * https://e-reports-ext.llnl.gov/pdf/234149.pdf
17 #define DEFAULTS "*delay: 30000 \n" \
19 "*showFPS: False \n" \
20 "*wireframe: False \n" \
21 "*geometry: 800x800\n"
23 # define refresh_hilbert 0
24 # define release_hilbert 0
26 #define countof(x) (sizeof((x))/sizeof((*x)))
28 #include "xlockmore.h"
33 #include "gltrackball.h"
36 #ifdef USE_GL /* whole file */
39 #define DEF_SPIN "True"
40 #define DEF_WANDER "False"
41 #define DEF_SPEED "1.0"
42 #define DEF_MODE "random"
43 #define DEF_ENDS "random"
44 #define DEF_MAX_DEPTH "5"
45 #define DEF_THICKNESS "0.25"
47 #define PAUSE_TICKS 180
59 XYZb *values; /* assume max depth of 20 (2^16 on each side) */
63 static int dlist_faces[] = { 20, 10, 8, 4, 3 };
67 GLXContext *glx_context;
69 trackball_state *trackball;
79 GLfloat path_start, path_end;
84 hilbert_cache **caches; /* filled lazily */
86 GLuint dlists [40][2]; /* we don't actually alloc all of these */
87 int dlist_polys [40][2];
89 } hilbert_configuration;
91 static hilbert_configuration *bps = NULL;
95 static Bool do_wander;
96 static char *mode_str;
97 static char *ends_str;
99 static GLfloat thickness;
101 static XrmOptionDescRec opts[] = {
102 { "-spin", ".spin", XrmoptionNoArg, "True" },
103 { "+spin", ".spin", XrmoptionNoArg, "False" },
104 { "-speed", ".speed", XrmoptionSepArg, 0 },
105 { "-wander", ".wander", XrmoptionNoArg, "True" },
106 { "+wander", ".wander", XrmoptionNoArg, "False" },
107 { "-mode", ".mode", XrmoptionSepArg, 0 },
108 { "-2d", ".mode", XrmoptionNoArg, "2D" },
109 { "-3d", ".mode", XrmoptionNoArg, "3D" },
110 { "-ends", ".ends", XrmoptionSepArg, 0 },
111 { "-closed", ".ends", XrmoptionNoArg, "closed" },
112 { "-open", ".ends", XrmoptionNoArg, "open" },
113 { "-max-depth", ".maxDepth", XrmoptionSepArg, 0 },
114 { "-thickness", ".thickness",XrmoptionSepArg, 0 },
117 static argtype vars[] = {
118 {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
119 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
120 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
121 {&mode_str, "mode", "Mode", DEF_MODE, t_String},
122 {&ends_str, "ends", "Ends", DEF_ENDS, t_String},
123 {&max_depth, "maxDepth", "MaxDepth", DEF_MAX_DEPTH, t_Int},
124 {&thickness, "thickness","Thickness",DEF_THICKNESS, t_Float},
127 ENTRYPOINT ModeSpecOpt hilbert_opts = {countof(opts), opts, countof(vars), vars, NULL};
130 /* 2D Hilbert, and closed-loop variant.
133 /* These arrays specify which bits of the T index contribute to the
134 X, Y and Z values. Higher order bits are defined recursively.
136 static const int xbit2d[4] = { 0, 0, 1, 1 };
137 static const int ybit2d[4] = { 0, 1, 1, 0 };
139 static const int xbit3d[8] = { 0,0,0,0,1,1,1,1 };
140 static const int ybit3d[8] = { 0,1,1,0,0,1,1,0 };
141 static const int zbit3d[8] = { 0,0,1,1,1,1,0,0 };
143 /* These matrixes encapsulate the necessary reflection and translation
144 of each 4 sub-squares or 8 sub-cubes. The r2d and r3d arrays are
145 the normal Hilbert descent, and the s2d and s3d arrays are the
146 modified curve used for only level 0 of a closed-form path.
149 static int r2d[4][2][2] = {
166 static int s2d[4][2][2] = {
168 | |..| | Used for outermost iteration only, in "closed" mode
184 static int r3d[8][3][3] = {
220 static int s3d[8][3][3] = {
222 /| /| Used for outermost iteration only, in "closed" mode
262 matrix_times_vector2d (int m[2][2], int v[2], int dest[2])
264 dest[0] = m[0][0]*v[0] + m[0][1]*v[1];
265 dest[1] = m[1][0]*v[0] + m[1][1]*v[1];
269 matrix_times_vector3d (int m[3][3], int v[3], int dest[3])
271 dest[0] = m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]*v[2];
272 dest[1] = m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]*v[2];
273 dest[2] = m[2][0]*v[0] + m[2][1]*v[1] + m[2][2]*v[2];
278 matrix_multiply2d (int m1[2][2], int m2[2][2], int dest[2][2])
280 dest[0][0] = m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0];
281 dest[0][1] = m1[0][0] * m2[0][1] + m1[0][1] * m2[1][1];
282 dest[1][0] = m1[1][0] * m2[0][0] + m1[1][1] * m2[1][0];
283 dest[1][1] = m1[1][0] * m2[0][1] + m1[1][1] * m2[1][1];
287 matrix_multiply3d (int m1[3][3], int m2[3][3], int dest[3][3])
290 for (i = 0; i < 3; i++)
291 for (j = 0; j < 3; j++)
294 for (k = 0; k < 3; k++)
295 dest[i][j] += m1[i][k] * m2[k][j];
300 identity_matrix2d (int m[2][2])
302 m[0][0] = m[1][1] = 1;
303 m[0][1] = m[1][0] = 0;
307 identity_matrix3d (int m[3][3])
309 m[0][0] = m[1][1] = m[2][2] = 1;
310 m[0][1] = m[0][2] = m[1][0] = m[1][2] = m[2][0] = m[2][1] = 0;
315 matrix_copy2d (int src[2][2], int dest[2][2])
318 for (i = 0; i < 2; i++)
319 for (j = 0; j < 2; j++)
320 dest[i][j] = src[i][j];
325 matrix_copy3d (int src[3][3], int dest[3][3])
328 for (i = 0; i < 3; i++)
329 for (j = 0; j < 3; j++)
330 dest[i][j] = src[i][j];
334 /* 2D and 3D Hilbert:
335 Given an index T along the curve, return the XY or XYZ coordinates.
336 N is depth of the curve.
340 t_to_xy (int n, int t, int *x, int *y, Bool closed_p)
342 int k, rt[2][2], rq[2][2], va[2], vb[2];
343 identity_matrix2d(rt);
346 for (k = n-1; k >= 0; k--)
348 int j = 3 & (t >> (2*k));
349 va[0] = 2 * xbit2d[j] - 1;
350 va[1] = 2 * ybit2d[j] - 1;
351 matrix_times_vector2d (rt, va, vb);
352 *x += ((vb[0] + 1) / 2) << k;
353 *y += ((vb[1] + 1) / 2) << k;
356 matrix_copy2d (rt, rq);
357 if (k == n-1 && closed_p)
358 matrix_multiply2d (rq, s2d[j], rt);
360 matrix_multiply2d (rq, r2d[j], rt);
367 t_to_xyz (int n, int t, int *x, int *y, int *z, Bool closed_p)
369 int k, rt[3][3], rq[3][3], va[3], vb[3];
370 identity_matrix3d(rt);
373 for (k = n-1; k >= 0; k--)
375 int j = 7 & (t >> (3*k));
376 va[0] = 2 * xbit3d[j] - 1;
377 va[1] = 2 * ybit3d[j] - 1;
378 va[2] = 2 * zbit3d[j] - 1;
379 matrix_times_vector3d (rt, va, vb);
380 *x += ((vb[0] + 1) / 2) << k;
381 *y += ((vb[1] + 1) / 2) << k;
382 *z += ((vb[2] + 1) / 2) << k;
385 matrix_copy3d (rt, rq);
386 if (k == n-1 && closed_p)
387 matrix_multiply3d (rq, s3d[j], rt);
389 matrix_multiply3d (rq, r3d[j], rt);
395 /* Rendering the curve
399 /* Draw a sphere at the intersection of two tubes, to round off
400 the connection between them. Use one of our cache display lists
401 of unit spheres in various sizes.
404 draw_joint (ModeInfo *mi, XYZ p, GLfloat diam, int faces, int wire)
406 hilbert_configuration *bp = &bps[MI_SCREEN(mi)];
408 diam *= 0.99; /* try to clean up the edges a bit */
410 if (faces <= 4) return 0; /* too small to see */
413 glTranslatef (p.x, p.y, p.z);
414 glScalef (diam, diam, diam);
416 /* i = unit_sphere (faces, faces, wire);*/
418 /* if (!bp->dlists[faces][0]) abort(); */
419 /* if (!bp->dlist_polys[faces][0]) abort(); */
421 glCallList (bp->dlists[faces][0]);
422 i = bp->dlist_polys[faces][0];
428 /* Draw a tube, and associated end caps. Use one of our cache display lists
429 of unit tubes in various sizes. Pick the resolution of the tube based
430 on how large it's being drawn. It's ok to get chunkier if the thing is
431 only a few pixels wide on the screen.
434 draw_segment (ModeInfo *mi,
435 XYZ p0, XYZ p1, /* from, to */
436 int t, int end_t, /* value and range */
437 GLfloat path_start, GLfloat path_end, /* clipping */
441 hilbert_configuration *bp = &bps[MI_SCREEN(mi)];
443 double t0 = (double) (t-1) / end_t; /* ratio of p[01] along curve */
444 double t1 = (double) t / end_t;
446 int wire = MI_IS_WIREFRAME(mi);
448 GLfloat dd = bp->diam;
451 if (path_start >= t1) /* whole segment is before clipping region */
453 if (path_end < t0) /* whole segment is after clipping region */
457 if (bp->twodee_p) dd *= 2; /* more polys in 2D mode */
459 faces = (dd > 0.040 ? dlist_faces[0] :
460 dd > 0.020 ? dlist_faces[1] :
461 dd > 0.010 ? dlist_faces[2] :
462 dd > 0.005 ? dlist_faces[3] :
463 dd > 0.002 ? dlist_faces[4] :
466 /* In 2d mode, we can drop into wireframe mode and it still looks ok... */
477 glDisable (GL_DEPTH_TEST);
478 glDisable (GL_CULL_FACE);
479 glDisable (GL_LIGHTING);
485 GLfloat seg_range = t1 - t0;
486 GLfloat clip_range = path_start - t0;
487 GLfloat ratio = clip_range / seg_range;
488 p.x = p0.x + ((p1.x - p0.x) * ratio);
489 p.y = p0.y + ((p1.y - p0.y) * ratio);
490 p.z = p0.z + ((p1.z - p0.z) * ratio);
497 GLfloat seg_range = t1 - t0;
498 GLfloat clip_range = path_end - t0;
499 GLfloat ratio = clip_range / seg_range;
500 p.x = p0.x + ((p1.x - p0.x) * ratio);
501 p.y = p0.y + ((p1.y - p0.y) * ratio);
502 p.z = p0.z + ((p1.z - p0.z) * ratio);
512 int segs = bp->ncolors * (t1 - t0);
516 if (segs < 1) segs = 1;
518 for (i = 0; i < segs; i++)
521 GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
525 p0b.x = p0.x + i * ((p1.x - p0.x) / segs);
526 p0b.y = p0.y + i * ((p1.y - p0.y) / segs);
527 p0b.z = p0.z + i * ((p1.z - p0.z) / segs);
529 p1b.x = p0.x + j * ((p1.x - p0.x) / segs);
530 p1b.y = p0.y + j * ((p1.y - p0.y) / segs);
531 p1b.z = p0.z + j * ((p1.z - p0.z) / segs);
535 /* #### this isn't quite right */
536 t0b = t0 + i * (t1 - t0) / segs;
538 c = bp->ncolors * t0b;
539 if (c >= bp->ncolors) c = bp->ncolors-1;
541 /* Above depth 6, was spending 5% of the time in glMateralfv,
542 so only set the color if it's different. */
544 if (c != *last_colorP)
546 color[0] = bp->colors[c].red / 65536.0;
547 color[1] = bp->colors[c].green / 65536.0;
548 color[2] = bp->colors[c].blue / 65536.0;
552 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
560 glVertex3f (p0b.x, p0b.y, p0b.z);
561 glVertex3f (p1b.x, p1b.y, p1b.z);
567 /* mi->polygon_count += tube (p0b.x, p0b.y, p0b.z,
569 bp->diam, 0, faces, True,
573 /* Render a rotated and scaled prefab unit tube.
575 There's probably a slightly more concise way to do this,
576 but since they're all at right angles at least we don't
581 glTranslatef (p0b.x, p0b.y, p0b.z);
587 else if (p1b.x < p0b.x)
589 glRotatef (180, 0, 0, 1);
592 else if (p1b.y > p0b.y) {
593 glRotatef (90, 0, 0, 1);
596 else if (p1b.y < p0b.y)
598 glRotatef (-90, 0, 0, 1);
601 else if (p1b.z > p0b.z)
603 glRotatef (-90, 0, 1, 0);
606 else /* (p1b.z < p0b.z) */
608 glRotatef (90, 0, 1, 0);
612 glScalef (s, bp->diam, bp->diam);
613 glCallList (bp->dlists[faces][1]);
614 mi->polygon_count += bp->dlist_polys[faces][1];
618 /* If this is the bleeding edge, cap it too. */
620 mi->polygon_count += draw_joint (mi, p0b, bp->diam, faces, wire);
626 /* If the path is closed, close it. This segment doesn't animate
627 like the others, but, oh well.
630 mi->polygon_count += draw_joint (mi, p1b, bp->diam, faces, wire);
640 fprintf (stderr, "%s: out of memory\n", progname);
645 /* Render the whole thing, but omit segments that lie outside of
646 the path_start and path_end ratios.
649 hilbert (ModeInfo *mi, int size, GLfloat path_start, GLfloat path_end)
651 hilbert_configuration *bp = &bps[MI_SCREEN(mi)];
652 int wire = MI_IS_WIREFRAME(mi);
654 GLfloat w = pow(2, size);
655 int end_t = (bp->twodee_p ? w * w : w * w * w);
659 Bool first_p = False;
662 Bool fill_cache_p = False;
666 /* Do this here since at higher resolutions, we turn wireframe on
671 glEnable (GL_NORMALIZE);
672 glEnable (GL_DEPTH_TEST);
673 glEnable (GL_CULL_FACE);
674 glEnable (GL_LIGHTING);
675 glEnable (GL_POLYGON_OFFSET_FILL);
681 bp->caches = (hilbert_cache **)
682 calloc (max_depth + 2, sizeof (*bp->caches));
683 if (!bp->caches) mem();
687 cc = bp->caches[size];
690 cc = (hilbert_cache *) calloc (1, sizeof (*cc));
691 cc->values = (XYZb *) calloc (end_t + 1, sizeof (*cc->values));
692 if (!cc->values) mem();
694 bp->caches[size] = cc;
698 for (t = 0; t < end_t; t++)
714 t_to_xyz (size, t, &x, &y, &z, bp->closed_p);
717 t_to_xy (size, t, &x, &y, bp->closed_p);
726 c.x = (GLfloat) x / w - 0.5;
727 c.y = (GLfloat) y / w - 0.5;
728 c.z = (GLfloat) z / w - 0.5;
730 /* #### We could save some polygons by not drawing the spheres
731 between colinear segments. */
734 if (draw_segment (mi, prev, c, t, end_t, path_start, path_end, !any_p,
745 if (bp->closed_p && path_end >= 1.0) {
746 draw_segment (mi, prev, first, t, end_t, path_start, path_end, !any_p,
753 /* Window management, etc
756 reshape_hilbert (ModeInfo *mi, int width, int height)
758 GLfloat h = (GLfloat) height / (GLfloat) width;
760 glViewport (0, 0, (GLint) width, (GLint) height);
762 glMatrixMode(GL_PROJECTION);
764 gluPerspective (30.0, 1/h, 1.0, 100.0);
766 glMatrixMode(GL_MODELVIEW);
768 gluLookAt( 0.0, 0.0, 30.0,
772 glClear(GL_COLOR_BUFFER_BIT);
777 hilbert_handle_event (ModeInfo *mi, XEvent *event)
779 hilbert_configuration *bp = &bps[MI_SCREEN(mi)];
781 if (event->xany.type == ButtonPress &&
782 event->xbutton.button == Button1)
784 bp->button_down_p = True;
785 gltrackball_start (bp->trackball,
786 event->xbutton.x, event->xbutton.y,
787 MI_WIDTH (mi), MI_HEIGHT (mi));
790 else if (event->xany.type == ButtonRelease &&
791 event->xbutton.button == Button1)
793 bp->button_down_p = False;
796 else if (event->xany.type == ButtonPress &&
797 (event->xbutton.button == Button4 ||
798 event->xbutton.button == Button5 ||
799 event->xbutton.button == Button6 ||
800 event->xbutton.button == Button7))
802 gltrackball_mousewheel (bp->trackball, event->xbutton.button, 10,
803 !!event->xbutton.state);
806 else if (event->xany.type == MotionNotify &&
809 gltrackball_track (bp->trackball,
810 event->xmotion.x, event->xmotion.y,
811 MI_WIDTH (mi), MI_HEIGHT (mi));
814 else if (event->xany.type == KeyPress)
818 XLookupString (&event->xkey, &c, 1, &keysym, 0);
819 if (c == '+' || c == '=')
822 if (bp->depth > max_depth) bp->depth = max_depth;
825 else if (c == '-' || c == '_')
828 if (bp->depth < 1) bp->depth = 1;
838 init_hilbert (ModeInfo *mi)
840 hilbert_configuration *bp;
841 int wire = MI_IS_WIREFRAME(mi);
845 bps = (hilbert_configuration *)
846 calloc (MI_NUM_SCREENS(mi), sizeof (hilbert_configuration));
848 fprintf(stderr, "%s: out of memory\n", progname);
853 bp = &bps[MI_SCREEN(mi)];
857 bp->path_start = 0.0;
861 if (thickness < 0.01) thickness = 0.01;
862 if (thickness > 1.0) thickness = 1.0;
864 if (speed <= 0) speed = 0.0001;
865 if (max_depth < 2) max_depth = 2;
866 if (max_depth > 20) max_depth = 20; /* hard limit due to hilbert_cache */
868 if (bp->depth > max_depth-1) bp->depth = max_depth-1;
870 bp->glx_context = init_GL(mi);
872 reshape_hilbert (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
876 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
877 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
878 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
879 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
881 glEnable (GL_LIGHTING);
882 glEnable (GL_LIGHT0);
884 glLightfv(GL_LIGHT0, GL_POSITION, pos);
885 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
886 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
887 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
891 double spin_speed = 0.04;
892 double tilt_speed = spin_speed / 10;
893 double wander_speed = 0.008;
894 double spin_accel = 0.01;
896 bp->rot = make_rotator (do_spin ? spin_speed : 0,
897 do_spin ? spin_speed : 0,
898 do_spin ? spin_speed : 0,
900 do_wander ? wander_speed : 0,
902 bp->rot2 = make_rotator (0, 0, 0, 0, tilt_speed, True);
903 bp->trackball = gltrackball_init ();
906 if (mode_str && !strcasecmp(mode_str, "2d"))
908 else if (mode_str && (!strcasecmp(mode_str, "3d")))
909 bp->twodee_p = False;
912 if (! (mode_str && !strcasecmp(mode_str, "random")))
913 fprintf (stderr, "%s: 'mode' must be '2d', '3d', or 'random'\n",
915 bp->twodee_p = ((random() % 3) == 0);
919 if (ends_str && (!strcasecmp(ends_str, "closed")))
921 else if (ends_str && (!strcasecmp(ends_str, "open")))
922 bp->closed_p = False;
925 if (! (ends_str && !strcasecmp(ends_str, "random")))
926 fprintf (stderr, "%s: 'ends' must be 'open', 'closed', or 'random'\n",
928 bp->closed_p = ((random() % 3) != 0);
932 /* More colors results in more polygons (more tube segments) so keeping
933 this small is worthwhile. */
934 bp->ncolors = (bp->twodee_p ? 1024 : 128);
938 /* Since the path is closed, colors must also be a closed loop */
939 bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
940 make_smooth_colormap (0, 0, 0,
941 bp->colors, &bp->ncolors,
946 /* Since the path is open, first and last colors should differ. */
948 bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
949 make_uniform_colormap (0, 0, 0,
950 bp->colors, &bp->ncolors,
955 /* Generate display lists for several different resolutions of
956 a unit tube and a unit sphere.
958 for (i = 0; i < countof(dlist_faces); i++)
960 int faces = dlist_faces[i];
961 bp->dlists[faces][0] = glGenLists (1);
963 glNewList (bp->dlists[faces][0], GL_COMPILE);
964 bp->dlist_polys[faces][0] = unit_sphere (faces, faces, wire);
967 bp->dlists[faces][1] = glGenLists (1);
969 glNewList (bp->dlists[faces][1], GL_COMPILE);
970 bp->dlist_polys[faces][1] =
971 tube (0, 0, 0, 1, 0, 0,
972 1, 0, faces, True, 0, wire);
979 draw_hilbert (ModeInfo *mi)
981 hilbert_configuration *bp = &bps[MI_SCREEN(mi)];
982 Display *dpy = MI_DISPLAY(mi);
983 Window window = MI_WINDOW(mi);
984 int wire = MI_IS_WIREFRAME(mi);
986 static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
987 static const GLfloat bshiny = 128.0;
988 GLfloat bcolor[4] = {1.0, 1.0, 1.0, 1.0};
990 if (!bp->glx_context)
993 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
995 glShadeModel(GL_SMOOTH);
999 glEnable (GL_NORMALIZE);
1000 glEnable (GL_DEPTH_TEST);
1001 glEnable (GL_CULL_FACE);
1002 glEnable (GL_LIGHTING);
1003 glEnable (GL_LIGHT0);
1004 glEnable (GL_POLYGON_OFFSET_FILL);
1007 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1011 glScalef(1.1, 1.1, 1.1);
1015 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
1016 glTranslatef((x - 0.5) * 8,
1020 /* Do it twice because we don't track the device's orientation. */
1021 glRotatef( current_device_rotation(), 0, 0, 1);
1022 gltrackball_rotate (bp->trackball);
1023 glRotatef(-current_device_rotation(), 0, 0, 1);
1025 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
1027 if (bp->twodee_p && do_spin) /* face front */
1030 get_position (bp->rot2, &x, &y, &z2, !bp->button_down_p);
1031 glRotatef (max/2 - x*max, 1, 0, 0);
1032 glRotatef (max/2 - y*max, 0, 1, 0);
1033 glRotatef (z * 360, 0, 0, 1); /* but upside down is ok */
1037 glRotatef (x * 360, 1, 0, 0);
1038 glRotatef (y * 360, 0, 1, 0);
1039 glRotatef (z * 360, 0, 0, 1);
1043 mi->polygon_count = 0;
1045 glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
1046 glMateriali (GL_FRONT, GL_SHININESS, bshiny);
1047 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor);
1050 glTranslatef (0.1, 0.1, 0);
1052 if (!do_spin && !bp->twodee_p)
1054 /* tilt the cube a little, and make the start and end visible */
1055 glTranslatef (-0.1, 0, 0);
1056 glRotatef (140, 0, 1, 0);
1057 glRotatef (30, 1, 0, 0);
1061 glLineWidth (bp->depth > 4 ? 1.0 :
1062 bp->depth > 3 ? 2.0 :
1065 if (bp->path_tick > 0) /* advancing the end point, [0 - N) */
1066 { /* drawing 1 partial path, 1st time only. */
1068 if (!bp->button_down_p)
1069 bp->path_end += 0.01 * speed;
1071 if (bp->path_end >= 1.0)
1073 bp->path_start = 0.0;
1076 bp->pause = PAUSE_TICKS;
1079 bp->diam = thickness / pow (2, bp->depth);
1080 hilbert (mi, bp->depth, bp->path_start, bp->path_end);
1081 mi->recursion_depth = bp->depth + bp->path_start;
1084 else /* advancing the start point, (N - 1] */
1085 { /* drawing 2 paths at different rez. */
1092 if (!bp->button_down_p)
1093 bp->path_start += 0.01 * speed;
1095 if (bp->path_start > 1.0)
1097 bp->path_start = 0.0;
1098 bp->depth += bp->depth_tick;
1099 bp->pause = PAUSE_TICKS;
1100 if (bp->depth > max_depth-1)
1102 bp->depth = max_depth;
1103 bp->depth_tick = -1;
1105 else if (bp->depth <= 1)
1114 GLfloat d1 = thickness / pow (2, bp->depth);
1115 GLfloat d2 = thickness / pow (2, bp->depth + bp->depth_tick);
1116 bp->diam = (d1 * (1 - bp->path_start) +
1117 d2 * bp->path_start);
1120 /* First time around, start is 0, and end animates forward.
1121 Then, to display the next higher resolution, we render
1122 depth=N while increasing start and leaving end=1.0;
1123 and simultaneously animationg depth=N+1 with start=0 and
1124 end increasing by the same amount.
1126 The two paths aren't actually connected together at the
1127 resolution-change interface, and sometimes they overlap,
1128 but things go fast enough that it's hard to spot those
1129 glitches, so it looks ok.
1131 glPolygonOffset (0, 0);
1132 hilbert (mi, bp->depth, bp->path_start, bp->path_end);
1134 glPolygonOffset (1.0, 1.0);
1135 hilbert (mi, bp->depth + bp->depth_tick, 0.0, bp->path_start);
1137 mi->recursion_depth = bp->depth + (bp->depth_tick * bp->path_start);
1142 if (mi->fps_p) do_fps (mi);
1145 glXSwapBuffers(dpy, window);
1148 XSCREENSAVER_MODULE ("Hilbert", hilbert)