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"
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 (gltrackball_event_handler (event, bp->trackball,
782 MI_WIDTH (mi), MI_HEIGHT (mi),
785 else if (event->xany.type == KeyPress)
789 XLookupString (&event->xkey, &c, 1, &keysym, 0);
790 if (c == '+' || c == '=' ||
791 keysym == XK_Up || keysym == XK_Right || keysym == XK_Next)
794 if (bp->depth > max_depth) bp->depth = max_depth;
797 else if (c == '-' || c == '_' ||
798 keysym == XK_Down || keysym == XK_Left || keysym == XK_Prior)
801 if (bp->depth < 1) bp->depth = 1;
804 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
806 bp->depth += bp->depth_tick;
807 if (bp->depth > max_depth-1)
809 bp->depth = max_depth;
812 else if (bp->depth <= 1)
826 init_hilbert (ModeInfo *mi)
828 hilbert_configuration *bp;
829 int wire = MI_IS_WIREFRAME(mi);
833 bps = (hilbert_configuration *)
834 calloc (MI_NUM_SCREENS(mi), sizeof (hilbert_configuration));
836 fprintf(stderr, "%s: out of memory\n", progname);
841 bp = &bps[MI_SCREEN(mi)];
845 bp->path_start = 0.0;
849 if (thickness < 0.01) thickness = 0.01;
850 if (thickness > 1.0) thickness = 1.0;
852 if (speed <= 0) speed = 0.0001;
853 if (max_depth < 2) max_depth = 2;
854 if (max_depth > 20) max_depth = 20; /* hard limit due to hilbert_cache */
856 if (bp->depth > max_depth-1) bp->depth = max_depth-1;
858 bp->glx_context = init_GL(mi);
860 reshape_hilbert (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
864 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
865 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
866 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
867 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
869 glEnable (GL_LIGHTING);
870 glEnable (GL_LIGHT0);
872 glLightfv(GL_LIGHT0, GL_POSITION, pos);
873 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
874 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
875 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
879 double spin_speed = 0.04;
880 double tilt_speed = spin_speed / 10;
881 double wander_speed = 0.008;
882 double spin_accel = 0.01;
884 bp->rot = make_rotator (do_spin ? spin_speed : 0,
885 do_spin ? spin_speed : 0,
886 do_spin ? spin_speed : 0,
888 do_wander ? wander_speed : 0,
890 bp->rot2 = make_rotator (0, 0, 0, 0, tilt_speed, True);
891 bp->trackball = gltrackball_init (True);
894 if (mode_str && !strcasecmp(mode_str, "2d"))
896 else if (mode_str && (!strcasecmp(mode_str, "3d")))
897 bp->twodee_p = False;
900 if (! (mode_str && !strcasecmp(mode_str, "random")))
901 fprintf (stderr, "%s: 'mode' must be '2d', '3d', or 'random'\n",
903 bp->twodee_p = ((random() % 3) == 0);
907 if (ends_str && (!strcasecmp(ends_str, "closed")))
909 else if (ends_str && (!strcasecmp(ends_str, "open")))
910 bp->closed_p = False;
913 if (! (ends_str && !strcasecmp(ends_str, "random")))
914 fprintf (stderr, "%s: 'ends' must be 'open', 'closed', or 'random'\n",
916 bp->closed_p = ((random() % 3) != 0);
920 /* More colors results in more polygons (more tube segments) so keeping
921 this small is worthwhile. */
922 bp->ncolors = (bp->twodee_p ? 1024 : 128);
926 /* Since the path is closed, colors must also be a closed loop */
927 bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
928 make_smooth_colormap (0, 0, 0,
929 bp->colors, &bp->ncolors,
934 /* Since the path is open, first and last colors should differ. */
936 bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
937 make_uniform_colormap (0, 0, 0,
938 bp->colors, &bp->ncolors,
943 /* Generate display lists for several different resolutions of
944 a unit tube and a unit sphere.
946 for (i = 0; i < countof(dlist_faces); i++)
948 int faces = dlist_faces[i];
949 bp->dlists[faces][0] = glGenLists (1);
951 glNewList (bp->dlists[faces][0], GL_COMPILE);
952 bp->dlist_polys[faces][0] = unit_sphere (faces, faces, wire);
955 bp->dlists[faces][1] = glGenLists (1);
957 glNewList (bp->dlists[faces][1], GL_COMPILE);
958 bp->dlist_polys[faces][1] =
959 tube (0, 0, 0, 1, 0, 0,
960 1, 0, faces, True, 0, wire);
967 draw_hilbert (ModeInfo *mi)
969 hilbert_configuration *bp = &bps[MI_SCREEN(mi)];
970 Display *dpy = MI_DISPLAY(mi);
971 Window window = MI_WINDOW(mi);
972 int wire = MI_IS_WIREFRAME(mi);
974 static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
975 static const GLfloat bshiny = 128.0;
976 GLfloat bcolor[4] = {1.0, 1.0, 1.0, 1.0};
978 if (!bp->glx_context)
981 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
983 glShadeModel(GL_SMOOTH);
987 glEnable (GL_NORMALIZE);
988 glEnable (GL_DEPTH_TEST);
989 glEnable (GL_CULL_FACE);
990 glEnable (GL_LIGHTING);
991 glEnable (GL_LIGHT0);
992 glEnable (GL_POLYGON_OFFSET_FILL);
995 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
999 glScalef(1.1, 1.1, 1.1);
1003 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
1004 glTranslatef((x - 0.5) * 8,
1008 gltrackball_rotate (bp->trackball);
1010 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
1012 if (bp->twodee_p && do_spin) /* face front */
1015 get_position (bp->rot2, &x, &y, &z2, !bp->button_down_p);
1016 glRotatef (max/2 - x*max, 1, 0, 0);
1017 glRotatef (max/2 - y*max, 0, 1, 0);
1018 glRotatef (z * 360, 0, 0, 1); /* but upside down is ok */
1022 glRotatef (x * 360, 1, 0, 0);
1023 glRotatef (y * 360, 0, 1, 0);
1024 glRotatef (z * 360, 0, 0, 1);
1028 mi->polygon_count = 0;
1030 glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
1031 glMateriali (GL_FRONT, GL_SHININESS, bshiny);
1032 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor);
1035 glTranslatef (0.1, 0.1, 0);
1037 if (!do_spin && !bp->twodee_p)
1039 /* tilt the cube a little, and make the start and end visible */
1040 glTranslatef (-0.1, 0, 0);
1041 glRotatef (140, 0, 1, 0);
1042 glRotatef (30, 1, 0, 0);
1046 glLineWidth (bp->depth > 4 ? 1.0 :
1047 bp->depth > 3 ? 2.0 :
1050 if (bp->path_tick > 0) /* advancing the end point, [0 - N) */
1051 { /* drawing 1 partial path, 1st time only. */
1053 if (!bp->button_down_p)
1054 bp->path_end += 0.01 * speed;
1056 if (bp->path_end >= 1.0)
1058 bp->path_start = 0.0;
1061 bp->pause = PAUSE_TICKS;
1064 bp->diam = thickness / pow (2, bp->depth);
1065 hilbert (mi, bp->depth, bp->path_start, bp->path_end);
1066 mi->recursion_depth = bp->depth + bp->path_start;
1069 else /* advancing the start point, (N - 1] */
1070 { /* drawing 2 paths at different rez. */
1077 if (!bp->button_down_p)
1078 bp->path_start += 0.01 * speed;
1080 if (bp->path_start > 1.0)
1082 bp->path_start = 0.0;
1083 bp->depth += bp->depth_tick;
1084 bp->pause = PAUSE_TICKS;
1085 if (bp->depth > max_depth-1)
1087 bp->depth = max_depth;
1088 bp->depth_tick = -1;
1090 else if (bp->depth <= 1)
1099 GLfloat d1 = thickness / pow (2, bp->depth);
1100 GLfloat d2 = thickness / pow (2, bp->depth + bp->depth_tick);
1101 bp->diam = (d1 * (1 - bp->path_start) +
1102 d2 * bp->path_start);
1105 /* First time around, start is 0, and end animates forward.
1106 Then, to display the next higher resolution, we render
1107 depth=N while increasing start and leaving end=1.0;
1108 and simultaneously animationg depth=N+1 with start=0 and
1109 end increasing by the same amount.
1111 The two paths aren't actually connected together at the
1112 resolution-change interface, and sometimes they overlap,
1113 but things go fast enough that it's hard to spot those
1114 glitches, so it looks ok.
1116 glPolygonOffset (0, 0);
1117 hilbert (mi, bp->depth, bp->path_start, bp->path_end);
1119 glPolygonOffset (1.0, 1.0);
1120 hilbert (mi, bp->depth + bp->depth_tick, 0.0, bp->path_start);
1122 mi->recursion_depth = bp->depth + (bp->depth_tick * bp->path_start);
1127 if (mi->fps_p) do_fps (mi);
1130 glXSwapBuffers(dpy, window);
1133 XSCREENSAVER_MODULE ("Hilbert", hilbert)