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" \
26 # define refresh_quasicrystal 0
27 # define release_quasicrystal 0
29 #define countof(x) (sizeof((x))/sizeof((*x)))
31 #include "xlockmore.h"
36 #ifdef USE_GL /* whole file */
38 #define DEF_SPEED "1.0"
46 GLXContext *glx_context;
56 } quasicrystal_configuration;
58 static quasicrystal_configuration *bps = NULL;
62 static XrmOptionDescRec opts[] = {
63 { "-spin", ".spin", XrmoptionNoArg, "True" },
64 { "+spin", ".spin", XrmoptionNoArg, "False" },
65 { "-wander", ".wander", XrmoptionNoArg, "True" },
66 { "+wander", ".wander", XrmoptionNoArg, "False" },
67 { "-symmetry", ".symmetric", XrmoptionNoArg, "True" },
68 { "-no-symmetry", ".symmetric", XrmoptionNoArg, "False" },
69 { "-speed", ".speed", XrmoptionSepArg, 0 },
70 { "-contrast", ".contrast", XrmoptionSepArg, 0 },
73 static argtype vars[] = {
74 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
77 ENTRYPOINT ModeSpecOpt quasicrystal_opts = {countof(opts), opts, countof(vars), vars, NULL};
81 /* Window management, etc
84 reshape_quasicrystal (ModeInfo *mi, int width, int height)
86 GLfloat h = (GLfloat) height / (GLfloat) width;
88 glViewport (0, 0, (GLint) width, (GLint) height);
90 glMatrixMode(GL_PROJECTION);
92 glOrtho (0, 1, 1, 0, -1, 1);
94 glMatrixMode(GL_MODELVIEW);
96 glTranslatef (0.5, 0.5, 0);
99 glScalef (1/h, 1/h, 1);
100 glTranslatef (-0.5, -0.5, 0);
101 glClear(GL_COLOR_BUFFER_BIT);
106 quasicrystal_handle_event (ModeInfo *mi, XEvent *event)
108 quasicrystal_configuration *bp = &bps[MI_SCREEN(mi)];
110 if (event->xany.type == ButtonPress &&
111 event->xbutton.button == Button1)
113 bp->button_down_p = True;
114 bp->mousex = event->xbutton.x;
115 bp->mousey = event->xbutton.y;
118 else if (event->xany.type == ButtonRelease &&
119 event->xbutton.button == Button1)
121 bp->button_down_p = False;
124 else if (event->xany.type == ButtonPress && /* wheel up or right */
125 (event->xbutton.button == Button4 ||
126 event->xbutton.button == Button7))
129 if (bp->contrast <= 0) return False;
133 else if (event->xany.type == ButtonPress && /* wheel down or left */
134 (event->xbutton.button == Button5 ||
135 event->xbutton.button == Button6))
138 if (bp->contrast >= 100) return False;
142 else if (event->xany.type == MotionNotify &&
145 /* Dragging up and down tweaks contrast */
147 int dx = event->xmotion.x - bp->mousex;
148 int dy = event->xmotion.y - bp->mousey;
150 if (abs(dy) > abs(dx))
152 bp->contrast += dy / 40.0;
153 if (bp->contrast < 0) bp->contrast = 0;
154 if (bp->contrast > 100) bp->contrast = 100;
157 bp->mousex = event->xmotion.x;
158 bp->mousey = event->xmotion.y;
163 if (event->xany.type == KeyPress)
167 XLookupString (&event->xkey, &c, 1, &keysym, 0);
168 if (c == '<' || c == ',' || c == '-' || c == '_' ||
169 keysym == XK_Left || keysym == XK_Up || keysym == XK_Prior)
171 else if (c == '>' || c == '.' || c == '=' || c == '+' ||
172 keysym == XK_Right || keysym == XK_Down ||
177 return screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event);
186 init_quasicrystal (ModeInfo *mi)
188 quasicrystal_configuration *bp;
189 int wire = MI_IS_WIREFRAME(mi);
190 unsigned char *tex_data = 0;
195 bps = (quasicrystal_configuration *)
196 calloc (MI_NUM_SCREENS(mi), sizeof (quasicrystal_configuration));
198 fprintf(stderr, "%s: out of memory\n", progname);
203 bp = &bps[MI_SCREEN(mi)];
205 bp->glx_context = init_GL(mi);
207 reshape_quasicrystal (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
209 glDisable (GL_DEPTH_TEST);
210 glEnable (GL_CULL_FACE);
212 bp->count = MI_COUNT(mi);
213 if (bp->count < 1) bp->count = 1;
220 glGetIntegerv (GL_MAX_TEXTURE_SIZE, &tex_width);
221 if (tex_width > 4096) tex_width = 4096;
223 tex_data = (unsigned char *) calloc (4, tex_width);
225 for (i = 0; i < tex_width; i++)
227 unsigned char y = 255 * (1 + sin (i * M_PI * 2 / tex_width)) / 2;
236 get_boolean_resource (MI_DISPLAY (mi), "symmetry", "Symmetry");
238 bp->contrast = get_float_resource (MI_DISPLAY (mi), "contrast", "Contrast");
239 if (bp->contrast < 0 || bp->contrast > 100)
241 fprintf (stderr, "%s: contrast must be between 0 and 100%%.\n", progname);
246 Bool spinp = get_boolean_resource (MI_DISPLAY (mi), "spin", "Spin");
247 Bool wanderp = get_boolean_resource (MI_DISPLAY (mi), "wander", "Wander");
248 double spin_speed = 0.01;
249 double wander_speed = 0.0001;
250 double spin_accel = 10.0;
251 double scale_speed = 0.005;
253 bp->planes = (plane *) calloc (sizeof (*bp->planes), bp->count);
255 bp->ncolors = 256; /* ncolors affects color-cycling speed */
256 bp->colors = (XColor *) calloc (bp->ncolors, sizeof(XColor));
257 make_smooth_colormap (0, 0, 0, bp->colors, &bp->ncolors,
261 for (i = 0; i < bp->count; i++)
263 plane *p = &bp->planes[i];
264 p->rot = make_rotator (0, 0,
265 spinp ? spin_speed : 0,
267 wanderp ? wander_speed : 0,
269 p->rot2 = make_rotator (0, 0,
277 glGenTextures (1, &p->texid);
278 glBindTexture (GL_TEXTURE_1D, p->texid);
279 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
280 glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA,
283 /* GL_UNSIGNED_BYTE, */
284 GL_UNSIGNED_INT_8_8_8_8_REV,
286 check_gl_error("texture");
288 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
289 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
291 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
292 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
294 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
299 if (tex_data) free (tex_data);
301 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
306 draw_quasicrystal (ModeInfo *mi)
308 quasicrystal_configuration *bp = &bps[MI_SCREEN(mi)];
309 Display *dpy = MI_DISPLAY(mi);
310 Window window = MI_WINDOW(mi);
311 int wire = MI_IS_WIREFRAME(mi);
315 if (!bp->glx_context)
318 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
320 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
322 mi->polygon_count = 0;
324 glShadeModel(GL_FLAT);
325 glDisable(GL_DEPTH_TEST);
326 glDisable(GL_CULL_FACE);
327 glDisable (GL_LIGHTING);
330 glEnable (GL_TEXTURE_1D);
332 glEnable (GL_TEXTURE_2D); /* jwzgles needs this, bleh. */
337 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
340 glTranslatef (0.5, 0.5, 0);
343 if (wire) glScalef (0.2, 0.2, 0.2);
345 for (i = 0; i < bp->count; i++)
347 plane *p = &bp->planes[i];
349 double scale = (wire ? 10 : 700.0 / bp->count);
354 get_position (p->rot, &x, &y, &z, !bp->button_down_p);
355 glTranslatef((x - 0.5) * 0.3333,
359 /* With -symmetry, keep the planes' scales in sync.
360 Otherwise, they scale independently.
362 if (bp->symmetric_p && i > 0)
366 get_position (p->rot2, &x, &y, &z, !bp->button_down_p);
367 pscale = 1 + (4 * z);
374 /* With -symmetry, evenly distribute the planes' rotation.
375 Otherwise, they rotate independently.
377 if (bp->symmetric_p && i > 0)
378 z = r + (i * M_PI * 2 / bp->count);
381 get_rotation (p->rot, &x, &y, &z, !bp->button_down_p);
386 glRotatef (z * 360, 0, 0, 1);
387 glTranslatef (-0.5, -0.5, 0);
389 glColor4f (1, 1, 1, (wire ? 0.5 : 1.0 / bp->count));
392 glBindTexture (GL_TEXTURE_1D, p->texid);
394 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
395 glNormal3f (0, 0, 1);
396 glTexCoord2f (-scale/2, scale/2); glVertex3f (0, 1, 0);
397 glTexCoord2f ( scale/2, scale/2); glVertex3f (1, 1, 0);
398 glTexCoord2f ( scale/2, -scale/2); glVertex3f (1, 0, 0);
399 glTexCoord2f (-scale/2, -scale/2); glVertex3f (0, 0, 0);
405 glDisable (GL_TEXTURE_1D);
406 glColor4f (1, 1, 1, 1.0 / bp->count);
407 for (j = 0; j < 1; j += (1 / scale))
410 glVertex3f (j, 0, 0);
411 glVertex3f (j, 1, 0);
422 /* Colorize the grayscale image. */
425 c[0] = bp->colors[bp->ccolor].red / 65536.0;
426 c[1] = bp->colors[bp->ccolor].green / 65536.0;
427 c[2] = bp->colors[bp->ccolor].blue / 65536.0;
430 /* Brighten the colors. */
431 c[0] = (0.6666 + c[0]/3);
432 c[1] = (0.6666 + c[1]/3);
433 c[2] = (0.6666 + c[2]/3);
435 glBlendFunc (GL_DST_COLOR, GL_SRC_COLOR);
436 glDisable (GL_TEXTURE_1D);
438 glTranslatef (-0.5, -0.5, 0);
440 glVertex3f (0, 1, 0);
441 glVertex3f (1, 1, 0);
442 glVertex3f (1, 0, 0);
443 glVertex3f (0, 0, 0);
448 /* Clip the colors to simulate contrast. */
450 if (bp->contrast > 0)
452 /* If c > 0, map 0 - 100 to 0.5 - 1.0, and use (s & ~d) */
453 GLfloat c = 1 - (bp->contrast / 2 / 100.0);
454 glDisable (GL_TEXTURE_1D);
455 glDisable (GL_BLEND);
456 glEnable (GL_COLOR_LOGIC_OP);
457 glLogicOp (GL_AND_REVERSE);
458 glColor4f (c, c, c, 1);
460 glVertex3f (0, 1, 0);
461 glVertex3f (1, 1, 0);
462 glVertex3f (1, 0, 0);
463 glVertex3f (0, 0, 0);
466 glDisable (GL_COLOR_LOGIC_OP);
471 if (bp->ccolor >= bp->ncolors)
477 if (mi->fps_p) do_fps (mi);
480 glXSwapBuffers(dpy, window);
483 XSCREENSAVER_MODULE ("QuasiCrystal", quasicrystal)