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 gltrackball_rotate (bp->trackball);
1022 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
1024 if (bp->twodee_p && do_spin) /* face front */
1027 get_position (bp->rot2, &x, &y, &z2, !bp->button_down_p);
1028 glRotatef (max/2 - x*max, 1, 0, 0);
1029 glRotatef (max/2 - y*max, 0, 1, 0);
1030 glRotatef (z * 360, 0, 0, 1); /* but upside down is ok */
1034 glRotatef (x * 360, 1, 0, 0);
1035 glRotatef (y * 360, 0, 1, 0);
1036 glRotatef (z * 360, 0, 0, 1);
1040 mi->polygon_count = 0;
1042 glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
1043 glMateriali (GL_FRONT, GL_SHININESS, bshiny);
1044 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor);
1047 glTranslatef (0.1, 0.1, 0);
1049 if (!do_spin && !bp->twodee_p)
1051 /* tilt the cube a little, and make the start and end visible */
1052 glTranslatef (-0.1, 0, 0);
1053 glRotatef (140, 0, 1, 0);
1054 glRotatef (30, 1, 0, 0);
1058 glLineWidth (bp->depth > 4 ? 1.0 :
1059 bp->depth > 3 ? 2.0 :
1062 if (bp->path_tick > 0) /* advancing the end point, [0 - N) */
1063 { /* drawing 1 partial path, 1st time only. */
1065 if (!bp->button_down_p)
1066 bp->path_end += 0.01 * speed;
1068 if (bp->path_end >= 1.0)
1070 bp->path_start = 0.0;
1073 bp->pause = PAUSE_TICKS;
1076 bp->diam = thickness / pow (2, bp->depth);
1077 hilbert (mi, bp->depth, bp->path_start, bp->path_end);
1078 mi->recursion_depth = bp->depth + bp->path_start;
1081 else /* advancing the start point, (N - 1] */
1082 { /* drawing 2 paths at different rez. */
1089 if (!bp->button_down_p)
1090 bp->path_start += 0.01 * speed;
1092 if (bp->path_start > 1.0)
1094 bp->path_start = 0.0;
1095 bp->depth += bp->depth_tick;
1096 bp->pause = PAUSE_TICKS;
1097 if (bp->depth > max_depth-1)
1099 bp->depth = max_depth;
1100 bp->depth_tick = -1;
1102 else if (bp->depth <= 1)
1111 GLfloat d1 = thickness / pow (2, bp->depth);
1112 GLfloat d2 = thickness / pow (2, bp->depth + bp->depth_tick);
1113 bp->diam = (d1 * (1 - bp->path_start) +
1114 d2 * bp->path_start);
1117 /* First time around, start is 0, and end animates forward.
1118 Then, to display the next higher resolution, we render
1119 depth=N while increasing start and leaving end=1.0;
1120 and simultaneously animationg depth=N+1 with start=0 and
1121 end increasing by the same amount.
1123 The two paths aren't actually connected together at the
1124 resolution-change interface, and sometimes they overlap,
1125 but things go fast enough that it's hard to spot those
1126 glitches, so it looks ok.
1128 glPolygonOffset (0, 0);
1129 hilbert (mi, bp->depth, bp->path_start, bp->path_end);
1131 glPolygonOffset (1.0, 1.0);
1132 hilbert (mi, bp->depth + bp->depth_tick, 0.0, bp->path_start);
1134 mi->recursion_depth = bp->depth + (bp->depth_tick * bp->path_start);
1139 if (mi->fps_p) do_fps (mi);
1142 glXSwapBuffers(dpy, window);
1145 XSCREENSAVER_MODULE ("Hilbert", hilbert)