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 == MotionNotify &&
127 /* Dragging up and down tweaks contrast */
129 int dx = event->xmotion.x - bp->mousex;
130 int dy = event->xmotion.y - bp->mousey;
132 if (abs(dy) > abs(dx))
134 bp->contrast += dy / 40.0;
135 if (bp->contrast < 0) bp->contrast = 0;
136 if (bp->contrast > 100) bp->contrast = 100;
139 bp->mousex = event->xmotion.x;
140 bp->mousey = event->xmotion.y;
150 init_quasicrystal (ModeInfo *mi)
152 quasicrystal_configuration *bp;
153 int wire = MI_IS_WIREFRAME(mi);
154 unsigned char *tex_data = 0;
159 bps = (quasicrystal_configuration *)
160 calloc (MI_NUM_SCREENS(mi), sizeof (quasicrystal_configuration));
162 fprintf(stderr, "%s: out of memory\n", progname);
167 bp = &bps[MI_SCREEN(mi)];
169 bp->glx_context = init_GL(mi);
171 reshape_quasicrystal (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
173 glDisable (GL_DEPTH_TEST);
174 glEnable (GL_CULL_FACE);
176 bp->count = MI_COUNT(mi);
177 if (bp->count < 1) bp->count = 1;
183 tex_data = (unsigned char *) calloc (4, tex_width);
185 for (i = 0; i < tex_width; i++)
187 unsigned char y = 255 * (1 + sin (i * M_PI * 2 / tex_width)) / 2;
196 get_boolean_resource (MI_DISPLAY (mi), "symmetry", "Symmetry");
198 bp->contrast = get_float_resource (MI_DISPLAY (mi), "contrast", "Contrast");
199 if (bp->contrast < 0 || bp->contrast > 100)
201 fprintf (stderr, "%s: contrast must be between 0 and 100%%.\n", progname);
206 Bool spinp = get_boolean_resource (MI_DISPLAY (mi), "spin", "Spin");
207 Bool wanderp = get_boolean_resource (MI_DISPLAY (mi), "wander", "Wander");
208 double spin_speed = 0.01;
209 double wander_speed = 0.0001;
210 double spin_accel = 10.0;
211 double scale_speed = 0.005;
213 bp->planes = (plane *) calloc (sizeof (*bp->planes), bp->count);
215 bp->ncolors = 256; /* ncolors affects color-cycling speed */
216 bp->colors = (XColor *) calloc (bp->ncolors, sizeof(XColor));
217 make_smooth_colormap (0, 0, 0, bp->colors, &bp->ncolors,
221 for (i = 0; i < bp->count; i++)
223 plane *p = &bp->planes[i];
224 p->rot = make_rotator (0, 0,
225 spinp ? spin_speed : 0,
227 wanderp ? wander_speed : 0,
229 p->rot2 = make_rotator (0, 0,
237 glGenTextures (1, &p->texid);
238 glBindTexture (GL_TEXTURE_1D, p->texid);
239 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
240 glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA,
243 /* GL_UNSIGNED_BYTE, */
244 GL_UNSIGNED_INT_8_8_8_8_REV,
246 check_gl_error("texture");
248 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
249 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
251 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
252 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
254 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
259 if (tex_data) free (tex_data);
261 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
266 draw_quasicrystal (ModeInfo *mi)
268 quasicrystal_configuration *bp = &bps[MI_SCREEN(mi)];
269 Display *dpy = MI_DISPLAY(mi);
270 Window window = MI_WINDOW(mi);
271 int wire = MI_IS_WIREFRAME(mi);
275 if (!bp->glx_context)
278 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
280 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
282 mi->polygon_count = 0;
284 glShadeModel(GL_FLAT);
285 glDisable(GL_DEPTH_TEST);
286 glDisable(GL_CULL_FACE);
287 glDisable (GL_LIGHTING);
290 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
293 glTranslatef (0.5, 0.5, 0);
296 if (wire) glScalef (0.2, 0.2, 0.2);
298 for (i = 0; i < bp->count; i++)
300 plane *p = &bp->planes[i];
302 double scale = (wire ? 10 : 700.0 / bp->count);
307 get_position (p->rot, &x, &y, &z, !bp->button_down_p);
308 glTranslatef((x - 0.5) * 0.3333,
312 /* With -symmetry, keep the planes' scales in sync.
313 Otherwise, they scale independently.
315 if (bp->symmetric_p && i > 0)
319 get_position (p->rot2, &x, &y, &z, !bp->button_down_p);
320 pscale = 1 + (4 * z);
327 /* With -symmetry, evenly distribute the planes' rotation.
328 Otherwise, they rotate independently.
330 if (bp->symmetric_p && i > 0)
331 z = r + (i * M_PI * 2 / bp->count);
334 get_rotation (p->rot, &x, &y, &z, !bp->button_down_p);
339 glRotatef (z * 360, 0, 0, 1);
340 glTranslatef (-0.5, -0.5, 0);
342 glColor4f (1, 1, 1, (wire ? 0.5 : 1.0 / bp->count));
346 glEnable (GL_TEXTURE_1D);
347 glBindTexture (GL_TEXTURE_1D, p->texid);
350 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
351 glNormal3f (0, 0, 1);
352 glTexCoord2f (-scale/2, scale/2); glVertex3f (0, 1, 0);
353 glTexCoord2f ( scale/2, scale/2); glVertex3f (1, 1, 0);
354 glTexCoord2f ( scale/2, -scale/2); glVertex3f (1, 0, 0);
355 glTexCoord2f (-scale/2, -scale/2); glVertex3f (0, 0, 0);
361 glDisable (GL_TEXTURE_1D);
362 glColor4f (1, 1, 1, 1.0 / bp->count);
363 for (j = 0; j < 1; j += (1 / scale))
366 glVertex3f (j, 0, 0);
367 glVertex3f (j, 1, 0);
378 /* Colorize the grayscale image. */
381 c[0] = bp->colors[bp->ccolor].red / 65536.0;
382 c[1] = bp->colors[bp->ccolor].green / 65536.0;
383 c[2] = bp->colors[bp->ccolor].blue / 65536.0;
386 /* Brighten the colors. */
387 c[0] = (0.6666 + c[0]/3);
388 c[1] = (0.6666 + c[1]/3);
389 c[2] = (0.6666 + c[2]/3);
391 glBlendFunc (GL_DST_COLOR, GL_SRC_COLOR);
392 glDisable (GL_TEXTURE_1D);
394 glTranslatef (-0.5, -0.5, 0);
396 glVertex3f (0, 1, 0);
397 glVertex3f (1, 1, 0);
398 glVertex3f (1, 0, 0);
399 glVertex3f (0, 0, 0);
404 /* Clip the colors to simulate contrast. */
406 if (bp->contrast > 0)
408 /* If c > 0, map 0 - 100 to 0.5 - 1.0, and use (s & ~d) */
409 GLfloat c = 1 - (bp->contrast / 2 / 100.0);
410 glDisable (GL_TEXTURE_1D);
411 glDisable (GL_BLEND);
412 glEnable (GL_COLOR_LOGIC_OP);
413 glLogicOp (GL_AND_REVERSE);
414 glColor4f (c, c, c, 1);
416 glVertex3f (0, 1, 0);
417 glVertex3f (1, 1, 0);
418 glVertex3f (1, 0, 0);
419 glVertex3f (0, 0, 0);
422 glDisable (GL_COLOR_LOGIC_OP);
427 if (bp->ccolor >= bp->ncolors)
433 if (mi->fps_p) do_fps (mi);
436 glXSwapBuffers(dpy, window);
439 XSCREENSAVER_MODULE ("QuasiCrystal", quasicrystal)