1 /* hexstrut, Copyright (c) 2016 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
12 #define DEFAULTS "*delay: 30000 \n" \
13 "*showFPS: False \n" \
14 "*wireframe: False \n" \
16 "*suppressRotationAnimation: True\n" \
18 # define refresh_hexstrut 0
20 #define countof(x) (sizeof((x))/sizeof((*x)))
22 #include "xlockmore.h"
26 #include "gltrackball.h"
29 #ifdef USE_GL /* whole file */
32 #define DEF_SPIN "True"
33 #define DEF_WANDER "True"
34 #define DEF_SPEED "1.0"
35 #define DEF_THICKNESS "0.2"
37 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
39 typedef struct { double a, o; } LL; /* latitude + longitude */
41 typedef struct triangle triangle;
45 triangle *neighbors[6];
52 GLXContext *glx_context;
54 trackball_state *trackball;
63 } hexstrut_configuration;
65 static hexstrut_configuration *bps = NULL;
69 static Bool do_wander;
70 static GLfloat thickness;
72 static XrmOptionDescRec opts[] = {
73 { "-spin", ".spin", XrmoptionNoArg, "True" },
74 { "+spin", ".spin", XrmoptionNoArg, "False" },
75 { "-speed", ".speed", XrmoptionSepArg, 0 },
76 { "-wander", ".wander", XrmoptionNoArg, "True" },
77 { "+wander", ".wander", XrmoptionNoArg, "False" },
78 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
81 static argtype vars[] = {
82 {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
83 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
84 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
85 {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
88 ENTRYPOINT ModeSpecOpt hexstrut_opts = {countof(opts), opts, countof(vars), vars, NULL};
92 /* Add t1 to the neighbor list of t0. */
94 link_neighbor (triangle *t0, triangle *t1)
99 for (k = 0; k < countof(t0->neighbors); k++)
101 if (t0->neighbors[k] == t1 ||
102 t0->neighbors[k] == 0)
104 t0->neighbors[k] = t1;
108 fprintf (stderr, "too many neighbors\n");
114 make_plane (ModeInfo *mi)
116 hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
117 int n = MI_COUNT(mi) * 2;
118 GLfloat size = 2.0 / n;
121 GLfloat h = size * sqrt(3) / 2;
122 triangle **grid = (triangle **) calloc (n * n, sizeof(*grid));
124 for (y = 0; y < n; y++)
125 for (x = 0; x < n; x++)
129 t = (triangle *) calloc (1, sizeof(*t));
130 t->p[0].x = (x - n/2) * w;
131 t->p[0].y = (y - n/2) * h;
136 t->p[1].x = t->p[0].x - w/2;
137 t->p[2].x = t->p[0].x + w/2;
138 t->p[1].y = t->p[0].y + h;
139 t->p[2].y = t->p[0].y + h;
143 triangle *t2 = grid[y * n + (x-1)];
144 link_neighbor (t, t2);
145 link_neighbor (t2, t);
149 triangle *t2 = grid[(y-1) * n + x];
150 link_neighbor (t, t2);
151 link_neighbor (t2, t);
155 t2 = grid[(y-1) * n + (x+1)];
156 link_neighbor (t, t2);
157 link_neighbor (t2, t);
161 t->next = bp->triangles;
172 tick_triangles (ModeInfo *mi)
174 hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
176 GLfloat step = 0.01 + (0.04 * speed);
178 if (! (random() % 80))
180 int n = random() % bp->count;
182 for (i = 0, t = bp->triangles; t && i < n; t = t->next, i++)
186 t->rot += step * ((random() & 1) ? 1 : -1);
187 t->odelay = t->delay = 4;
191 for (t = bp->triangles; t; t = t->next)
193 /* If this triangle is rotating, continue until done. */
196 t->rot += step * (t->rot > 0 ? 1 : -1);
199 if (t->ccolor > bp->ncolors)
202 if (t->rot > 1 || t->rot < -1)
204 t->orot += (t->rot > 1 ? 1 : -1);
209 /* If this triangle's propagation delay hasn't hit zero, decrement it.
210 When it does, start its neighbors rotating.
217 for (i = 0; i < countof(t->neighbors); i++)
219 if (t->neighbors[i] &&
220 t->neighbors[i]->rot == 0)
222 t->neighbors[i]->rot += step * (t->rot > 0 ? 1 : -1);
223 t->neighbors[i]->delay =
224 t->neighbors[i]->odelay = t->odelay;
233 draw_triangles (ModeInfo *mi)
235 hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
236 int wire = MI_IS_WIREFRAME(mi);
238 GLfloat length = sqrt(3) / 3;
239 GLfloat t2 = length * thickness / 2;
243 triangle *t = bp->triangles;
244 GLfloat X = t->p[0].x - t->p[1].x;
245 GLfloat Y = t->p[0].y - t->p[1].y;
246 GLfloat Z = t->p[0].z - t->p[1].z;
247 scale = sqrt(X*X + Y*Y + Z*Z);
250 glFrontFace (GL_CCW);
252 glBegin (wire ? GL_LINES : GL_QUADS);
254 glNormal3f (0, 0, 1);
255 for (t = bp->triangles; t; t = t->next)
261 GLfloat angle = (M_PI * 2 / 3) * t->rot;
262 GLfloat cr = cos(angle), sr = sin(angle);
264 c.x = (t->p[0].x + t->p[1].x + t->p[2].x) / 3;
265 c.y = (t->p[0].y + t->p[1].y + t->p[2].y) / 3;
266 c.z = (t->p[0].z + t->p[1].z + t->p[2].z) / 3;
268 /* Actually we don't need normals at all, since no lighting.
269 do_normal (t->p[0].x, t->p[0].y, t->p[0].z,
270 t->p[1].x, t->p[1].y, t->p[1].z,
271 t->p[2].x, t->p[2].y, t->p[2].z);
274 color[0] = bp->colors[t->ccolor].red / 65535.0;
275 color[1] = bp->colors[t->ccolor].green / 65535.0;
276 color[2] = bp->colors[t->ccolor].blue / 65535.0;
280 color[0] = color[0] * 0.75 + 0.25;
281 color[1] = color[1] * 0.75 + 0.25;
282 color[2] = color[2] * 0.75 + 0.25;
285 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
287 for (i = 0; i < 3; i++)
289 /* Orient to direction of corner. */
290 GLfloat x = t->p[i].x - c.x;
291 GLfloat y = t->p[i].y - c.y;
292 GLfloat z = t->p[i].z - c.z;
294 GLfloat smc = sr * y - cr * x;
295 GLfloat spc = cr * y + sr * x;
297 GLfloat st2 = t2 * scale / sqrt(x*x + y*y);
298 GLfloat slength = length * scale / sqrt(x*x + y*y + z*z);
300 GLfloat xt2 = spc * st2;
301 GLfloat yt2 = smc * st2;
302 GLfloat xlength = c.x - slength * smc;
303 GLfloat ylength = c.y + slength * spc;
304 GLfloat zlength = c.z + slength * z;
307 glVertex3f (c.x - xt2, c.y - yt2, c.z);
309 glVertex3f (c.x + xt2, c.y + yt2, c.z);
311 glVertex3f (xlength + xt2, ylength + yt2, zlength);
314 glVertex3f (xlength + xt2, ylength + yt2, zlength);
316 glVertex3f (xlength - xt2, ylength - yt2, zlength);
319 glVertex3f (c.x - xt2, c.y - yt2, c.z);
329 /* Window management, etc
332 reshape_hexstrut (ModeInfo *mi, int width, int height)
334 GLfloat h = (GLfloat) height / (GLfloat) width;
336 glViewport (0, 0, (GLint) width, (GLint) height);
338 glMatrixMode(GL_PROJECTION);
340 gluPerspective (30.0, 1/h, 1.0, 100.0);
342 glMatrixMode(GL_MODELVIEW);
344 gluLookAt( 0.0, 0.0, 30.0,
348 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
350 int o = (int) current_device_rotation();
351 if (o != 0 && o != 180 && o != -180)
352 glScalef (1/h, 1/h, 1/h);
356 glClear(GL_COLOR_BUFFER_BIT);
361 hexstrut_handle_event (ModeInfo *mi, XEvent *event)
363 hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
365 if (gltrackball_event_handler (event, bp->trackball,
366 MI_WIDTH (mi), MI_HEIGHT (mi),
369 else if (event->xany.type == KeyPress)
373 XLookupString (&event->xkey, &c, 1, &keysym, 0);
374 if (c == ' ' || c == '\t')
377 make_smooth_colormap (0, 0, 0,
378 bp->colors, &bp->ncolors,
389 init_hexstrut (ModeInfo *mi)
391 hexstrut_configuration *bp;
394 bps = (hexstrut_configuration *)
395 calloc (MI_NUM_SCREENS(mi), sizeof (hexstrut_configuration));
397 fprintf(stderr, "%s: out of memory\n", progname);
402 bp = &bps[MI_SCREEN(mi)];
404 bp->glx_context = init_GL(mi);
406 reshape_hexstrut (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
409 double spin_speed = 0.002;
410 double wander_speed = 0.003;
411 double spin_accel = 1.0;
413 bp->rot = make_rotator (do_spin ? spin_speed : 0,
414 do_spin ? spin_speed : 0,
415 do_spin ? spin_speed : 0,
417 do_wander ? wander_speed : 0,
419 bp->trackball = gltrackball_init (True);
423 /* Let's tilt the scene a little. */
424 gltrackball_start (bp->trackball, 500, 500, 1000, 1000);
425 gltrackball_track (bp->trackball,
426 350 + (random() % 300),
427 350 + (random() % 300),
431 if (thickness < 0.05) thickness = 0.05;
432 if (thickness < 0.05) MI_IS_WIREFRAME(mi) = True;
433 if (thickness > 1.7) thickness = 1.7;
434 if (speed > 2) speed = 2;
437 bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
438 make_smooth_colormap (0, 0, 0,
439 bp->colors, &bp->ncolors,
447 draw_hexstrut (ModeInfo *mi)
449 hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
450 Display *dpy = MI_DISPLAY(mi);
451 Window window = MI_WINDOW(mi);
453 if (!bp->glx_context)
456 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
458 glShadeModel(GL_SMOOTH);
460 glDisable(GL_DEPTH_TEST);
461 glEnable(GL_NORMALIZE);
462 glDisable(GL_CULL_FACE);
464 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
470 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
471 glTranslatef((x - 0.5) * 6,
475 gltrackball_rotate (bp->trackball);
477 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
478 glRotatef (z * 360, 0.0, 0.0, 1.0);
481 mi->polygon_count = 0;
483 glScalef (30, 30, 30);
485 if (! bp->button_down_p)
491 if (mi->fps_p) do_fps (mi);
494 glXSwapBuffers(dpy, window);
499 release_hexstrut (ModeInfo *mi)
501 hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
502 while (bp->triangles)
504 triangle *t = bp->triangles->next;
505 free (bp->triangles);
510 XSCREENSAVER_MODULE ("Hexstrut", hexstrut)