summaryrefslogtreecommitdiffstats
path: root/hacks/glx/voronoi.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/glx/voronoi.c')
-rw-r--r--hacks/glx/voronoi.c543
1 files changed, 543 insertions, 0 deletions
diff --git a/hacks/glx/voronoi.c b/hacks/glx/voronoi.c
new file mode 100644
index 0000000..895f967
--- /dev/null
+++ b/hacks/glx/voronoi.c
@@ -0,0 +1,543 @@
+/* voronoi, Copyright (c) 2007-2018 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#define DEFAULTS "*delay: 20000 \n" \
+ "*showFPS: False \n" \
+ "*suppressRotationAnimation: True\n" \
+
+# define free_voronoi 0
+# define release_voronoi 0
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
+
+
+#include "xlockmore.h"
+#include <ctype.h>
+
+#ifdef USE_GL /* whole file */
+
+#define DEF_POINTS "25"
+#define DEF_POINT_SIZE "9"
+#define DEF_POINT_SPEED "1.0"
+#define DEF_POINT_DELAY "0.05"
+#define DEF_ZOOM_SPEED "1.0"
+#define DEF_ZOOM_DELAY "15"
+
+typedef struct node {
+ GLfloat x, y;
+ GLfloat dx, dy;
+ GLfloat ddx, ddy;
+ struct node *next;
+ GLfloat color[4], color2[4];
+ int rot;
+} node;
+
+typedef struct {
+ GLXContext *glx_context;
+ node *nodes;
+ int nnodes;
+ node *dragging;
+ int ncolors;
+ XColor *colors;
+ int point_size;
+
+ enum { MODE_WAITING, MODE_ADDING, MODE_ZOOMING } mode;
+ int adding;
+ double last_time;
+
+ GLfloat zooming; /* 1.0 starting zoom, 0.0 no longer zooming. */
+ GLfloat zoom_toward[2];
+
+} voronoi_configuration;
+
+static voronoi_configuration *vps = NULL;
+
+/* command line arguments */
+static int npoints;
+static GLfloat point_size, point_speed, point_delay;
+static GLfloat zoom_speed, zoom_delay;
+
+static XrmOptionDescRec opts[] = {
+ { "-points", ".points", XrmoptionSepArg, 0 },
+ { "-point-size", ".pointSize", XrmoptionSepArg, 0 },
+ { "-point-speed", ".pointSpeed", XrmoptionSepArg, 0 },
+ { "-point-delay", ".pointDelay", XrmoptionSepArg, 0 },
+ { "-zoom-speed", ".zoomSpeed", XrmoptionSepArg, 0 },
+ { "-zoom-delay", ".zoomDelay", XrmoptionSepArg, 0 },
+};
+
+static argtype vars[] = {
+ {&npoints, "points", "Points", DEF_POINTS, t_Int},
+ {&point_size, "pointSize", "PointSize", DEF_POINT_SIZE, t_Float},
+ {&point_speed, "pointSpeed", "PointSpeed", DEF_POINT_SPEED, t_Float},
+ {&point_delay, "pointDelay", "PointDelay", DEF_POINT_DELAY, t_Float},
+ {&zoom_speed, "zoomSpeed", "ZoomSpeed", DEF_ZOOM_SPEED, t_Float},
+ {&zoom_delay, "zoomDelay", "ZoomDelay", DEF_ZOOM_DELAY, t_Float},
+};
+
+ENTRYPOINT ModeSpecOpt voronoi_opts =
+ {countof(opts), opts, countof(vars), vars, NULL};
+
+
+/* Returns the current time in seconds as a double.
+ */
+static double
+double_time (void)
+{
+ struct timeval now;
+# ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+ gettimeofday(&now, &tzp);
+# else
+ gettimeofday(&now);
+# endif
+
+ return (now.tv_sec + ((double) now.tv_usec * 0.000001));
+}
+
+
+static node *
+add_node (voronoi_configuration *vp, GLfloat x, GLfloat y)
+{
+ node *nn = (node *) calloc (1, sizeof (*nn));
+ int i;
+ nn->x = x;
+ nn->y = y;
+
+ i = random() % vp->ncolors;
+ nn->color[0] = vp->colors[i].red / 65536.0;
+ nn->color[1] = vp->colors[i].green / 65536.0;
+ nn->color[2] = vp->colors[i].blue / 65536.0;
+ nn->color[3] = 1.0;
+
+ nn->color2[0] = nn->color[0] * 0.7;
+ nn->color2[1] = nn->color[1] * 0.7;
+ nn->color2[2] = nn->color[2] * 0.7;
+ nn->color2[3] = 1.0;
+
+ nn->ddx = frand (0.000001 * point_speed) * (random() & 1 ? 1 : -1);
+ nn->ddy = frand (0.000001 * point_speed) * (random() & 1 ? 1 : -1);
+
+ nn->rot = (random() % 360) * (random() & 1 ? 1 : -1);
+
+ nn->next = vp->nodes;
+ vp->nodes = nn;
+ vp->nnodes++;
+ return nn;
+}
+
+
+static int
+cone (void)
+{
+ int i;
+ int faces = 64;
+ GLfloat step = M_PI * 2 / faces;
+ GLfloat th = 0;
+ GLfloat x = 1;
+ GLfloat y = 0;
+
+ glBegin(GL_TRIANGLE_FAN);
+ glVertex3f (0, 0, 1);
+ for (i = 0; i < faces; i++)
+ {
+ glVertex3f (x, y, 0);
+ th += step;
+ x = cos (th);
+ y = sin (th);
+ }
+ glVertex3f (1, 0, 0);
+ glEnd();
+ return faces;
+}
+
+
+static void
+move_points (voronoi_configuration *vp)
+{
+ node *nn;
+ for (nn = vp->nodes; nn; nn = nn->next)
+ {
+ if (nn == vp->dragging) continue;
+ nn->x += nn->dx;
+ nn->y += nn->dy;
+
+ if (vp->mode == MODE_WAITING)
+ {
+ nn->dx += nn->ddx;
+ nn->dy += nn->ddy;
+ }
+ }
+}
+
+
+static void
+prune_points (voronoi_configuration *vp)
+{
+ node *nn;
+ node *prev = 0;
+ int lim = 5;
+
+ for (nn = vp->nodes; nn; prev = nn, nn = (nn ? nn->next : 0))
+ if (nn->x < -lim || nn->x > lim ||
+ nn->y < -lim || nn->y > lim)
+ {
+ if (prev)
+ prev->next = nn->next;
+ else
+ vp->nodes = nn->next;
+ free (nn);
+ vp->nnodes--;
+ nn = prev;
+ }
+}
+
+
+static void
+zoom_points (voronoi_configuration *vp)
+{
+ node *nn;
+
+ GLfloat tick = sin (vp->zooming * M_PI);
+ GLfloat scale = 1 + (tick * 0.02 * zoom_speed);
+
+ vp->zooming -= (0.01 * zoom_speed);
+ if (vp->zooming < 0) vp->zooming = 0;
+
+ if (vp->zooming <= 0) return;
+
+ if (scale < 1) scale = 1;
+
+ for (nn = vp->nodes; nn; nn = nn->next)
+ {
+ GLfloat x = nn->x - vp->zoom_toward[0];
+ GLfloat y = nn->y - vp->zoom_toward[1];
+ x *= scale;
+ y *= scale;
+ nn->x = x + vp->zoom_toward[0];
+ nn->y = y + vp->zoom_toward[1];
+ }
+}
+
+
+
+static void
+draw_cells (ModeInfo *mi)
+{
+ voronoi_configuration *vp = &vps[MI_SCREEN(mi)];
+ node *nn;
+ int lim = 5;
+
+ for (nn = vp->nodes; nn; nn = nn->next)
+ {
+ if (nn->x < -lim || nn->x > lim ||
+ nn->y < -lim || nn->y > lim)
+ continue;
+
+ glPushMatrix();
+ glTranslatef (nn->x, nn->y, 0);
+ glScalef (lim*2, lim*2, 1);
+ glColor4fv (nn->color);
+ mi->polygon_count += cone ();
+ glPopMatrix();
+ }
+
+ glClear (GL_DEPTH_BUFFER_BIT);
+
+ if (vp->point_size <= 0)
+ ;
+ else if (vp->point_size < 3)
+ {
+ glPointSize (vp->point_size);
+ for (nn = vp->nodes; nn; nn = nn->next)
+ {
+ glBegin (GL_POINTS);
+ glColor4fv (nn->color2);
+ glVertex2f (nn->x, nn->y);
+ glEnd();
+ mi->polygon_count++;
+ }
+ }
+ else
+ {
+ for (nn = vp->nodes; nn; nn = nn->next)
+ {
+ int w = MI_WIDTH (mi);
+ int h = MI_HEIGHT (mi);
+ int s = vp->point_size;
+ int i;
+
+ glColor4fv (nn->color2);
+ glPushMatrix();
+ glTranslatef (nn->x, nn->y, 0);
+ glScalef (1.0 / w * s, 1.0 / h * s, 1);
+
+ glLineWidth (vp->point_size / 10);
+ nn->rot += (nn->rot < 0 ? -1 : 1);
+ glRotatef (nn->rot, 0, 0, 1);
+
+ glRotatef (180, 0, 0, 1);
+ for (i = 0; i < 5; i++)
+ {
+ glBegin (GL_TRIANGLES);
+ glVertex2f (0, 1);
+ glVertex2f (-0.2, 0);
+ glVertex2f ( 0.2, 0);
+ glEnd ();
+ glRotatef (360.0/5, 0, 0, 1);
+ mi->polygon_count++;
+ }
+ glPopMatrix();
+ }
+ }
+
+#if 0
+ glPushMatrix();
+ glColor3f(1,1,1);
+ glBegin(GL_LINE_LOOP);
+ glVertex3f(0,0,0);
+ glVertex3f(1,0,0);
+ glVertex3f(1,1,0);
+ glVertex3f(0,1,0);
+ glEnd();
+ glScalef(0.25, 0.25, 1);
+ glBegin(GL_LINE_LOOP);
+ glVertex3f(0,0,0);
+ glVertex3f(1,0,0);
+ glVertex3f(1,1,0);
+ glVertex3f(0,1,0);
+ glEnd();
+ glPopMatrix();
+#endif
+}
+
+
+/* Window management, etc
+ */
+ENTRYPOINT void
+reshape_voronoi (ModeInfo *mi, int width, int height)
+{
+/* voronoi_configuration *vp = &vps[MI_SCREEN(mi)];*/
+
+ glViewport (0, 0, (GLint) width, (GLint) height);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho (0, 1, 1, 0, -1, 1);
+
+# ifdef HAVE_MOBILE /* So much WTF */
+ {
+ int rot = current_device_rotation();
+
+ glTranslatef (0.5, 0.5, 0);
+ // glScalef(0.19, 0.19, 0.19);
+
+ if (rot == 180 || rot == -180) {
+ glTranslatef (1, 1, 0);
+ } else if (rot == 90 || rot == -270) {
+ glRotatef (180, 0, 0, 1);
+ glTranslatef (0, 1, 0);
+ } else if (rot == -90 || rot == 270) {
+ glRotatef (180, 0, 0, 1);
+ glTranslatef (1, 0, 0);
+ }
+
+ glTranslatef(-0.5, -0.5, 0);
+ }
+# endif
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+
+static node *
+find_node (ModeInfo *mi, GLfloat x, GLfloat y)
+{
+ voronoi_configuration *vp = &vps[MI_SCREEN(mi)];
+ int ps = (vp->point_size < 5 ? 5 : vp->point_size);
+ GLfloat hysteresis = (1.0 / MI_WIDTH (mi)) * ps;
+ node *nn;
+ for (nn = vp->nodes; nn; nn = nn->next)
+ if (nn->x > x - hysteresis && nn->x < x + hysteresis &&
+ nn->y > y - hysteresis && nn->y < y + hysteresis)
+ return nn;
+ return 0;
+}
+
+
+ENTRYPOINT Bool
+voronoi_handle_event (ModeInfo *mi, XEvent *event)
+{
+ voronoi_configuration *vp = &vps[MI_SCREEN(mi)];
+
+ if (event->xany.type == ButtonPress)
+ {
+ GLfloat x = (GLfloat) event->xbutton.x / MI_WIDTH (mi);
+ GLfloat y = (GLfloat) event->xbutton.y / MI_HEIGHT (mi);
+ node *nn = find_node (mi, x, y);
+ if (!nn)
+ nn = add_node (vp, x, y);
+ vp->dragging = nn;
+
+ return True;
+ }
+ else if (event->xany.type == ButtonRelease && vp->dragging)
+ {
+ vp->dragging = 0;
+ return True;
+ }
+ else if (event->xany.type == MotionNotify && vp->dragging)
+ {
+ vp->dragging->x = (GLfloat) event->xmotion.x / MI_WIDTH (mi);
+ vp->dragging->y = (GLfloat) event->xmotion.y / MI_HEIGHT (mi);
+ return True;
+ }
+
+ return False;
+}
+
+static void
+state_change (ModeInfo *mi)
+{
+ voronoi_configuration *vp = &vps[MI_SCREEN(mi)];
+ double now = double_time();
+
+ if (vp->dragging)
+ {
+ vp->last_time = now;
+ vp->adding = 0;
+ vp->zooming = 0;
+ return;
+ }
+
+ switch (vp->mode)
+ {
+ case MODE_WAITING:
+ if (vp->last_time + zoom_delay <= now)
+ {
+ node *tn = vp->nodes;
+ vp->zoom_toward[0] = (tn ? tn->x : 0.5);
+ vp->zoom_toward[1] = (tn ? tn->y : 0.5);
+
+ vp->mode = MODE_ZOOMING;
+ vp->zooming = 1;
+
+ vp->last_time = now;
+ }
+ break;
+
+ case MODE_ADDING:
+ if (vp->last_time + point_delay <= now)
+ {
+ add_node (vp,
+ BELLRAND(0.5) + 0.25,
+ BELLRAND(0.5) + 0.25);
+ vp->last_time = now;
+ vp->adding--;
+ if (vp->adding <= 0)
+ {
+ vp->adding = 0;
+ vp->mode = MODE_WAITING;
+ vp->last_time = now;
+ }
+ }
+ break;
+
+ case MODE_ZOOMING:
+ {
+ zoom_points (vp);
+ if (vp->zooming <= 0)
+ {
+ vp->mode = MODE_ADDING;
+ vp->adding = npoints;
+ vp->last_time = now;
+ }
+ }
+ break;
+
+ default:
+ abort();
+ }
+}
+
+
+ENTRYPOINT void
+init_voronoi (ModeInfo *mi)
+{
+ voronoi_configuration *vp;
+
+ MI_INIT (mi, vps);
+
+ vp = &vps[MI_SCREEN(mi)];
+
+ vp->glx_context = init_GL(mi);
+
+ vp->point_size = point_size;
+ if (vp->point_size < 0) vp->point_size = 10;
+
+ if (MI_WIDTH(mi) > 2560) vp->point_size *= 2; /* Retina displays */
+
+ vp->ncolors = 128;
+ vp->colors = (XColor *) calloc (vp->ncolors, sizeof(XColor));
+ make_smooth_colormap (0, 0, 0,
+ vp->colors, &vp->ncolors,
+ False, False, False);
+
+ reshape_voronoi (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+
+ vp->mode = MODE_ADDING;
+ vp->adding = npoints * 2;
+ vp->last_time = 0;
+}
+
+
+ENTRYPOINT void
+draw_voronoi (ModeInfo *mi)
+{
+ voronoi_configuration *vp = &vps[MI_SCREEN(mi)];
+ Display *dpy = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+
+ if (!vp->glx_context)
+ return;
+
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(vp->glx_context));
+
+ glShadeModel(GL_FLAT);
+ glEnable(GL_POINT_SMOOTH);
+/* glEnable(GL_LINE_SMOOTH);*/
+/* glEnable(GL_POLYGON_SMOOTH);*/
+
+ glEnable (GL_DEPTH_TEST);
+ glDepthFunc (GL_LEQUAL);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ mi->polygon_count = 0;
+ draw_cells (mi);
+ move_points (vp);
+ prune_points (vp);
+ state_change (mi);
+
+ if (mi->fps_p) do_fps (mi);
+ glFinish();
+
+ glXSwapBuffers(dpy, window);
+}
+
+XSCREENSAVER_MODULE ("Voronoi", voronoi)
+
+#endif /* USE_GL */