1 /* glknots, Copyright (c) 2003, 2004 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 * Generates some 3D knots (closed loops).
12 * Inspired by Paul Bourke <pbourke@swin.edu.au> at
13 * http://astronomy.swin.edu.au/~pbourke/curves/knot/
16 #include <X11/Intrinsic.h>
18 extern XtAppContext app;
20 #define PROGCLASS "GLKnots"
21 #define HACK_INIT init_knot
22 #define HACK_DRAW draw_knot
23 #define HACK_RESHAPE reshape_knot
24 #define HACK_HANDLE_EVENT knot_handle_event
25 #define EVENT_MASK PointerMotionMask
26 #define sws_opts xlockmore_opts
28 #define DEF_SPIN "XYZ"
29 #define DEF_WANDER "True"
30 #define DEF_SPEED "1.0"
31 #define DEF_THICKNESS "0.3"
32 #define DEF_SEGMENTS "800"
33 #define DEF_DURATION "8"
35 #define DEFAULTS "*delay: 30000 \n" \
36 "*showFPS: False \n" \
37 "*wireframe: False \n" \
40 #define countof(x) (sizeof((x))/sizeof((*x)))
42 #include "xlockmore.h"
46 #include "gltrackball.h"
49 #ifdef USE_GL /* whole file */
54 GLXContext *glx_context;
56 trackball_state *trackball;
65 int mode; /* 0 = normal, 1 = out, 2 = in */
71 static knot_configuration *bps = NULL;
75 static Bool do_wander;
76 static GLfloat thickness;
77 static unsigned int segments;
80 static XrmOptionDescRec opts[] = {
81 { "-spin", ".spin", XrmoptionSepArg, 0 },
82 { "+spin", ".spin", XrmoptionNoArg, "" },
83 { "-wander", ".wander", XrmoptionNoArg, "True" },
84 { "+wander", ".wander", XrmoptionNoArg, "False" },
85 { "-speed", ".speed", XrmoptionSepArg, 0 },
86 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
87 { "-segments", ".segments", XrmoptionSepArg, 0 },
88 { "-duration", ".duration", XrmoptionSepArg, 0 },
91 static argtype vars[] = {
92 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
93 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
94 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
95 {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
96 {&segments, "segments", "Segments", DEF_SEGMENTS, t_Int},
97 {&duration, "duration", "Duration", DEF_DURATION, t_Int},
100 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
104 make_knot (ModeInfo *mi)
106 int wire = MI_IS_WIREFRAME(mi);
108 GLfloat diam = (4 * thickness);
109 int faces = (wire ? 3 : 6);
112 double x, y, z, ox=0, oy=0, oz=0;
117 Bool blobby_p = (0 == (random() % 5));
118 Bool type = (random() % 2);
120 for (i = 0; i < countof(p); i++)
122 p[i] = 1 + (random() % 4);
123 if (! (random() % 3))
124 p[i] += (random() % 5);
130 p[1] *= ((p[0] + p[0]) / 10);
134 mi->polygon_count = 0;
136 for (i = 0; i <= segments; i++)
140 mu = i * (M_PI * 2) / segments;
141 x = 10 * (cos(mu) + cos(p[0]*mu)) + cos(p[1]*mu) + cos(p[2]*mu);
142 y = 6 * sin(mu) + 10 * sin(p[3]*mu);
143 z = 16 * sin(p[4]*mu) * sin(p[5]*mu/2) + p[6]*sin(p[7]*mu) -
148 mu = i * (M_PI * 2) * p[0] / (double) segments;
149 x = 10 * cos(mu) * (1 + cos(p[1] * mu/ p[0]) / 2.0);
150 y = 25 * sin(p[1] * mu / p[0]) / 2.0;
151 z = 10 * sin(mu) * (1 + cos(p[1] * mu/ p[0]) / 2.0);
158 GLfloat dist = sqrt ((x-ox)*(x-ox) +
166 di = dist * (segments / 500.0);
173 faces, True, wire, wire);
175 mi->polygon_count += faces;
185 /* Window management, etc
188 reshape_knot (ModeInfo *mi, int width, int height)
190 GLfloat h = (GLfloat) height / (GLfloat) width;
192 glViewport (0, 0, (GLint) width, (GLint) height);
194 glMatrixMode(GL_PROJECTION);
196 gluPerspective (30.0, 1/h, 1.0, 100.0);
198 glMatrixMode(GL_MODELVIEW);
200 gluLookAt( 0.0, 0.0, 30.0,
204 glClear(GL_COLOR_BUFFER_BIT);
209 new_knot (ModeInfo *mi)
211 knot_configuration *bp = &bps[MI_SCREEN(mi)];
214 bp->clear_p = !!(random() % 15);
217 bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
218 make_smooth_colormap (0, 0, 0,
219 bp->colors, &bp->ncolors,
222 for (i = 0; i < bp->ncolors; i++)
224 /* make colors twice as bright */
225 bp->colors[i].red = (bp->colors[i].red >> 2) + 0x7FFF;
226 bp->colors[i].green = (bp->colors[i].green >> 2) + 0x7FFF;
227 bp->colors[i].blue = (bp->colors[i].blue >> 2) + 0x7FFF;
230 glNewList (bp->knot_list, GL_COMPILE);
237 knot_handle_event (ModeInfo *mi, XEvent *event)
239 knot_configuration *bp = &bps[MI_SCREEN(mi)];
241 if (event->xany.type == ButtonPress &&
242 event->xbutton.button == Button1)
244 bp->button_down_p = True;
245 gltrackball_start (bp->trackball,
246 event->xbutton.x, event->xbutton.y,
247 MI_WIDTH (mi), MI_HEIGHT (mi));
250 else if (event->xany.type == ButtonRelease &&
251 event->xbutton.button == Button1)
253 bp->button_down_p = False;
256 else if (event->xany.type == ButtonPress &&
257 (event->xbutton.button == Button4 ||
258 event->xbutton.button == Button5))
260 gltrackball_mousewheel (bp->trackball, event->xbutton.button, 5,
261 !!event->xbutton.state);
264 else if (event->xany.type == MotionNotify &&
267 gltrackball_track (bp->trackball,
268 event->xmotion.x, event->xmotion.y,
269 MI_WIDTH (mi), MI_HEIGHT (mi));
279 init_knot (ModeInfo *mi)
281 knot_configuration *bp;
282 int wire = MI_IS_WIREFRAME(mi);
285 bps = (knot_configuration *)
286 calloc (MI_NUM_SCREENS(mi), sizeof (knot_configuration));
288 fprintf(stderr, "%s: out of memory\n", progname);
292 bp = &bps[MI_SCREEN(mi)];
295 bp = &bps[MI_SCREEN(mi)];
297 bp->glx_context = init_GL(mi);
299 if (thickness <= 0) thickness = 0.001;
300 else if (thickness > 1) thickness = 1;
302 if (segments < 10) segments = 10;
304 reshape_knot (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
308 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
309 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
310 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
311 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
313 glEnable(GL_LIGHTING);
315 glEnable(GL_DEPTH_TEST);
316 glEnable(GL_CULL_FACE);
318 glLightfv(GL_LIGHT0, GL_POSITION, pos);
319 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
320 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
321 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
325 Bool spinx=False, spiny=False, spinz=False;
326 double spin_speed = 2.0;
327 double wander_speed = 0.05;
328 double spin_accel = 0.2;
333 if (*s == 'x' || *s == 'X') spinx = True;
334 else if (*s == 'y' || *s == 'Y') spiny = True;
335 else if (*s == 'z' || *s == 'Z') spinz = True;
339 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
346 bp->rot = make_rotator (spinx ? spin_speed : 0,
347 spiny ? spin_speed : 0,
348 spinz ? spin_speed : 0,
350 do_wander ? wander_speed : 0,
351 (spinx && spiny && spinz));
352 bp->trackball = gltrackball_init ();
355 bp->knot_list = glGenLists (1);
358 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
363 draw_knot (ModeInfo *mi)
365 knot_configuration *bp = &bps[MI_SCREEN(mi)];
366 Display *dpy = MI_DISPLAY(mi);
367 Window window = MI_WINDOW(mi);
369 static GLfloat bcolor[4] = {0.0, 0.0, 0.0, 1.0};
370 static GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
371 static GLfloat bshiny = 128.0;
373 static time_t last_time = 0;
375 if (!bp->glx_context)
383 time_t now = time((time_t *) 0);
384 if (last_time == 0) last_time = now;
386 if (!bp->button_down_p &&
387 now - last_time >= duration)
389 bp->mode = 1; /* go out */
390 bp->mode_tick = 10 * speed;
395 else if (bp->mode == 1) /* out */
397 if (--bp->mode_tick <= 0)
400 bp->mode_tick = 10 * speed;
401 bp->mode = 2; /* go in */
404 else if (bp->mode == 2) /* in */
406 if (--bp->mode_tick <= 0)
407 bp->mode = 0; /* normal */
412 glShadeModel(GL_SMOOTH);
414 glEnable(GL_DEPTH_TEST);
415 glEnable(GL_NORMALIZE);
416 glEnable(GL_CULL_FACE);
419 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
425 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
426 glTranslatef((x - 0.5) * 8,
430 gltrackball_rotate (bp->trackball);
432 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
434 glRotatef (x * 360, 1.0, 0.0, 0.0);
435 glRotatef (y * 360, 0.0, 1.0, 0.0);
436 glRotatef (z * 360, 0.0, 0.0, 1.0);
439 bcolor[0] = bp->colors[bp->ccolor].red / 65536.0;
440 bcolor[1] = bp->colors[bp->ccolor].green / 65536.0;
441 bcolor[2] = bp->colors[bp->ccolor].blue / 65536.0;
443 if (bp->ccolor >= bp->ncolors) bp->ccolor = 0;
445 glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
446 glMateriali (GL_FRONT, GL_SHININESS, bshiny);
447 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor);
449 glScalef(0.25, 0.25, 0.25);
453 GLfloat s = (bp->mode == 1
454 ? bp->mode_tick / (10 * speed)
455 : ((10 * speed) - bp->mode_tick + 1) / (10 * speed));
459 glCallList (bp->knot_list);
463 if (mi->fps_p) do_fps (mi);
466 glXSwapBuffers(dpy, window);