1 /* quasicrystal, Copyright (c) 2013 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 * Overlapping sine waves create interesting plane-tiling interference
12 * patterns. Created by jwz, Jul 2013. Inspired by
13 * http://mainisusuallyafunction.blogspot.com/2011/10/quasicrystals-as-sums-of-waves-in-plane.html
17 #define DEFAULTS "*delay: 30000 \n" \
20 "*symmetric: True \n" \
23 "*showFPS: False \n" \
24 "*wireframe: False \n" \
25 "*suppressRotationAnimation: True\n" \
27 # define free_quasicrystal 0
28 # define release_quasicrystal 0
30 #define countof(x) (sizeof((x))/sizeof((*x)))
32 #include "xlockmore.h"
37 #ifdef USE_GL /* whole file */
39 #define DEF_SPEED "1.0"
47 GLXContext *glx_context;
57 } quasicrystal_configuration;
59 static quasicrystal_configuration *bps = NULL;
63 static XrmOptionDescRec opts[] = {
64 { "-spin", ".spin", XrmoptionNoArg, "True" },
65 { "+spin", ".spin", XrmoptionNoArg, "False" },
66 { "-wander", ".wander", XrmoptionNoArg, "True" },
67 { "+wander", ".wander", XrmoptionNoArg, "False" },
68 { "-symmetry", ".symmetric", XrmoptionNoArg, "True" },
69 { "-symmetric", ".symmetric", XrmoptionNoArg, "True" },
70 { "-no-symmetry", ".symmetric", XrmoptionNoArg, "False" },
71 { "-nonsymmetric", ".symmetric", XrmoptionNoArg, "False" },
72 { "-speed", ".speed", XrmoptionSepArg, 0 },
73 { "-contrast", ".contrast", XrmoptionSepArg, 0 },
76 static argtype vars[] = {
77 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
80 ENTRYPOINT ModeSpecOpt quasicrystal_opts = {countof(opts), opts, countof(vars), vars, NULL};
84 /* Window management, etc
87 reshape_quasicrystal (ModeInfo *mi, int width, int height)
89 GLfloat h = (GLfloat) height / (GLfloat) width;
91 glViewport (0, 0, (GLint) width, (GLint) height);
93 glMatrixMode(GL_PROJECTION);
95 glOrtho (0, 1, 1, 0, -1, 1);
97 glMatrixMode(GL_MODELVIEW);
99 glTranslatef (0.5, 0.5, 0);
102 glScalef (1/h, 1/h, 1);
103 glTranslatef (-0.5, -0.5, 0);
104 glClear(GL_COLOR_BUFFER_BIT);
109 quasicrystal_handle_event (ModeInfo *mi, XEvent *event)
111 quasicrystal_configuration *bp = &bps[MI_SCREEN(mi)];
113 if (event->xany.type == ButtonPress &&
114 event->xbutton.button == Button1)
116 bp->button_down_p = True;
117 bp->mousex = event->xbutton.x;
118 bp->mousey = event->xbutton.y;
121 else if (event->xany.type == ButtonRelease &&
122 event->xbutton.button == Button1)
124 bp->button_down_p = False;
127 else if (event->xany.type == ButtonPress && /* wheel up or right */
128 (event->xbutton.button == Button4 ||
129 event->xbutton.button == Button7))
132 if (bp->contrast <= 0) return False;
136 else if (event->xany.type == ButtonPress && /* wheel down or left */
137 (event->xbutton.button == Button5 ||
138 event->xbutton.button == Button6))
141 if (bp->contrast >= 100) return False;
145 else if (event->xany.type == MotionNotify &&
148 /* Dragging up and down tweaks contrast */
150 int dx = event->xmotion.x - bp->mousex;
151 int dy = event->xmotion.y - bp->mousey;
153 if (abs(dy) > abs(dx))
155 bp->contrast += dy / 40.0;
156 if (bp->contrast < 0) bp->contrast = 0;
157 if (bp->contrast > 100) bp->contrast = 100;
160 bp->mousex = event->xmotion.x;
161 bp->mousey = event->xmotion.y;
166 if (event->xany.type == KeyPress)
170 XLookupString (&event->xkey, &c, 1, &keysym, 0);
171 if (c == '<' || c == ',' || c == '-' || c == '_' ||
172 keysym == XK_Left || keysym == XK_Up || keysym == XK_Prior)
174 else if (c == '>' || c == '.' || c == '=' || c == '+' ||
175 keysym == XK_Right || keysym == XK_Down ||
180 return screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event);
189 init_quasicrystal (ModeInfo *mi)
191 quasicrystal_configuration *bp;
192 int wire = MI_IS_WIREFRAME(mi);
193 unsigned char *tex_data = 0;
199 bp = &bps[MI_SCREEN(mi)];
201 bp->glx_context = init_GL(mi);
203 reshape_quasicrystal (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
205 glDisable (GL_DEPTH_TEST);
206 glEnable (GL_CULL_FACE);
208 bp->count = MI_COUNT(mi);
209 if (bp->count < 1) bp->count = 1;
216 glGetIntegerv (GL_MAX_TEXTURE_SIZE, &tex_width);
217 if (tex_width > 4096) tex_width = 4096;
219 tex_data = (unsigned char *) calloc (4, tex_width);
221 for (i = 0; i < tex_width; i++)
223 unsigned char y = 255 * (1 + sin (i * M_PI * 2 / tex_width)) / 2;
232 get_boolean_resource (MI_DISPLAY (mi), "symmetric", "Symmetric");
234 bp->contrast = get_float_resource (MI_DISPLAY (mi), "contrast", "Contrast");
235 if (bp->contrast < 0 || bp->contrast > 100)
237 fprintf (stderr, "%s: contrast must be between 0 and 100%%.\n", progname);
242 Bool spinp = get_boolean_resource (MI_DISPLAY (mi), "spin", "Spin");
243 Bool wanderp = get_boolean_resource (MI_DISPLAY (mi), "wander", "Wander");
244 double spin_speed = 0.01;
245 double wander_speed = 0.0001;
246 double spin_accel = 10.0;
247 double scale_speed = 0.005;
249 bp->planes = (plane *) calloc (sizeof (*bp->planes), bp->count);
251 bp->ncolors = 256; /* ncolors affects color-cycling speed */
252 bp->colors = (XColor *) calloc (bp->ncolors, sizeof(XColor));
253 make_smooth_colormap (0, 0, 0, bp->colors, &bp->ncolors,
257 for (i = 0; i < bp->count; i++)
259 plane *p = &bp->planes[i];
260 p->rot = make_rotator (0, 0,
261 spinp ? spin_speed : 0,
263 wanderp ? wander_speed : 0,
265 p->rot2 = make_rotator (0, 0,
273 glGenTextures (1, &p->texid);
274 glBindTexture (GL_TEXTURE_1D, p->texid);
275 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
276 glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA,
278 GL_RGBA, GL_UNSIGNED_BYTE, tex_data);
279 check_gl_error("texture");
281 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
282 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
284 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
285 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
287 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
292 if (tex_data) free (tex_data);
294 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
299 draw_quasicrystal (ModeInfo *mi)
301 quasicrystal_configuration *bp = &bps[MI_SCREEN(mi)];
302 Display *dpy = MI_DISPLAY(mi);
303 Window window = MI_WINDOW(mi);
304 int wire = MI_IS_WIREFRAME(mi);
308 if (!bp->glx_context)
311 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
313 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
315 mi->polygon_count = 0;
317 glShadeModel(GL_FLAT);
318 glDisable(GL_DEPTH_TEST);
319 glDisable(GL_CULL_FACE);
320 glDisable (GL_LIGHTING);
323 glEnable (GL_TEXTURE_1D);
325 glEnable (GL_TEXTURE_2D); /* jwzgles needs this, bleh. */
330 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
333 glTranslatef (0.5, 0.5, 0);
336 if (wire) glScalef (0.2, 0.2, 0.2);
338 for (i = 0; i < bp->count; i++)
340 plane *p = &bp->planes[i];
342 double scale = (wire ? 10 : 700.0 / bp->count);
347 get_position (p->rot, &x, &y, &z, !bp->button_down_p);
348 glTranslatef((x - 0.5) * 0.3333,
352 /* With -symmetry, keep the planes' scales in sync.
353 Otherwise, they scale independently.
355 if (bp->symmetric_p && i > 0)
359 get_position (p->rot2, &x, &y, &z, !bp->button_down_p);
360 pscale = 1 + (4 * z);
367 /* With -symmetry, evenly distribute the planes' rotation.
368 Otherwise, they rotate independently.
370 if (bp->symmetric_p && i > 0)
371 z = r + (i * M_PI * 2 / bp->count);
374 get_rotation (p->rot, &x, &y, &z, !bp->button_down_p);
379 glRotatef (z * 360, 0, 0, 1);
380 glTranslatef (-0.5, -0.5, 0);
382 glColor4f (1, 1, 1, (wire ? 0.5 : 1.0 / bp->count));
385 glBindTexture (GL_TEXTURE_1D, p->texid);
387 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
388 glNormal3f (0, 0, 1);
389 glTexCoord2f (-scale/2, scale/2); glVertex3f (0, 1, 0);
390 glTexCoord2f ( scale/2, scale/2); glVertex3f (1, 1, 0);
391 glTexCoord2f ( scale/2, -scale/2); glVertex3f (1, 0, 0);
392 glTexCoord2f (-scale/2, -scale/2); glVertex3f (0, 0, 0);
398 glDisable (GL_TEXTURE_1D);
399 glColor4f (1, 1, 1, 1.0 / bp->count);
400 for (j = 0; j < 1; j += (1 / scale))
403 glVertex3f (j, 0, 0);
404 glVertex3f (j, 1, 0);
415 /* Colorize the grayscale image. */
418 c[0] = bp->colors[bp->ccolor].red / 65536.0;
419 c[1] = bp->colors[bp->ccolor].green / 65536.0;
420 c[2] = bp->colors[bp->ccolor].blue / 65536.0;
423 /* Brighten the colors. */
424 c[0] = (0.6666 + c[0]/3);
425 c[1] = (0.6666 + c[1]/3);
426 c[2] = (0.6666 + c[2]/3);
428 glBlendFunc (GL_DST_COLOR, GL_SRC_COLOR);
429 glDisable (GL_TEXTURE_1D);
431 glTranslatef (-0.5, -0.5, 0);
433 glVertex3f (0, 1, 0);
434 glVertex3f (1, 1, 0);
435 glVertex3f (1, 0, 0);
436 glVertex3f (0, 0, 0);
441 /* Clip the colors to simulate contrast. */
443 if (bp->contrast > 0)
445 /* If c > 0, map 0 - 100 to 0.5 - 1.0, and use (s & ~d) */
446 GLfloat c = 1 - (bp->contrast / 2 / 100.0);
447 glDisable (GL_TEXTURE_1D);
448 glDisable (GL_BLEND);
449 glEnable (GL_COLOR_LOGIC_OP);
450 glLogicOp (GL_AND_REVERSE);
451 glColor4f (c, c, c, 1);
453 glVertex3f (0, 1, 0);
454 glVertex3f (1, 1, 0);
455 glVertex3f (1, 0, 0);
456 glVertex3f (0, 0, 0);
459 glDisable (GL_COLOR_LOGIC_OP);
464 if (bp->ccolor >= bp->ncolors)
470 if (mi->fps_p) do_fps (mi);
473 glXSwapBuffers(dpy, window);
476 XSCREENSAVER_MODULE ("QuasiCrystal", quasicrystal)