1 /* voronoi, Copyright (c) 2007-2018 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: 20000 \n" \
13 "*showFPS: False \n" \
14 "*suppressRotationAnimation: True\n" \
16 # define free_voronoi 0
17 # define release_voronoi 0
19 #define countof(x) (sizeof((x))/sizeof((*x)))
21 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
24 #include "xlockmore.h"
27 #ifdef USE_GL /* whole file */
29 #define DEF_POINTS "25"
30 #define DEF_POINT_SIZE "9"
31 #define DEF_POINT_SPEED "1.0"
32 #define DEF_POINT_DELAY "0.05"
33 #define DEF_ZOOM_SPEED "1.0"
34 #define DEF_ZOOM_DELAY "15"
41 GLfloat color[4], color2[4];
46 GLXContext *glx_context;
54 enum { MODE_WAITING, MODE_ADDING, MODE_ZOOMING } mode;
58 GLfloat zooming; /* 1.0 starting zoom, 0.0 no longer zooming. */
59 GLfloat zoom_toward[2];
61 } voronoi_configuration;
63 static voronoi_configuration *vps = NULL;
65 /* command line arguments */
67 static GLfloat point_size, point_speed, point_delay;
68 static GLfloat zoom_speed, zoom_delay;
70 static XrmOptionDescRec opts[] = {
71 { "-points", ".points", XrmoptionSepArg, 0 },
72 { "-point-size", ".pointSize", XrmoptionSepArg, 0 },
73 { "-point-speed", ".pointSpeed", XrmoptionSepArg, 0 },
74 { "-point-delay", ".pointDelay", XrmoptionSepArg, 0 },
75 { "-zoom-speed", ".zoomSpeed", XrmoptionSepArg, 0 },
76 { "-zoom-delay", ".zoomDelay", XrmoptionSepArg, 0 },
79 static argtype vars[] = {
80 {&npoints, "points", "Points", DEF_POINTS, t_Int},
81 {&point_size, "pointSize", "PointSize", DEF_POINT_SIZE, t_Float},
82 {&point_speed, "pointSpeed", "PointSpeed", DEF_POINT_SPEED, t_Float},
83 {&point_delay, "pointDelay", "PointDelay", DEF_POINT_DELAY, t_Float},
84 {&zoom_speed, "zoomSpeed", "ZoomSpeed", DEF_ZOOM_SPEED, t_Float},
85 {&zoom_delay, "zoomDelay", "ZoomDelay", DEF_ZOOM_DELAY, t_Float},
88 ENTRYPOINT ModeSpecOpt voronoi_opts =
89 {countof(opts), opts, countof(vars), vars, NULL};
92 /* Returns the current time in seconds as a double.
98 # ifdef GETTIMEOFDAY_TWO_ARGS
100 gettimeofday(&now, &tzp);
105 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
110 add_node (voronoi_configuration *vp, GLfloat x, GLfloat y)
112 node *nn = (node *) calloc (1, sizeof (*nn));
117 i = random() % vp->ncolors;
118 nn->color[0] = vp->colors[i].red / 65536.0;
119 nn->color[1] = vp->colors[i].green / 65536.0;
120 nn->color[2] = vp->colors[i].blue / 65536.0;
123 nn->color2[0] = nn->color[0] * 0.7;
124 nn->color2[1] = nn->color[1] * 0.7;
125 nn->color2[2] = nn->color[2] * 0.7;
128 nn->ddx = frand (0.000001 * point_speed) * (random() & 1 ? 1 : -1);
129 nn->ddy = frand (0.000001 * point_speed) * (random() & 1 ? 1 : -1);
131 nn->rot = (random() % 360) * (random() & 1 ? 1 : -1);
133 nn->next = vp->nodes;
145 GLfloat step = M_PI * 2 / faces;
150 glBegin(GL_TRIANGLE_FAN);
151 glVertex3f (0, 0, 1);
152 for (i = 0; i < faces; i++)
154 glVertex3f (x, y, 0);
159 glVertex3f (1, 0, 0);
166 move_points (voronoi_configuration *vp)
169 for (nn = vp->nodes; nn; nn = nn->next)
171 if (nn == vp->dragging) continue;
175 if (vp->mode == MODE_WAITING)
185 prune_points (voronoi_configuration *vp)
191 for (nn = vp->nodes; nn; prev = nn, nn = (nn ? nn->next : 0))
192 if (nn->x < -lim || nn->x > lim ||
193 nn->y < -lim || nn->y > lim)
196 prev->next = nn->next;
198 vp->nodes = nn->next;
207 zoom_points (voronoi_configuration *vp)
211 GLfloat tick = sin (vp->zooming * M_PI);
212 GLfloat scale = 1 + (tick * 0.02 * zoom_speed);
214 vp->zooming -= (0.01 * zoom_speed);
215 if (vp->zooming < 0) vp->zooming = 0;
217 if (vp->zooming <= 0) return;
219 if (scale < 1) scale = 1;
221 for (nn = vp->nodes; nn; nn = nn->next)
223 GLfloat x = nn->x - vp->zoom_toward[0];
224 GLfloat y = nn->y - vp->zoom_toward[1];
227 nn->x = x + vp->zoom_toward[0];
228 nn->y = y + vp->zoom_toward[1];
235 draw_cells (ModeInfo *mi)
237 voronoi_configuration *vp = &vps[MI_SCREEN(mi)];
241 for (nn = vp->nodes; nn; nn = nn->next)
243 if (nn->x < -lim || nn->x > lim ||
244 nn->y < -lim || nn->y > lim)
248 glTranslatef (nn->x, nn->y, 0);
249 glScalef (lim*2, lim*2, 1);
250 glColor4fv (nn->color);
251 mi->polygon_count += cone ();
255 glClear (GL_DEPTH_BUFFER_BIT);
257 if (vp->point_size <= 0)
259 else if (vp->point_size < 3)
261 glPointSize (vp->point_size);
262 for (nn = vp->nodes; nn; nn = nn->next)
265 glColor4fv (nn->color2);
266 glVertex2f (nn->x, nn->y);
273 for (nn = vp->nodes; nn; nn = nn->next)
275 int w = MI_WIDTH (mi);
276 int h = MI_HEIGHT (mi);
277 int s = vp->point_size;
280 glColor4fv (nn->color2);
282 glTranslatef (nn->x, nn->y, 0);
283 glScalef (1.0 / w * s, 1.0 / h * s, 1);
285 glLineWidth (vp->point_size / 10);
286 nn->rot += (nn->rot < 0 ? -1 : 1);
287 glRotatef (nn->rot, 0, 0, 1);
289 glRotatef (180, 0, 0, 1);
290 for (i = 0; i < 5; i++)
292 glBegin (GL_TRIANGLES);
294 glVertex2f (-0.2, 0);
295 glVertex2f ( 0.2, 0);
297 glRotatef (360.0/5, 0, 0, 1);
307 glBegin(GL_LINE_LOOP);
313 glScalef(0.25, 0.25, 1);
314 glBegin(GL_LINE_LOOP);
325 /* Window management, etc
328 reshape_voronoi (ModeInfo *mi, int width, int height)
330 /* voronoi_configuration *vp = &vps[MI_SCREEN(mi)];*/
332 glViewport (0, 0, (GLint) width, (GLint) height);
334 glMatrixMode(GL_PROJECTION);
336 glOrtho (0, 1, 1, 0, -1, 1);
338 # ifdef HAVE_MOBILE /* So much WTF */
340 int rot = current_device_rotation();
342 glTranslatef (0.5, 0.5, 0);
343 // glScalef(0.19, 0.19, 0.19);
345 if (rot == 180 || rot == -180) {
346 glTranslatef (1, 1, 0);
347 } else if (rot == 90 || rot == -270) {
348 glRotatef (180, 0, 0, 1);
349 glTranslatef (0, 1, 0);
350 } else if (rot == -90 || rot == 270) {
351 glRotatef (180, 0, 0, 1);
352 glTranslatef (1, 0, 0);
355 glTranslatef(-0.5, -0.5, 0);
359 glMatrixMode(GL_MODELVIEW);
362 glClear(GL_COLOR_BUFFER_BIT);
367 find_node (ModeInfo *mi, GLfloat x, GLfloat y)
369 voronoi_configuration *vp = &vps[MI_SCREEN(mi)];
370 int ps = (vp->point_size < 5 ? 5 : vp->point_size);
371 GLfloat hysteresis = (1.0 / MI_WIDTH (mi)) * ps;
373 for (nn = vp->nodes; nn; nn = nn->next)
374 if (nn->x > x - hysteresis && nn->x < x + hysteresis &&
375 nn->y > y - hysteresis && nn->y < y + hysteresis)
382 voronoi_handle_event (ModeInfo *mi, XEvent *event)
384 voronoi_configuration *vp = &vps[MI_SCREEN(mi)];
386 if (event->xany.type == ButtonPress)
388 GLfloat x = (GLfloat) event->xbutton.x / MI_WIDTH (mi);
389 GLfloat y = (GLfloat) event->xbutton.y / MI_HEIGHT (mi);
390 node *nn = find_node (mi, x, y);
392 nn = add_node (vp, x, y);
397 else if (event->xany.type == ButtonRelease && vp->dragging)
402 else if (event->xany.type == MotionNotify && vp->dragging)
404 vp->dragging->x = (GLfloat) event->xmotion.x / MI_WIDTH (mi);
405 vp->dragging->y = (GLfloat) event->xmotion.y / MI_HEIGHT (mi);
413 state_change (ModeInfo *mi)
415 voronoi_configuration *vp = &vps[MI_SCREEN(mi)];
416 double now = double_time();
429 if (vp->last_time + zoom_delay <= now)
431 node *tn = vp->nodes;
432 vp->zoom_toward[0] = (tn ? tn->x : 0.5);
433 vp->zoom_toward[1] = (tn ? tn->y : 0.5);
435 vp->mode = MODE_ZOOMING;
443 if (vp->last_time + point_delay <= now)
446 BELLRAND(0.5) + 0.25,
447 BELLRAND(0.5) + 0.25);
453 vp->mode = MODE_WAITING;
462 if (vp->zooming <= 0)
464 vp->mode = MODE_ADDING;
465 vp->adding = npoints;
478 init_voronoi (ModeInfo *mi)
480 voronoi_configuration *vp;
484 vp = &vps[MI_SCREEN(mi)];
486 vp->glx_context = init_GL(mi);
488 vp->point_size = point_size;
489 if (vp->point_size < 0) vp->point_size = 10;
491 if (MI_WIDTH(mi) > 2560) vp->point_size *= 2; /* Retina displays */
494 vp->colors = (XColor *) calloc (vp->ncolors, sizeof(XColor));
495 make_smooth_colormap (0, 0, 0,
496 vp->colors, &vp->ncolors,
497 False, False, False);
499 reshape_voronoi (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
501 vp->mode = MODE_ADDING;
502 vp->adding = npoints * 2;
508 draw_voronoi (ModeInfo *mi)
510 voronoi_configuration *vp = &vps[MI_SCREEN(mi)];
511 Display *dpy = MI_DISPLAY(mi);
512 Window window = MI_WINDOW(mi);
514 if (!vp->glx_context)
517 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(vp->glx_context));
519 glShadeModel(GL_FLAT);
520 glEnable(GL_POINT_SMOOTH);
521 /* glEnable(GL_LINE_SMOOTH);*/
522 /* glEnable(GL_POLYGON_SMOOTH);*/
524 glEnable (GL_DEPTH_TEST);
525 glDepthFunc (GL_LEQUAL);
527 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
529 mi->polygon_count = 0;
535 if (mi->fps_p) do_fps (mi);
538 glXSwapBuffers(dpy, window);
541 XSCREENSAVER_MODULE ("Voronoi", voronoi)