1 /* hexstrut, Copyright (c) 2016-2017 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
19 # define release_hexstrut 0
21 #define countof(x) (sizeof((x))/sizeof((*x)))
23 #include "xlockmore.h"
27 #include "gltrackball.h"
30 #ifdef USE_GL /* whole file */
33 #define DEF_SPIN "True"
34 #define DEF_WANDER "True"
35 #define DEF_SPEED "1.0"
36 #define DEF_THICKNESS "0.2"
38 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
40 typedef struct { double a, o; } LL; /* latitude + longitude */
42 typedef struct triangle triangle;
46 triangle *neighbors[6];
53 GLXContext *glx_context;
55 trackball_state *trackball;
64 } hexstrut_configuration;
66 static hexstrut_configuration *bps = NULL;
70 static Bool do_wander;
71 static GLfloat thickness;
73 static XrmOptionDescRec opts[] = {
74 { "-spin", ".spin", XrmoptionNoArg, "True" },
75 { "+spin", ".spin", XrmoptionNoArg, "False" },
76 { "-speed", ".speed", XrmoptionSepArg, 0 },
77 { "-wander", ".wander", XrmoptionNoArg, "True" },
78 { "+wander", ".wander", XrmoptionNoArg, "False" },
79 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
82 static argtype vars[] = {
83 {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
84 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
85 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
86 {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
89 ENTRYPOINT ModeSpecOpt hexstrut_opts = {countof(opts), opts, countof(vars), vars, NULL};
93 /* Add t1 to the neighbor list of t0. */
95 link_neighbor (triangle *t0, triangle *t1)
100 for (k = 0; k < countof(t0->neighbors); k++)
102 if (t0->neighbors[k] == t1 ||
103 t0->neighbors[k] == 0)
105 t0->neighbors[k] = t1;
109 fprintf (stderr, "too many neighbors\n");
115 make_plane (ModeInfo *mi)
117 hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
118 int n = MI_COUNT(mi) * 2;
119 GLfloat size = 2.0 / n;
122 GLfloat h = size * sqrt(3) / 2;
123 triangle **grid = (triangle **) calloc (n * n, sizeof(*grid));
125 for (y = 0; y < n; y++)
126 for (x = 0; x < n; x++)
130 t = (triangle *) calloc (1, sizeof(*t));
131 t->p[0].x = (x - n/2) * w;
132 t->p[0].y = (y - n/2) * h;
137 t->p[1].x = t->p[0].x - w/2;
138 t->p[2].x = t->p[0].x + w/2;
139 t->p[1].y = t->p[0].y + h;
140 t->p[2].y = t->p[0].y + h;
144 triangle *t2 = grid[y * n + (x-1)];
145 link_neighbor (t, t2);
146 link_neighbor (t2, t);
150 triangle *t2 = grid[(y-1) * n + x];
151 link_neighbor (t, t2);
152 link_neighbor (t2, t);
156 t2 = grid[(y-1) * n + (x+1)];
157 link_neighbor (t, t2);
158 link_neighbor (t2, t);
162 t->next = bp->triangles;
173 tick_triangles (ModeInfo *mi)
175 hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
177 GLfloat step = 0.01 + (0.04 * speed);
179 if (! (random() % 80))
181 int n = random() % bp->count;
183 for (i = 0, t = bp->triangles; t && i < n; t = t->next, i++)
187 t->rot += step * ((random() & 1) ? 1 : -1);
188 t->odelay = t->delay = 4;
192 for (t = bp->triangles; t; t = t->next)
194 /* If this triangle is rotating, continue until done. */
197 t->rot += step * (t->rot > 0 ? 1 : -1);
200 if (t->ccolor >= bp->ncolors)
203 if (t->rot > 1 || t->rot < -1)
205 t->orot += (t->rot > 1 ? 1 : -1);
210 /* If this triangle's propagation delay hasn't hit zero, decrement it.
211 When it does, start its neighbors rotating.
218 for (i = 0; i < countof(t->neighbors); i++)
220 if (t->neighbors[i] &&
221 t->neighbors[i]->rot == 0)
223 t->neighbors[i]->rot += step * (t->rot > 0 ? 1 : -1);
224 t->neighbors[i]->delay =
225 t->neighbors[i]->odelay = t->odelay;
234 draw_triangles (ModeInfo *mi)
236 hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
237 int wire = MI_IS_WIREFRAME(mi);
239 GLfloat length = sqrt(3) / 3;
240 GLfloat t2 = length * thickness / 2;
244 triangle *t = bp->triangles;
245 GLfloat X = t->p[0].x - t->p[1].x;
246 GLfloat Y = t->p[0].y - t->p[1].y;
247 GLfloat Z = t->p[0].z - t->p[1].z;
248 scale = sqrt(X*X + Y*Y + Z*Z);
251 glFrontFace (GL_CCW);
253 glBegin (wire ? GL_LINES : GL_QUADS);
255 glNormal3f (0, 0, 1);
256 for (t = bp->triangles; t; t = t->next)
262 GLfloat angle = (M_PI * 2 / 3) * t->rot;
263 GLfloat cr = cos(angle), sr = sin(angle);
265 c.x = (t->p[0].x + t->p[1].x + t->p[2].x) / 3;
266 c.y = (t->p[0].y + t->p[1].y + t->p[2].y) / 3;
267 c.z = (t->p[0].z + t->p[1].z + t->p[2].z) / 3;
269 /* Actually we don't need normals at all, since no lighting.
270 do_normal (t->p[0].x, t->p[0].y, t->p[0].z,
271 t->p[1].x, t->p[1].y, t->p[1].z,
272 t->p[2].x, t->p[2].y, t->p[2].z);
275 color[0] = bp->colors[t->ccolor].red / 65535.0;
276 color[1] = bp->colors[t->ccolor].green / 65535.0;
277 color[2] = bp->colors[t->ccolor].blue / 65535.0;
281 color[0] = color[0] * 0.75 + 0.25;
282 color[1] = color[1] * 0.75 + 0.25;
283 color[2] = color[2] * 0.75 + 0.25;
286 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
288 for (i = 0; i < 3; i++)
290 /* Orient to direction of corner. */
291 GLfloat x = t->p[i].x - c.x;
292 GLfloat y = t->p[i].y - c.y;
293 GLfloat z = t->p[i].z - c.z;
295 GLfloat smc = sr * y - cr * x;
296 GLfloat spc = cr * y + sr * x;
298 GLfloat st2 = t2 * scale / sqrt(x*x + y*y);
299 GLfloat slength = length * scale / sqrt(x*x + y*y + z*z);
301 GLfloat xt2 = spc * st2;
302 GLfloat yt2 = smc * st2;
303 GLfloat xlength = c.x - slength * smc;
304 GLfloat ylength = c.y + slength * spc;
305 GLfloat zlength = c.z + slength * z;
308 glVertex3f (c.x - xt2, c.y - yt2, c.z);
310 glVertex3f (c.x + xt2, c.y + yt2, c.z);
312 glVertex3f (xlength + xt2, ylength + yt2, zlength);
315 glVertex3f (xlength + xt2, ylength + yt2, zlength);
317 glVertex3f (xlength - xt2, ylength - yt2, zlength);
320 glVertex3f (c.x - xt2, c.y - yt2, c.z);
330 /* Window management, etc
333 reshape_hexstrut (ModeInfo *mi, int width, int height)
335 GLfloat h = (GLfloat) height / (GLfloat) width;
337 glViewport (0, 0, (GLint) width, (GLint) height);
339 glMatrixMode(GL_PROJECTION);
341 gluPerspective (30.0, 1/h, 1.0, 100.0);
343 glMatrixMode(GL_MODELVIEW);
345 gluLookAt( 0.0, 0.0, 30.0,
349 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
351 int o = (int) current_device_rotation();
352 if (o != 0 && o != 180 && o != -180)
353 glScalef (1/h, 1/h, 1/h);
357 glClear(GL_COLOR_BUFFER_BIT);
362 hexstrut_handle_event (ModeInfo *mi, XEvent *event)
364 hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
366 if (gltrackball_event_handler (event, bp->trackball,
367 MI_WIDTH (mi), MI_HEIGHT (mi),
370 else if (event->xany.type == KeyPress)
374 XLookupString (&event->xkey, &c, 1, &keysym, 0);
375 if (c == ' ' || c == '\t')
378 make_smooth_colormap (0, 0, 0,
379 bp->colors, &bp->ncolors,
389 static void free_hexstrut (ModeInfo *mi);
392 init_hexstrut (ModeInfo *mi)
394 hexstrut_configuration *bp;
396 MI_INIT (mi, bps, free_hexstrut);
398 bp = &bps[MI_SCREEN(mi)];
400 bp->glx_context = init_GL(mi);
402 reshape_hexstrut (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
405 double spin_speed = 0.002;
406 double wander_speed = 0.003;
407 double spin_accel = 1.0;
409 bp->rot = make_rotator (do_spin ? spin_speed : 0,
410 do_spin ? spin_speed : 0,
411 do_spin ? spin_speed : 0,
413 do_wander ? wander_speed : 0,
415 bp->trackball = gltrackball_init (True);
419 /* Let's tilt the scene a little. */
420 gltrackball_reset (bp->trackball,
424 if (thickness < 0.05) thickness = 0.05;
425 if (thickness < 0.05) MI_IS_WIREFRAME(mi) = True;
426 if (thickness > 1.7) thickness = 1.7;
427 if (speed > 2) speed = 2;
430 bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
431 make_smooth_colormap (0, 0, 0,
432 bp->colors, &bp->ncolors,
440 draw_hexstrut (ModeInfo *mi)
442 hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
443 Display *dpy = MI_DISPLAY(mi);
444 Window window = MI_WINDOW(mi);
446 if (!bp->glx_context)
449 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
451 glShadeModel(GL_SMOOTH);
453 glDisable(GL_DEPTH_TEST);
454 glEnable(GL_NORMALIZE);
455 glDisable(GL_CULL_FACE);
457 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
463 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
464 glTranslatef((x - 0.5) * 6,
468 gltrackball_rotate (bp->trackball);
470 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
471 glRotatef (z * 360, 0.0, 0.0, 1.0);
474 mi->polygon_count = 0;
476 glScalef (30, 30, 30);
478 if (! bp->button_down_p)
484 if (mi->fps_p) do_fps (mi);
487 glXSwapBuffers(dpy, window);
492 free_hexstrut (ModeInfo *mi)
494 hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
495 while (bp->triangles)
497 triangle *t = bp->triangles->next;
498 free (bp->triangles);
503 XSCREENSAVER_MODULE ("Hexstrut", hexstrut)