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)