1 /* hilbert, Copyright (c) 2011-2014 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" \
22 "*suppressRotationAnimation: True\n" \
24 # define refresh_hilbert 0
25 # define release_hilbert 0
27 #define countof(x) (sizeof((x))/sizeof((*x)))
29 #include "xlockmore.h"
34 #include "gltrackball.h"
37 #ifdef USE_GL /* whole file */
40 #define DEF_SPIN "True"
41 #define DEF_WANDER "False"
42 #define DEF_SPEED "1.0"
43 #define DEF_MODE "random"
44 #define DEF_ENDS "random"
45 #define DEF_MAX_DEPTH "5"
46 #define DEF_THICKNESS "0.25"
48 #define PAUSE_TICKS 180
60 XYZb *values; /* assume max depth of 20 (2^16 on each side) */
64 static int dlist_faces[] = { 20, 10, 8, 4, 3 };
68 GLXContext *glx_context;
70 trackball_state *trackball;
80 GLfloat path_start, path_end;
85 hilbert_cache **caches; /* filled lazily */
87 GLuint dlists [40][2]; /* we don't actually alloc all of these */
88 int dlist_polys [40][2];
90 } hilbert_configuration;
92 static hilbert_configuration *bps = NULL;
96 static Bool do_wander;
97 static char *mode_str;
98 static char *ends_str;
100 static GLfloat thickness;
102 static XrmOptionDescRec opts[] = {
103 { "-spin", ".spin", XrmoptionNoArg, "True" },
104 { "+spin", ".spin", XrmoptionNoArg, "False" },
105 { "-speed", ".speed", XrmoptionSepArg, 0 },
106 { "-wander", ".wander", XrmoptionNoArg, "True" },
107 { "+wander", ".wander", XrmoptionNoArg, "False" },
108 { "-mode", ".mode", XrmoptionSepArg, 0 },
109 { "-2d", ".mode", XrmoptionNoArg, "2D" },
110 { "-3d", ".mode", XrmoptionNoArg, "3D" },
111 { "-ends", ".ends", XrmoptionSepArg, 0 },
112 { "-closed", ".ends", XrmoptionNoArg, "closed" },
113 { "-open", ".ends", XrmoptionNoArg, "open" },
114 { "-max-depth", ".maxDepth", XrmoptionSepArg, 0 },
115 { "-thickness", ".thickness",XrmoptionSepArg, 0 },
118 static argtype vars[] = {
119 {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
120 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
121 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
122 {&mode_str, "mode", "Mode", DEF_MODE, t_String},
123 {&ends_str, "ends", "Ends", DEF_ENDS, t_String},
124 {&max_depth, "maxDepth", "MaxDepth", DEF_MAX_DEPTH, t_Int},
125 {&thickness, "thickness","Thickness",DEF_THICKNESS, t_Float},
128 ENTRYPOINT ModeSpecOpt hilbert_opts = {countof(opts), opts, countof(vars), vars, NULL};
131 /* 2D Hilbert, and closed-loop variant.
134 /* These arrays specify which bits of the T index contribute to the
135 X, Y and Z values. Higher order bits are defined recursively.
137 static const int xbit2d[4] = { 0, 0, 1, 1 };
138 static const int ybit2d[4] = { 0, 1, 1, 0 };
140 static const int xbit3d[8] = { 0,0,0,0,1,1,1,1 };
141 static const int ybit3d[8] = { 0,1,1,0,0,1,1,0 };
142 static const int zbit3d[8] = { 0,0,1,1,1,1,0,0 };
144 /* These matrixes encapsulate the necessary reflection and translation
145 of each 4 sub-squares or 8 sub-cubes. The r2d and r3d arrays are
146 the normal Hilbert descent, and the s2d and s3d arrays are the
147 modified curve used for only level 0 of a closed-form path.
150 static int r2d[4][2][2] = {
167 static int s2d[4][2][2] = {
169 | |..| | Used for outermost iteration only, in "closed" mode
185 static int r3d[8][3][3] = {
221 static int s3d[8][3][3] = {
223 /| /| Used for outermost iteration only, in "closed" mode
263 matrix_times_vector2d (int m[2][2], int v[2], int dest[2])
265 dest[0] = m[0][0]*v[0] + m[0][1]*v[1];
266 dest[1] = m[1][0]*v[0] + m[1][1]*v[1];
270 matrix_times_vector3d (int m[3][3], int v[3], int dest[3])
272 dest[0] = m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]*v[2];
273 dest[1] = m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]*v[2];
274 dest[2] = m[2][0]*v[0] + m[2][1]*v[1] + m[2][2]*v[2];
279 matrix_multiply2d (int m1[2][2], int m2[2][2], int dest[2][2])
281 dest[0][0] = m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0];
282 dest[0][1] = m1[0][0] * m2[0][1] + m1[0][1] * m2[1][1];
283 dest[1][0] = m1[1][0] * m2[0][0] + m1[1][1] * m2[1][0];
284 dest[1][1] = m1[1][0] * m2[0][1] + m1[1][1] * m2[1][1];
288 matrix_multiply3d (int m1[3][3], int m2[3][3], int dest[3][3])
291 for (i = 0; i < 3; i++)
292 for (j = 0; j < 3; j++)
295 for (k = 0; k < 3; k++)
296 dest[i][j] += m1[i][k] * m2[k][j];
301 identity_matrix2d (int m[2][2])
303 m[0][0] = m[1][1] = 1;
304 m[0][1] = m[1][0] = 0;
308 identity_matrix3d (int m[3][3])
310 m[0][0] = m[1][1] = m[2][2] = 1;
311 m[0][1] = m[0][2] = m[1][0] = m[1][2] = m[2][0] = m[2][1] = 0;
316 matrix_copy2d (int src[2][2], int dest[2][2])
319 for (i = 0; i < 2; i++)
320 for (j = 0; j < 2; j++)
321 dest[i][j] = src[i][j];
326 matrix_copy3d (int src[3][3], int dest[3][3])
329 for (i = 0; i < 3; i++)
330 for (j = 0; j < 3; j++)
331 dest[i][j] = src[i][j];
335 /* 2D and 3D Hilbert:
336 Given an index T along the curve, return the XY or XYZ coordinates.
337 N is depth of the curve.
341 t_to_xy (int n, int t, int *x, int *y, Bool closed_p)
343 int k, rt[2][2], rq[2][2], va[2], vb[2];
344 identity_matrix2d(rt);
347 for (k = n-1; k >= 0; k--)
349 int j = 3 & (t >> (2*k));
350 va[0] = 2 * xbit2d[j] - 1;
351 va[1] = 2 * ybit2d[j] - 1;
352 matrix_times_vector2d (rt, va, vb);
353 *x += ((vb[0] + 1) / 2) << k;
354 *y += ((vb[1] + 1) / 2) << k;
357 matrix_copy2d (rt, rq);
358 if (k == n-1 && closed_p)
359 matrix_multiply2d (rq, s2d[j], rt);
361 matrix_multiply2d (rq, r2d[j], rt);
368 t_to_xyz (int n, int t, int *x, int *y, int *z, Bool closed_p)
370 int k, rt[3][3], rq[3][3], va[3], vb[3];
371 identity_matrix3d(rt);
374 for (k = n-1; k >= 0; k--)
376 int j = 7 & (t >> (3*k));
377 va[0] = 2 * xbit3d[j] - 1;
378 va[1] = 2 * ybit3d[j] - 1;
379 va[2] = 2 * zbit3d[j] - 1;
380 matrix_times_vector3d (rt, va, vb);
381 *x += ((vb[0] + 1) / 2) << k;
382 *y += ((vb[1] + 1) / 2) << k;
383 *z += ((vb[2] + 1) / 2) << k;
386 matrix_copy3d (rt, rq);
387 if (k == n-1 && closed_p)
388 matrix_multiply3d (rq, s3d[j], rt);
390 matrix_multiply3d (rq, r3d[j], rt);
396 /* Rendering the curve
400 /* Draw a sphere at the intersection of two tubes, to round off
401 the connection between them. Use one of our cache display lists
402 of unit spheres in various sizes.
405 draw_joint (ModeInfo *mi, XYZ p, GLfloat diam, int faces, int wire)
407 hilbert_configuration *bp = &bps[MI_SCREEN(mi)];
409 diam *= 0.99; /* try to clean up the edges a bit */
411 if (faces <= 4) return 0; /* too small to see */
414 glTranslatef (p.x, p.y, p.z);
415 glScalef (diam, diam, diam);
417 /* i = unit_sphere (faces, faces, wire);*/
419 /* if (!bp->dlists[faces][0]) abort(); */
420 /* if (!bp->dlist_polys[faces][0]) abort(); */
422 glCallList (bp->dlists[faces][0]);
423 i = bp->dlist_polys[faces][0];
429 /* Draw a tube, and associated end caps. Use one of our cache display lists
430 of unit tubes in various sizes. Pick the resolution of the tube based
431 on how large it's being drawn. It's ok to get chunkier if the thing is
432 only a few pixels wide on the screen.
435 draw_segment (ModeInfo *mi,
436 XYZ p0, XYZ p1, /* from, to */
437 int t, int end_t, /* value and range */
438 GLfloat path_start, GLfloat path_end, /* clipping */
442 hilbert_configuration *bp = &bps[MI_SCREEN(mi)];
444 double t0 = (double) (t-1) / end_t; /* ratio of p[01] along curve */
445 double t1 = (double) t / end_t;
447 int wire = MI_IS_WIREFRAME(mi);
449 GLfloat dd = bp->diam;
452 if (path_start >= t1) /* whole segment is before clipping region */
454 if (path_end < t0) /* whole segment is after clipping region */
458 if (bp->twodee_p) dd *= 2; /* more polys in 2D mode */
460 faces = (dd > 0.040 ? dlist_faces[0] :
461 dd > 0.020 ? dlist_faces[1] :
462 dd > 0.010 ? dlist_faces[2] :
463 dd > 0.005 ? dlist_faces[3] :
464 dd > 0.002 ? dlist_faces[4] :
467 /* In 2d mode, we can drop into wireframe mode and it still looks ok... */
478 glDisable (GL_DEPTH_TEST);
479 glDisable (GL_CULL_FACE);
480 glDisable (GL_LIGHTING);
486 GLfloat seg_range = t1 - t0;
487 GLfloat clip_range = path_start - t0;
488 GLfloat ratio = clip_range / seg_range;
489 p.x = p0.x + ((p1.x - p0.x) * ratio);
490 p.y = p0.y + ((p1.y - p0.y) * ratio);
491 p.z = p0.z + ((p1.z - p0.z) * ratio);
498 GLfloat seg_range = t1 - t0;
499 GLfloat clip_range = path_end - t0;
500 GLfloat ratio = clip_range / seg_range;
501 p.x = p0.x + ((p1.x - p0.x) * ratio);
502 p.y = p0.y + ((p1.y - p0.y) * ratio);
503 p.z = p0.z + ((p1.z - p0.z) * ratio);
513 int segs = bp->ncolors * (t1 - t0);
517 if (segs < 1) segs = 1;
519 for (i = 0; i < segs; i++)
522 GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
526 p0b.x = p0.x + i * ((p1.x - p0.x) / segs);
527 p0b.y = p0.y + i * ((p1.y - p0.y) / segs);
528 p0b.z = p0.z + i * ((p1.z - p0.z) / segs);
530 p1b.x = p0.x + j * ((p1.x - p0.x) / segs);
531 p1b.y = p0.y + j * ((p1.y - p0.y) / segs);
532 p1b.z = p0.z + j * ((p1.z - p0.z) / segs);
536 /* #### this isn't quite right */
537 t0b = t0 + i * (t1 - t0) / segs;
539 c = bp->ncolors * t0b;
540 if (c >= bp->ncolors) c = bp->ncolors-1;
542 /* Above depth 6, was spending 5% of the time in glMateralfv,
543 so only set the color if it's different. */
545 if (c != *last_colorP)
547 color[0] = bp->colors[c].red / 65536.0;
548 color[1] = bp->colors[c].green / 65536.0;
549 color[2] = bp->colors[c].blue / 65536.0;
553 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
561 glVertex3f (p0b.x, p0b.y, p0b.z);
562 glVertex3f (p1b.x, p1b.y, p1b.z);
568 /* mi->polygon_count += tube (p0b.x, p0b.y, p0b.z,
570 bp->diam, 0, faces, True,
574 /* Render a rotated and scaled prefab unit tube.
576 There's probably a slightly more concise way to do this,
577 but since they're all at right angles at least we don't
582 glTranslatef (p0b.x, p0b.y, p0b.z);
588 else if (p1b.x < p0b.x)
590 glRotatef (180, 0, 0, 1);
593 else if (p1b.y > p0b.y) {
594 glRotatef (90, 0, 0, 1);
597 else if (p1b.y < p0b.y)
599 glRotatef (-90, 0, 0, 1);
602 else if (p1b.z > p0b.z)
604 glRotatef (-90, 0, 1, 0);
607 else /* (p1b.z < p0b.z) */
609 glRotatef (90, 0, 1, 0);
613 glScalef (s, bp->diam, bp->diam);
614 glCallList (bp->dlists[faces][1]);
615 mi->polygon_count += bp->dlist_polys[faces][1];
619 /* If this is the bleeding edge, cap it too. */
621 mi->polygon_count += draw_joint (mi, p0b, bp->diam, faces, wire);
627 /* If the path is closed, close it. This segment doesn't animate
628 like the others, but, oh well.
631 mi->polygon_count += draw_joint (mi, p1b, bp->diam, faces, wire);
641 fprintf (stderr, "%s: out of memory\n", progname);
646 /* Render the whole thing, but omit segments that lie outside of
647 the path_start and path_end ratios.
650 hilbert (ModeInfo *mi, int size, GLfloat path_start, GLfloat path_end)
652 hilbert_configuration *bp = &bps[MI_SCREEN(mi)];
653 int wire = MI_IS_WIREFRAME(mi);
655 GLfloat w = pow(2, size);
656 int end_t = (bp->twodee_p ? w * w : w * w * w);
660 Bool first_p = False;
663 Bool fill_cache_p = False;
667 /* Do this here since at higher resolutions, we turn wireframe on
672 glEnable (GL_NORMALIZE);
673 glEnable (GL_DEPTH_TEST);
674 glEnable (GL_CULL_FACE);
675 glEnable (GL_LIGHTING);
676 glEnable (GL_POLYGON_OFFSET_FILL);
682 bp->caches = (hilbert_cache **)
683 calloc (max_depth + 2, sizeof (*bp->caches));
684 if (!bp->caches) mem();
688 cc = bp->caches[size];
691 cc = (hilbert_cache *) calloc (1, sizeof (*cc));
692 cc->values = (XYZb *) calloc (end_t + 1, sizeof (*cc->values));
693 if (!cc->values) mem();
695 bp->caches[size] = cc;
699 for (t = 0; t < end_t; t++)
715 t_to_xyz (size, t, &x, &y, &z, bp->closed_p);
718 t_to_xy (size, t, &x, &y, bp->closed_p);
727 c.x = (GLfloat) x / w - 0.5;
728 c.y = (GLfloat) y / w - 0.5;
729 c.z = (GLfloat) z / w - 0.5;
731 /* #### We could save some polygons by not drawing the spheres
732 between colinear segments. */
735 if (draw_segment (mi, prev, c, t, end_t, path_start, path_end, !any_p,
746 if (bp->closed_p && path_end >= 1.0) {
747 draw_segment (mi, prev, first, t, end_t, path_start, path_end, !any_p,
754 /* Window management, etc
757 reshape_hilbert (ModeInfo *mi, int width, int height)
759 GLfloat h = (GLfloat) height / (GLfloat) width;
761 glViewport (0, 0, (GLint) width, (GLint) height);
763 glMatrixMode(GL_PROJECTION);
765 gluPerspective (30.0, 1/h, 1.0, 100.0);
767 glMatrixMode(GL_MODELVIEW);
769 gluLookAt( 0.0, 0.0, 30.0,
773 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
775 int o = (int) current_device_rotation();
776 if (o != 0 && o != 180 && o != -180)
777 glScalef (1/h, 1/h, 1/h);
781 glClear(GL_COLOR_BUFFER_BIT);
786 hilbert_handle_event (ModeInfo *mi, XEvent *event)
788 hilbert_configuration *bp = &bps[MI_SCREEN(mi)];
790 if (gltrackball_event_handler (event, bp->trackball,
791 MI_WIDTH (mi), MI_HEIGHT (mi),
794 else if (event->xany.type == KeyPress)
798 XLookupString (&event->xkey, &c, 1, &keysym, 0);
799 if (c == '+' || c == '=' ||
800 keysym == XK_Up || keysym == XK_Right || keysym == XK_Next)
803 if (bp->depth > max_depth) bp->depth = max_depth;
806 else if (c == '-' || c == '_' ||
807 keysym == XK_Down || keysym == XK_Left || keysym == XK_Prior)
810 if (bp->depth < 1) bp->depth = 1;
813 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
815 bp->depth += bp->depth_tick;
816 if (bp->depth > max_depth-1)
818 bp->depth = max_depth;
821 else if (bp->depth <= 1)
835 init_hilbert (ModeInfo *mi)
837 hilbert_configuration *bp;
838 int wire = MI_IS_WIREFRAME(mi);
841 MI_INIT (mi, bps, NULL);
843 bp = &bps[MI_SCREEN(mi)];
847 bp->path_start = 0.0;
851 if (thickness < 0.01) thickness = 0.01;
852 if (thickness > 1.0) thickness = 1.0;
854 if (speed <= 0) speed = 0.0001;
855 if (max_depth < 2) max_depth = 2;
856 if (max_depth > 20) max_depth = 20; /* hard limit due to hilbert_cache */
858 if (bp->depth > max_depth-1) bp->depth = max_depth-1;
860 bp->glx_context = init_GL(mi);
862 reshape_hilbert (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
866 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
867 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
868 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
869 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
871 glEnable (GL_LIGHTING);
872 glEnable (GL_LIGHT0);
874 glLightfv(GL_LIGHT0, GL_POSITION, pos);
875 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
876 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
877 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
881 double spin_speed = 0.04;
882 double tilt_speed = spin_speed / 10;
883 double wander_speed = 0.008;
884 double spin_accel = 0.01;
886 bp->rot = make_rotator (do_spin ? spin_speed : 0,
887 do_spin ? spin_speed : 0,
888 do_spin ? spin_speed : 0,
890 do_wander ? wander_speed : 0,
892 bp->rot2 = make_rotator (0, 0, 0, 0, tilt_speed, True);
893 bp->trackball = gltrackball_init (True);
896 if (mode_str && !strcasecmp(mode_str, "2d"))
898 else if (mode_str && (!strcasecmp(mode_str, "3d")))
899 bp->twodee_p = False;
902 if (! (mode_str && !strcasecmp(mode_str, "random")))
903 fprintf (stderr, "%s: 'mode' must be '2d', '3d', or 'random'\n",
905 bp->twodee_p = ((random() % 3) == 0);
909 if (ends_str && (!strcasecmp(ends_str, "closed")))
911 else if (ends_str && (!strcasecmp(ends_str, "open")))
912 bp->closed_p = False;
915 if (! (ends_str && !strcasecmp(ends_str, "random")))
916 fprintf (stderr, "%s: 'ends' must be 'open', 'closed', or 'random'\n",
918 bp->closed_p = ((random() % 3) != 0);
922 /* More colors results in more polygons (more tube segments) so keeping
923 this small is worthwhile. */
924 bp->ncolors = (bp->twodee_p ? 1024 : 128);
928 /* Since the path is closed, colors must also be a closed loop */
929 bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
930 make_smooth_colormap (0, 0, 0,
931 bp->colors, &bp->ncolors,
936 /* Since the path is open, first and last colors should differ. */
938 bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
939 make_uniform_colormap (0, 0, 0,
940 bp->colors, &bp->ncolors,
945 /* Generate display lists for several different resolutions of
946 a unit tube and a unit sphere.
948 for (i = 0; i < countof(dlist_faces); i++)
950 int faces = dlist_faces[i];
951 bp->dlists[faces][0] = glGenLists (1);
953 glNewList (bp->dlists[faces][0], GL_COMPILE);
954 bp->dlist_polys[faces][0] = unit_sphere (faces, faces, wire);
957 bp->dlists[faces][1] = glGenLists (1);
959 glNewList (bp->dlists[faces][1], GL_COMPILE);
960 bp->dlist_polys[faces][1] =
961 tube (0, 0, 0, 1, 0, 0,
962 1, 0, faces, True, 0, wire);
969 draw_hilbert (ModeInfo *mi)
971 hilbert_configuration *bp = &bps[MI_SCREEN(mi)];
972 Display *dpy = MI_DISPLAY(mi);
973 Window window = MI_WINDOW(mi);
974 int wire = MI_IS_WIREFRAME(mi);
976 static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
977 static const GLfloat bshiny = 128.0;
978 GLfloat bcolor[4] = {1.0, 1.0, 1.0, 1.0};
980 if (!bp->glx_context)
983 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
985 glShadeModel(GL_SMOOTH);
989 glEnable (GL_NORMALIZE);
990 glEnable (GL_DEPTH_TEST);
991 glEnable (GL_CULL_FACE);
992 glEnable (GL_LIGHTING);
993 glEnable (GL_LIGHT0);
994 glEnable (GL_POLYGON_OFFSET_FILL);
997 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1001 glScalef(1.1, 1.1, 1.1);
1005 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
1006 glTranslatef((x - 0.5) * 8,
1010 gltrackball_rotate (bp->trackball);
1012 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
1014 if (bp->twodee_p && do_spin) /* face front */
1017 get_position (bp->rot2, &x, &y, &z2, !bp->button_down_p);
1018 glRotatef (max/2 - x*max, 1, 0, 0);
1019 glRotatef (max/2 - y*max, 0, 1, 0);
1020 glRotatef (z * 360, 0, 0, 1); /* but upside down is ok */
1024 glRotatef (x * 360, 1, 0, 0);
1025 glRotatef (y * 360, 0, 1, 0);
1026 glRotatef (z * 360, 0, 0, 1);
1030 mi->polygon_count = 0;
1032 glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
1033 glMateriali (GL_FRONT, GL_SHININESS, bshiny);
1034 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor);
1037 glTranslatef (0.1, 0.1, 0);
1039 if (!do_spin && !bp->twodee_p)
1041 /* tilt the cube a little, and make the start and end visible */
1042 glTranslatef (-0.1, 0, 0);
1043 glRotatef (140, 0, 1, 0);
1044 glRotatef (30, 1, 0, 0);
1048 glLineWidth (bp->depth > 4 ? 1.0 :
1049 bp->depth > 3 ? 2.0 :
1052 if (bp->path_tick > 0) /* advancing the end point, [0 - N) */
1053 { /* drawing 1 partial path, 1st time only. */
1055 if (!bp->button_down_p)
1056 bp->path_end += 0.01 * speed;
1058 if (bp->path_end >= 1.0)
1060 bp->path_start = 0.0;
1063 bp->pause = PAUSE_TICKS;
1066 bp->diam = thickness / pow (2, bp->depth);
1067 hilbert (mi, bp->depth, bp->path_start, bp->path_end);
1068 mi->recursion_depth = bp->depth + bp->path_start;
1071 else /* advancing the start point, (N - 1] */
1072 { /* drawing 2 paths at different rez. */
1079 if (!bp->button_down_p)
1080 bp->path_start += 0.01 * speed;
1082 if (bp->path_start > 1.0)
1084 bp->path_start = 0.0;
1085 bp->depth += bp->depth_tick;
1086 bp->pause = PAUSE_TICKS;
1087 if (bp->depth > max_depth-1)
1089 bp->depth = max_depth;
1090 bp->depth_tick = -1;
1092 else if (bp->depth <= 1)
1101 GLfloat d1 = thickness / pow (2, bp->depth);
1102 GLfloat d2 = thickness / pow (2, bp->depth + bp->depth_tick);
1103 bp->diam = (d1 * (1 - bp->path_start) +
1104 d2 * bp->path_start);
1107 /* First time around, start is 0, and end animates forward.
1108 Then, to display the next higher resolution, we render
1109 depth=N while increasing start and leaving end=1.0;
1110 and simultaneously animationg depth=N+1 with start=0 and
1111 end increasing by the same amount.
1113 The two paths aren't actually connected together at the
1114 resolution-change interface, and sometimes they overlap,
1115 but things go fast enough that it's hard to spot those
1116 glitches, so it looks ok.
1118 glPolygonOffset (0, 0);
1119 hilbert (mi, bp->depth, bp->path_start, bp->path_end);
1121 glPolygonOffset (1.0, 1.0);
1122 hilbert (mi, bp->depth + bp->depth_tick, 0.0, bp->path_start);
1124 mi->recursion_depth = bp->depth + (bp->depth_tick * bp->path_start);
1129 if (mi->fps_p) do_fps (mi);
1132 glXSwapBuffers(dpy, window);
1135 XSCREENSAVER_MODULE ("Hilbert", hilbert)