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 refresh_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 { "-no-symmetry", ".symmetric", XrmoptionNoArg, "False" },
70 { "-speed", ".speed", XrmoptionSepArg, 0 },
71 { "-contrast", ".contrast", XrmoptionSepArg, 0 },
74 static argtype vars[] = {
75 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
78 ENTRYPOINT ModeSpecOpt quasicrystal_opts = {countof(opts), opts, countof(vars), vars, NULL};
82 /* Window management, etc
85 reshape_quasicrystal (ModeInfo *mi, int width, int height)
87 GLfloat h = (GLfloat) height / (GLfloat) width;
89 glViewport (0, 0, (GLint) width, (GLint) height);
91 glMatrixMode(GL_PROJECTION);
93 glOrtho (0, 1, 1, 0, -1, 1);
95 glMatrixMode(GL_MODELVIEW);
97 glTranslatef (0.5, 0.5, 0);
100 glScalef (1/h, 1/h, 1);
101 glTranslatef (-0.5, -0.5, 0);
102 glClear(GL_COLOR_BUFFER_BIT);
107 quasicrystal_handle_event (ModeInfo *mi, XEvent *event)
109 quasicrystal_configuration *bp = &bps[MI_SCREEN(mi)];
111 if (event->xany.type == ButtonPress &&
112 event->xbutton.button == Button1)
114 bp->button_down_p = True;
115 bp->mousex = event->xbutton.x;
116 bp->mousey = event->xbutton.y;
119 else if (event->xany.type == ButtonRelease &&
120 event->xbutton.button == Button1)
122 bp->button_down_p = False;
125 else if (event->xany.type == ButtonPress && /* wheel up or right */
126 (event->xbutton.button == Button4 ||
127 event->xbutton.button == Button7))
130 if (bp->contrast <= 0) return False;
134 else if (event->xany.type == ButtonPress && /* wheel down or left */
135 (event->xbutton.button == Button5 ||
136 event->xbutton.button == Button6))
139 if (bp->contrast >= 100) return False;
143 else if (event->xany.type == MotionNotify &&
146 /* Dragging up and down tweaks contrast */
148 int dx = event->xmotion.x - bp->mousex;
149 int dy = event->xmotion.y - bp->mousey;
151 if (abs(dy) > abs(dx))
153 bp->contrast += dy / 40.0;
154 if (bp->contrast < 0) bp->contrast = 0;
155 if (bp->contrast > 100) bp->contrast = 100;
158 bp->mousex = event->xmotion.x;
159 bp->mousey = event->xmotion.y;
164 if (event->xany.type == KeyPress)
168 XLookupString (&event->xkey, &c, 1, &keysym, 0);
169 if (c == '<' || c == ',' || c == '-' || c == '_' ||
170 keysym == XK_Left || keysym == XK_Up || keysym == XK_Prior)
172 else if (c == '>' || c == '.' || c == '=' || c == '+' ||
173 keysym == XK_Right || keysym == XK_Down ||
178 return screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event);
187 init_quasicrystal (ModeInfo *mi)
189 quasicrystal_configuration *bp;
190 int wire = MI_IS_WIREFRAME(mi);
191 unsigned char *tex_data = 0;
196 bps = (quasicrystal_configuration *)
197 calloc (MI_NUM_SCREENS(mi), sizeof (quasicrystal_configuration));
199 fprintf(stderr, "%s: out of memory\n", progname);
204 bp = &bps[MI_SCREEN(mi)];
206 bp->glx_context = init_GL(mi);
208 reshape_quasicrystal (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
210 glDisable (GL_DEPTH_TEST);
211 glEnable (GL_CULL_FACE);
213 bp->count = MI_COUNT(mi);
214 if (bp->count < 1) bp->count = 1;
221 glGetIntegerv (GL_MAX_TEXTURE_SIZE, &tex_width);
222 if (tex_width > 4096) tex_width = 4096;
224 tex_data = (unsigned char *) calloc (4, tex_width);
226 for (i = 0; i < tex_width; i++)
228 unsigned char y = 255 * (1 + sin (i * M_PI * 2 / tex_width)) / 2;
237 get_boolean_resource (MI_DISPLAY (mi), "symmetry", "Symmetry");
239 bp->contrast = get_float_resource (MI_DISPLAY (mi), "contrast", "Contrast");
240 if (bp->contrast < 0 || bp->contrast > 100)
242 fprintf (stderr, "%s: contrast must be between 0 and 100%%.\n", progname);
247 Bool spinp = get_boolean_resource (MI_DISPLAY (mi), "spin", "Spin");
248 Bool wanderp = get_boolean_resource (MI_DISPLAY (mi), "wander", "Wander");
249 double spin_speed = 0.01;
250 double wander_speed = 0.0001;
251 double spin_accel = 10.0;
252 double scale_speed = 0.005;
254 bp->planes = (plane *) calloc (sizeof (*bp->planes), bp->count);
256 bp->ncolors = 256; /* ncolors affects color-cycling speed */
257 bp->colors = (XColor *) calloc (bp->ncolors, sizeof(XColor));
258 make_smooth_colormap (0, 0, 0, bp->colors, &bp->ncolors,
262 for (i = 0; i < bp->count; i++)
264 plane *p = &bp->planes[i];
265 p->rot = make_rotator (0, 0,
266 spinp ? spin_speed : 0,
268 wanderp ? wander_speed : 0,
270 p->rot2 = make_rotator (0, 0,
278 glGenTextures (1, &p->texid);
279 glBindTexture (GL_TEXTURE_1D, p->texid);
280 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
281 glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA,
284 /* GL_UNSIGNED_BYTE, */
285 GL_UNSIGNED_INT_8_8_8_8_REV,
287 check_gl_error("texture");
289 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
290 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
292 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
293 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
295 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
300 if (tex_data) free (tex_data);
302 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
307 draw_quasicrystal (ModeInfo *mi)
309 quasicrystal_configuration *bp = &bps[MI_SCREEN(mi)];
310 Display *dpy = MI_DISPLAY(mi);
311 Window window = MI_WINDOW(mi);
312 int wire = MI_IS_WIREFRAME(mi);
316 if (!bp->glx_context)
319 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
321 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
323 mi->polygon_count = 0;
325 glShadeModel(GL_FLAT);
326 glDisable(GL_DEPTH_TEST);
327 glDisable(GL_CULL_FACE);
328 glDisable (GL_LIGHTING);
331 glEnable (GL_TEXTURE_1D);
333 glEnable (GL_TEXTURE_2D); /* jwzgles needs this, bleh. */
338 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
341 glTranslatef (0.5, 0.5, 0);
344 if (wire) glScalef (0.2, 0.2, 0.2);
346 for (i = 0; i < bp->count; i++)
348 plane *p = &bp->planes[i];
350 double scale = (wire ? 10 : 700.0 / bp->count);
355 get_position (p->rot, &x, &y, &z, !bp->button_down_p);
356 glTranslatef((x - 0.5) * 0.3333,
360 /* With -symmetry, keep the planes' scales in sync.
361 Otherwise, they scale independently.
363 if (bp->symmetric_p && i > 0)
367 get_position (p->rot2, &x, &y, &z, !bp->button_down_p);
368 pscale = 1 + (4 * z);
375 /* With -symmetry, evenly distribute the planes' rotation.
376 Otherwise, they rotate independently.
378 if (bp->symmetric_p && i > 0)
379 z = r + (i * M_PI * 2 / bp->count);
382 get_rotation (p->rot, &x, &y, &z, !bp->button_down_p);
387 glRotatef (z * 360, 0, 0, 1);
388 glTranslatef (-0.5, -0.5, 0);
390 glColor4f (1, 1, 1, (wire ? 0.5 : 1.0 / bp->count));
393 glBindTexture (GL_TEXTURE_1D, p->texid);
395 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
396 glNormal3f (0, 0, 1);
397 glTexCoord2f (-scale/2, scale/2); glVertex3f (0, 1, 0);
398 glTexCoord2f ( scale/2, scale/2); glVertex3f (1, 1, 0);
399 glTexCoord2f ( scale/2, -scale/2); glVertex3f (1, 0, 0);
400 glTexCoord2f (-scale/2, -scale/2); glVertex3f (0, 0, 0);
406 glDisable (GL_TEXTURE_1D);
407 glColor4f (1, 1, 1, 1.0 / bp->count);
408 for (j = 0; j < 1; j += (1 / scale))
411 glVertex3f (j, 0, 0);
412 glVertex3f (j, 1, 0);
423 /* Colorize the grayscale image. */
426 c[0] = bp->colors[bp->ccolor].red / 65536.0;
427 c[1] = bp->colors[bp->ccolor].green / 65536.0;
428 c[2] = bp->colors[bp->ccolor].blue / 65536.0;
431 /* Brighten the colors. */
432 c[0] = (0.6666 + c[0]/3);
433 c[1] = (0.6666 + c[1]/3);
434 c[2] = (0.6666 + c[2]/3);
436 glBlendFunc (GL_DST_COLOR, GL_SRC_COLOR);
437 glDisable (GL_TEXTURE_1D);
439 glTranslatef (-0.5, -0.5, 0);
441 glVertex3f (0, 1, 0);
442 glVertex3f (1, 1, 0);
443 glVertex3f (1, 0, 0);
444 glVertex3f (0, 0, 0);
449 /* Clip the colors to simulate contrast. */
451 if (bp->contrast > 0)
453 /* If c > 0, map 0 - 100 to 0.5 - 1.0, and use (s & ~d) */
454 GLfloat c = 1 - (bp->contrast / 2 / 100.0);
455 glDisable (GL_TEXTURE_1D);
456 glDisable (GL_BLEND);
457 glEnable (GL_COLOR_LOGIC_OP);
458 glLogicOp (GL_AND_REVERSE);
459 glColor4f (c, c, c, 1);
461 glVertex3f (0, 1, 0);
462 glVertex3f (1, 1, 0);
463 glVertex3f (1, 0, 0);
464 glVertex3f (0, 0, 0);
467 glDisable (GL_COLOR_LOGIC_OP);
472 if (bp->ccolor >= bp->ncolors)
478 if (mi->fps_p) do_fps (mi);
481 glXSwapBuffers(dpy, window);
484 XSCREENSAVER_MODULE ("QuasiCrystal", quasicrystal)