1 /* unknownpleasures, 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 * Translated from Mathematica code by Archery:
12 * http://intothecontinuum.tumblr.com/post/27443100682/in-july-1967-astronomers-at-the-cavendish
14 * Interestingly, the original image is copyright-free:
15 * http://adamcap.com/2011/05/19/history-of-joy-division-unknown-pleasures-album-art/
16 * http://en.wikipedia.org/wiki/Unknown_Pleasures
20 * - Performance is not great. Spending half our time in compute_line()
21 * and half our time in glEnd(). It's a vast number of cos/pow calls,
22 * and a vast number of polygons. I'm not sure how much could be cached.
24 * - There's too low periodicity vertically on the screen.
26 * - There's too low periodicity in time.
28 * - Could take advantage of time periodicity for caching: just save every
29 * poly for an N second loop. That would be a huge amount of RAM though.
31 * - At low resolutions, GL_POLYGON_OFFSET_FILL seems to work poorly
32 * and the lines get blocky.
36 #define DEFAULTS "*delay: 30000 \n" \
38 "*resolution: 100 \n" \
40 "*showFPS: False \n" \
41 "*wireframe: False \n" \
42 "*geometry: =800x800" "\n" \
44 # define refresh_unk 0
45 # define release_unk 0
47 #define countof(x) (sizeof((x))/sizeof((*x)))
49 #include "xlockmore.h"
51 #include "gltrackball.h"
54 #ifdef USE_GL /* whole file */
56 #define DEF_SPEED "1.0"
60 GLXContext *glx_context;
61 trackball_state *trackball;
69 static unk_configuration *bps = NULL;
73 static XrmOptionDescRec opts[] = {
74 { "-speed", ".speed", XrmoptionSepArg, 0 },
75 { "-resolution", ".resolution", XrmoptionSepArg, 0 },
76 { "-ortho", ".ortho", XrmoptionNoArg, "True" },
77 { "-no-ortho", ".ortho", XrmoptionNoArg, "False" },
80 static argtype vars[] = {
81 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
84 ENTRYPOINT ModeSpecOpt unk_opts = {countof(opts), opts, countof(vars), vars, NULL};
88 /* Window management, etc
91 reshape_unk (ModeInfo *mi, int width, int height)
93 unk_configuration *bp = &bps[MI_SCREEN(mi)];
94 GLfloat h = (GLfloat) height / (GLfloat) width;
96 glViewport (0, 0, (GLint) width, (GLint) height);
100 glMatrixMode(GL_PROJECTION);
102 gluPerspective (1.0, 1/h, 690, 710);
104 glMatrixMode(GL_MODELVIEW);
106 gluLookAt( 0, 0, 700,
110 glScalef (1/h, 1/h, 1);
111 glTranslatef (0, -0.5, 0);
115 glMatrixMode(GL_PROJECTION);
117 gluPerspective (30.0, 1/h, 20.0, 40.0);
119 glMatrixMode(GL_MODELVIEW);
121 gluLookAt( 0.0, 0.0, 30.0,
125 glScalef (1/h, 1/h, 1);
128 glClear(GL_COLOR_BUFFER_BIT);
133 unk_handle_event (ModeInfo *mi, XEvent *event)
135 unk_configuration *bp = &bps[MI_SCREEN(mi)];
137 if (event->xany.type == ButtonPress &&
138 event->xbutton.button == Button1)
140 bp->button_down_p = True;
141 gltrackball_start (bp->trackball,
142 event->xbutton.x, event->xbutton.y,
143 MI_WIDTH (mi), MI_HEIGHT (mi));
146 else if (event->xany.type == ButtonRelease &&
147 event->xbutton.button == Button1)
149 bp->button_down_p = False;
152 else if (event->xany.type == ButtonPress &&
153 (event->xbutton.button == Button4 ||
154 event->xbutton.button == Button5 ||
155 event->xbutton.button == Button6 ||
156 event->xbutton.button == Button7))
158 int b = event->xbutton.button;
160 if (b == Button6 || b == Button7)
163 switch (b) { /* swap axes */
164 case Button4: b = Button6; break;
165 case Button5: b = Button7; break;
166 case Button6: b = Button4; break;
167 case Button7: b = Button5; break;
169 gltrackball_mousewheel (bp->trackball, b, speed, !!event->xbutton.state);
172 else if (event->xany.type == MotionNotify &&
175 gltrackball_track (bp->trackball,
176 event->xmotion.x, event->xmotion.y,
177 MI_WIDTH (mi), MI_HEIGHT (mi));
180 else if (event->xany.type == KeyPress)
184 XLookupString (&event->xkey, &c, 1, &keysym, 0);
187 bp->orthop = !bp->orthop;
188 reshape_unk (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
200 init_unk (ModeInfo *mi)
202 unk_configuration *bp;
205 bps = (unk_configuration *)
206 calloc (MI_NUM_SCREENS(mi), sizeof (unk_configuration));
208 fprintf(stderr, "%s: out of memory\n", progname);
213 bp = &bps[MI_SCREEN(mi)];
215 bp->glx_context = init_GL(mi);
217 bp->orthop = get_boolean_resource (MI_DISPLAY (mi), "ortho", "Ortho");
218 bp->resolution = get_float_resource (MI_DISPLAY (mi),
219 "resolution", "Resolution");
220 if (bp->resolution < 1) bp->resolution = 1;
221 if (bp->resolution > 300) bp->resolution = 300;
223 reshape_unk (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
225 bp->count = MI_COUNT(mi);
226 if (bp->count < 1) bp->count = 1;
228 bp->trackball = gltrackball_init ();
230 if (MI_COUNT(mi) < 1) MI_COUNT(mi) = 1;
232 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
240 /* A simple, fast, deterministic PRNG.
241 ya_rand_init() is too slow for this, and the stream of numbers here
242 doesn't have to be high quality.
248 /* Kluge to let me pick a good seed factor by trial and error... */
249 static int seed0 = 0;
250 static int count = 0;
251 if (count++ > 150000000) seed0 = 0, count=0;
254 seed0 = (random() & 0xFFFFF);
255 fprintf(stderr, "seed0 = %8x %d\n", seed0, seed0);
259 long seed = seed0 * f;
260 seed = 48271 * (seed % 44488) - 3399 * (seed / 44488);
261 f = (double) seed / 0x7FFFFFFF;
268 compute_line (ModeInfo *mi,
269 int xmin, int xmax, GLfloat xinc,
274 unk_configuration *bp = &bps[MI_SCREEN(mi)];
279 /* Compute the points on the line */
281 for (fx = xmin; fx < xmax; fx += xinc)
285 GLfloat hsum = 0, vsum = 0;
287 if (x == lastx) continue;
290 for (n = 1; n <= 30; n++)
292 /* This sum affects crinkliness of the lines */
299 * exp (-pow ((.3 * (x / res_scale) + 30
300 - 1 * 100 * R (2 * n * y)),
305 for (n = 1; n <= 4; n++)
307 /* This sum affects amplitude of the peaks */
308 vsum += (3 * (1 + R (3 * n * y))
309 * fabs (sin (bp->t + R (n * y)
311 * exp (-pow (((x / res_scale) - 1 * 100 * R (n * y)),
316 /* Scale of this affects amplitude of the flat parts */
317 points[x - xmin] = (0.2 * sin (2 * M_PI * R (6 * y) + hsum) + vsum);
324 draw_unk (ModeInfo *mi)
326 unk_configuration *bp = &bps[MI_SCREEN(mi)];
327 Display *dpy = MI_DISPLAY(mi);
328 Window window = MI_WINDOW(mi);
329 int wire = MI_IS_WIREFRAME(mi);
330 GLfloat aspect = 1.5;
332 GLfloat res_scale = 4;
333 int xmin = -50 * res_scale;
334 int xmax = 150 * res_scale;
335 GLfloat xinc = 100.0 / (bp->resolution / res_scale);
337 int ytop = MI_COUNT(mi);
342 if (xinc < 0.25) xinc = 0.25;
344 if (!bp->glx_context)
347 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
349 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
351 mi->polygon_count = 0;
353 glShadeModel (GL_FLAT);
354 glEnable (GL_DEPTH_TEST);
355 glDisable (GL_CULL_FACE);
359 glRotatef(current_device_rotation(), 0, 0, 1);
361 gltrackball_rotate (bp->trackball);
362 glScalef (10, 10, 10);
364 glRotatef (-45, 1, 0, 0);
365 glTranslatef (-0.5, -0.5, 0);
367 glTranslatef (0, 0.05, 0);
369 glTranslatef (0, 0.15, 0);
371 points = (GLfloat *) malloc (sizeof(*points) * (xmax - xmin));
373 if (!bp->button_down_p)
375 double s = 6.3 / 19 / 3;
387 /* Lower the resolution to get a decent frame rate on iPhone 4s */
388 if (mi->xgwa.width <= 640 || mi->xgwa.height <= 640)
395 /* Lower it even further for iPhone 3 */
396 if (mi->xgwa.width <= 480 || mi->xgwa.height <= 480)
402 /* Performance just sucks on iPad 3, even with a very high xinc. WTF? */
404 if (mi->xgwa.width >= 2048 || mi->xgwa.height >= 2048)
408 # endif /* USE_IPHONE */
411 /* Make the image fill the screen a little more fully */
412 if (mi->xgwa.width <= 640 || mi->xgwa.height <= 640)
414 glScalef (1.2, 1.2, 1.2);
415 glTranslatef (-0.08, 0, 0);
418 if (mi->xgwa.width <= 480 || mi->xgwa.height <= 480)
428 glDisable (GL_POLYGON_OFFSET_FILL);
431 glTranslatef (0, (1-aspect)/2, -0.005);
432 glScalef (1, aspect, 1);
433 glTranslatef (0.5, 0.5, 0);
436 glVertex3f (-0.5, -0.5, 0);
437 glVertex3f ( 0.5, -0.5, 0);
438 glVertex3f ( 0.5, 0.5, 0);
439 glVertex3f (-0.5, 0.5, 0);
446 glEnable (GL_LINE_SMOOTH);
447 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
448 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
451 /* So the masking quads don't interfere with the lines */
452 glEnable (GL_POLYGON_OFFSET_FILL);
453 glPolygonOffset (1.0, 1.0);
456 for (y = ymin; y <= ytop; y += yinc)
458 /* Compute all the verts on the line */
459 compute_line (mi, xmin, xmax, xinc, res_scale, y, points);
461 /* Draw the line segments; then draw the black shielding quads. */
463 GLfloat yy = y / (GLfloat) ytop;
466 yy = (yy * aspect) - ((aspect - 1) / 2);
468 for (linesp = 0; linesp <= 1; linesp++)
473 GLfloat c = (linesp || wire ? 1 : 0);
478 : wire ? GL_LINES : GL_QUAD_STRIP);
480 for (fx = xmin; fx < xmax; fx += xinc)
483 GLfloat xx = (x - xmin) / (GLfloat) (xmax - xmin);
484 GLfloat zz = points [x - xmin];
486 if (x == lastx) continue;
490 glVertex3f (xx, yy, zz);
492 glVertex3f (xx, yy, 0);
504 if (mi->fps_p) do_fps (mi);
507 glXSwapBuffers(dpy, window);
510 XSCREENSAVER_MODULE_2 ("UnknownPleasures", unknownpleasures, unk)