1 /* xscreensaver, Copyright (c) 1997-2008 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 * Written as an Xlib program some time in 1997.
12 * Rewritten as an OpenGL program 24-Aug-2008.
16 Currently, we do this:
18 - start pieces off screen and move them in.
19 - when they land, they fill the puzzle grid with a shuffled
20 puzzle (pieces are rotated, too).
21 - swap random pairs of pieces until the puzzle is solved.
22 - scatter the pieces off screen (resulting in black).
23 - load new image and repeat.
25 Another idea would be to show the puzzle being solved the way
28 - start off with black screen.
29 - assume knowledge of size of grid (position of corners).
30 - find a corner piece, and place it.
31 - while puzzle unsolved:
32 - pick a random piece;
33 - if it is the correct piece for any open edge, place it;
34 - if it fits physically in any rotation at any open edge,
35 place it, then toss it back (show the fake-out).
36 - if it doesn't fit at all, don't animate it at all.
38 This would take a long time to solve, I think...
40 An even harder idea would involve building up completed "clumps"
41 and sliding them around (a coral growth / accretion approach)
44 #define DEF_SPEED "1.0"
45 #define DEF_COMPLEXITY "1.0"
46 #define DEF_RESOLUTION "100"
47 #define DEF_THICKNESS "0.06"
48 #define DEF_WOBBLE "True"
49 #define DEF_DEBUG "False"
51 #define DEFAULTS "*delay: 20000 \n" \
52 "*showFPS: False \n" \
53 "*wireframe: False \n" \
54 "*desktopGrabber: xscreensaver-getimage -no-desktop %s\n" \
55 "*grabDesktopImages: False \n" \
56 "*chooseRandomImages: True \n"
59 # define refresh_jigsaw 0
60 # define release_jigsaw 0
62 #define countof(x) (sizeof((x))/sizeof((*x)))
67 # include <X11/Xlib.h>
74 #endif /* HAVE_JWZGLES */
76 #include "xlockmore.h"
78 #include "gltrackball.h"
81 #include "grab-ximage.h"
84 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
86 #ifdef USE_GL /* whole file */
97 typedef struct jigsaw_configuration jigsaw_configuration;
100 double x,y,z,r; /* position and Z rotation (in degrees) */
105 jigsaw_configuration *jc;
110 XYZR home; /* correct position in puzzle */
111 XYZR current; /* where it is right now */
112 XYZR from, to; /* in transit from A to B */
113 double tick; /* 0-1.0, how far from A to B */
114 double arc_height; /* height of apex of curved path from A to B */
115 double tilt, max_tilt;
120 struct jigsaw_configuration {
121 GLXContext *glx_context;
122 trackball_state *trackball;
128 puzzle_piece *puzzle;
130 enum { PUZZLE_LOADING,
133 PUZZLE_SCATTER } state;
138 GLfloat tex_x, tex_y, tex_width, tex_height, aspect;
140 GLuint line_thickness;
143 static jigsaw_configuration *sps = NULL;
145 static GLfloat speed;
146 static GLfloat complexity_arg;
147 static int resolution_arg;
148 static GLfloat thickness_arg;
149 static Bool wobble_p;
152 static XrmOptionDescRec opts[] = {
153 { "-speed", ".speed", XrmoptionSepArg, 0 },
154 { "-complexity", ".complexity", XrmoptionSepArg, 0 },
155 { "-resolution", ".resolution", XrmoptionSepArg, 0 },
156 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
157 { "-wobble", ".wobble", XrmoptionNoArg, "True" },
158 { "+wobble", ".wobble", XrmoptionNoArg, "False" },
159 { "-debug", ".debug", XrmoptionNoArg, "True" },
162 static argtype vars[] = {
163 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
164 {&complexity_arg, "complexity", "Complexity", DEF_COMPLEXITY, t_Float},
165 {&resolution_arg, "resolution", "Resolution", DEF_RESOLUTION, t_Int},
166 {&thickness_arg, "thickness", "Thickness", DEF_THICKNESS, t_Float},
167 {&wobble_p, "wobble", "Wobble", DEF_WOBBLE, t_Bool},
168 {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
171 ENTRYPOINT ModeSpecOpt jigsaw_opts = {countof(opts), opts, countof(vars), vars, NULL};
174 /* Returns a spline describing one edge of a puzzle piece of the given length.
177 make_puzzle_curve (int pixels)
179 double x0 = 0.0000, y0 = 0.0000;
180 double x1 = 0.3333, y1 = 0.1000;
181 double x2 = 0.4333, y2 = 0.0333;
182 double x3 = 0.4666, y3 = -0.0666;
183 double x4 = 0.3333, y4 = -0.1666;
184 double x5 = 0.3666, y5 = -0.2900;
185 double x6 = 0.5000, y6 = -0.3333;
187 spline *s = make_spline(20);
191 s->control_x[s->n_controls] = pixels * (x); \
192 s->control_y[s->n_controls] = pixels * (y); \
215 tess_error_cb (GLenum errorCode)
217 fprintf (stderr, "%s: tesselation error: %s\n",
218 progname, gluErrorString(errorCode));
224 tess_combine_cb (GLdouble coords[3], GLdouble *d[4], GLfloat w[4],
227 GLdouble *new = (GLdouble *) malloc (3 * sizeof(*new));
236 tess_vertex_cb (void *vertex_data, void *closure)
238 puzzle_piece *p = (puzzle_piece *) closure;
239 GLdouble *v = (GLdouble *) vertex_data;
246 GLfloat pw = p->jc->puzzle_width;
247 GLfloat ph = p->jc->puzzle_height;
249 GLfloat xx = x / (GLfloat) resolution_arg; /* 0-1 from piece origin */
250 GLfloat yy = y / (GLfloat) resolution_arg;
251 GLdouble tx = (p->home.x + xx) / pw; /* 0-1 from puzzle origin */
252 GLdouble ty = (ph - p->home.y - yy) / ph;
254 tx = p->jc->tex_x + (tx * p->jc->tex_width);
255 ty = p->jc->tex_y + (ty * p->jc->tex_height);
257 glTexCoord2d (tx, ty);
260 glVertex3d (x, y, z);
265 /* Draws a puzzle piece. The top/right/bottom/left_type args
266 indicate the direction the tabs point: 1 for out, -1 for in, 0 for flat.
269 draw_piece (jigsaw_configuration *jc, puzzle_piece *p,
270 int resolution, GLfloat thickness,
271 int top_type, int right_type,
272 int bottom_type, int left_type,
275 spline *s = make_puzzle_curve (resolution);
276 GLdouble *pts = (GLdouble *) malloc (s->n_points * 4 * 3 * sizeof(*pts));
279 GLdouble z = resolution * thickness;
287 pts[o++] = resolution;
291 for (i = 0; i < s->n_points; i++) {
292 pts[o++] = s->points[i].x;
293 pts[o++] = s->points[i].y * top_type;
298 if (right_type == 0) {
299 pts[o++] = resolution;
300 pts[o++] = resolution;
303 for (i = 1; i < s->n_points; i++) {
304 pts[o++] = resolution + s->points[i].y * (-right_type);
305 pts[o++] = s->points[i].x;
310 if (bottom_type == 0) {
312 pts[o++] = resolution;
315 for (i = 1; i < s->n_points; i++) {
316 pts[o++] = s->points[s->n_points-i-1].x;
317 pts[o++] = resolution + s->points[s->n_points-i-1].y * (-bottom_type);
322 if (left_type == 0) {
327 for (i = 1; i < s->n_points; i++) {
328 pts[o++] = s->points[s->n_points-i-1].y * left_type;
329 pts[o++] = s->points[s->n_points-i-1].x;
336 { GLfloat s = 1.0 / resolution; glScalef (s, s, s); }
338 glPolygonMode (GL_FRONT_AND_BACK, wire ? GL_LINE : GL_FILL);
342 glDisable (GL_TEXTURE_2D);
343 glDisable (GL_BLEND);
344 glDisable (GL_LIGHTING);
349 # define _GLUfuncptr void(*)(void)
351 GLUtesselator *tess = gluNewTess();
352 gluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)glBegin);
353 gluTessCallback(tess, GLU_TESS_VERTEX_DATA,(_GLUfuncptr)tess_vertex_cb);
354 gluTessCallback(tess, GLU_TESS_END, (_GLUfuncptr)glEnd);
355 gluTessCallback(tess, GLU_TESS_COMBINE, (_GLUfuncptr)tess_combine_cb);
356 gluTessCallback(tess, GLU_TESS_ERROR, (_GLUfuncptr)tess_error_cb);
359 glEnable (GL_TEXTURE_2D);
361 glEnable (GL_LIGHTING);
362 glBindTexture(GL_TEXTURE_2D, jc->texid);
363 glFrontFace (GL_CCW);
364 glNormal3f (0, 0, 1);
365 gluTessBeginPolygon (tess, p);
366 gluTessBeginContour (tess);
367 for (i = 0; i < o; i += 3)
369 GLdouble *p = pts + i;
370 gluTessVertex (tess, p, p);
371 polys++; /* not quite right but close */
373 gluTessEndContour(tess);
374 gluTessEndPolygon(tess);
377 glDisable (GL_TEXTURE_2D);
379 glNormal3f (0, 0, -1);
380 gluTessBeginPolygon (tess, 0);
381 gluTessBeginContour (tess);
382 for (i = 0; i < o; i += 3)
384 GLdouble *p = pts + i;
386 gluTessVertex (tess, p, p);
387 polys++; /* not quite right but close */
389 gluTessEndContour(tess);
390 gluTessEndPolygon(tess);
395 glFrontFace (GL_CCW);
396 glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
397 for (i = 0; i < o; i += 3)
401 GLdouble *p = pts + i;
402 GLdouble *pj = pts + j;
403 GLdouble *pk = pts + k;
405 do_normal (pj[0], pj[1], -pj[2],
407 pk[0], pk[1], pk[2]);
409 glVertex3f (p[0], p[1], -p[2]);
410 glVertex3f (p[0], p[1], p[2]);
416 glColor3f (0.3, 0.3, 0.3);
418 glDisable (GL_TEXTURE_2D);
419 glDisable (GL_LIGHTING);
420 glLineWidth (jc->line_thickness);
422 glBegin (GL_LINE_LOOP);
423 for (i = 0; i < o; i += 3)
424 glVertex3f (pts[i], pts[i+1], pts[i+2]);
428 glBegin (GL_LINE_LOOP);
429 for (i = 0; i < o; i += 3)
430 glVertex3f (pts[i], pts[i+1], -pts[i+2]);
441 free_puzzle_grid (jigsaw_configuration *jc)
444 for (i = 0; i < jc->puzzle_width * jc->puzzle_height; i++)
445 glDeleteLists (jc->puzzle[i].dlist, 1);
448 jc->puzzle_width = 0;
449 jc->puzzle_height = 0;
454 make_puzzle_grid (ModeInfo *mi)
456 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
457 int wire = MI_IS_WIREFRAME(mi);
459 GLfloat size = (8 + (random() % 8)) * complexity_arg;
462 free_puzzle_grid (jc);
465 jc->aspect = MI_WIDTH(mi) / (float) MI_HEIGHT(mi);
467 if (jc->aspect >= 1.0)
469 jc->puzzle_width = size;
470 jc->puzzle_height = (size + 0.5) / jc->aspect;
474 jc->puzzle_width = (size + 0.5) * jc->aspect;
475 jc->puzzle_height = size;
478 if (jc->puzzle_width < 1) jc->puzzle_width = 1;
479 if (jc->puzzle_height < 1) jc->puzzle_height = 1;
482 fprintf (stderr, "%s: grid %4d x %-4d (%.2f)\n", progname,
483 jc->puzzle_width, jc->puzzle_height,
484 ((float) jc->puzzle_width / jc->puzzle_height));
486 jc->puzzle = (puzzle_piece *)
487 calloc (jc->puzzle_width * (jc->puzzle_height+1), sizeof(*jc->puzzle));
489 /* Randomize the right and bottom edge of each piece.
490 Match the left edge of the piece to the right to our right edge.
491 Match the top edge of the piece to the bottom to our bottom edge.
493 for (y = 0; y < jc->puzzle_height; y++)
494 for (x = 0; x < jc->puzzle_width; x++)
496 puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
497 puzzle_piece *r = &jc->puzzle [y * jc->puzzle_width + x+1];
498 puzzle_piece *b = &jc->puzzle [(y+1) * jc->puzzle_width + x];
499 p->edge[RIGHT] = (random() & 1) ? IN : OUT;
500 p->edge[BOTTOM] = (random() & 1) ? IN : OUT;
501 r->edge[LEFT] = p->edge[RIGHT] == IN ? OUT : IN;
502 b->edge[TOP] = p->edge[BOTTOM] == IN ? OUT : IN;
505 /* tell each piece where it belongs. */
506 for (y = 0; y < jc->puzzle_height; y++)
507 for (x = 0; x < jc->puzzle_width; x++)
509 puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
513 p->current = p->home;
515 /* make sure the outer border is flat */
516 if (p->home.x == 0) p->edge[LEFT] = FLAT;
517 if (p->home.y == 0) p->edge[TOP] = FLAT;
518 if (p->home.x == jc->puzzle_width-1) p->edge[RIGHT] = FLAT;
519 if (p->home.y == jc->puzzle_height-1) p->edge[BOTTOM] = FLAT;
521 /* generate the polygons */
522 p->dlist = glGenLists (1);
523 check_gl_error ("generating lists");
524 if (p->dlist <= 0) abort();
526 glNewList (p->dlist, GL_COMPILE);
527 p->polys += draw_piece (jc, p,
528 resolution_arg, thickness_arg,
529 p->edge[TOP], p->edge[RIGHT],
530 p->edge[BOTTOM], p->edge[LEFT],
537 static void shuffle_grid (ModeInfo *mi);
541 image_loaded_cb (const char *filename, XRectangle *geometry,
542 int image_width, int image_height,
543 int texture_width, int texture_height,
546 ModeInfo *mi = (ModeInfo *) closure;
547 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
549 jc->tex_x = geometry->x / (float) texture_width;
550 jc->tex_y = geometry->y / (float) texture_height;
551 jc->tex_width = geometry->width / (float) texture_width;
552 jc->tex_height = geometry->height / (float) texture_height;
553 jc->aspect = geometry->width / (float) geometry->height;
557 fprintf (stderr, "%s: image %s\n", progname,
558 (filename ? filename : "(null)"));
559 fprintf (stderr, "%s: image %4d x %-4d + %4d + %-4d (%.2f)\n", progname,
560 geometry->width, geometry->height, geometry->x, geometry->y,
561 (float) geometry->width / geometry->height);
562 fprintf (stderr, "%s: tex %4d x %-4d\n", progname,
563 texture_width, texture_height);
564 fprintf (stderr, "%s: tex %4.2f x %4.2f + %4.2f + %4.2f (%.2f)\n",
566 jc->tex_width, jc->tex_height, jc->tex_x, jc->tex_y,
567 (jc->tex_width / jc->tex_height) *
568 (texture_width / texture_height));
571 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
572 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
573 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
574 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
576 make_puzzle_grid (mi);
581 load_image (ModeInfo *mi)
583 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
584 load_texture_async (mi->xgwa.screen, mi->window,
585 *jc->glx_context, 0, 0,
587 image_loaded_cb, mi);
591 /* Whether the two pieces are the same shape, when the second piece
592 is rotated by the given degrees.
595 same_shape (puzzle_piece *p0, puzzle_piece *p1, int rotated_by)
600 return (p0->edge[0] == p1->edge[0] &&
601 p0->edge[1] == p1->edge[1] &&
602 p0->edge[2] == p1->edge[2] &&
603 p0->edge[3] == p1->edge[3]);
605 return (p0->edge[0] == p1->edge[1] &&
606 p0->edge[1] == p1->edge[2] &&
607 p0->edge[2] == p1->edge[3] &&
608 p0->edge[3] == p1->edge[0]);
610 return (p0->edge[0] == p1->edge[2] &&
611 p0->edge[1] == p1->edge[3] &&
612 p0->edge[2] == p1->edge[0] &&
613 p0->edge[3] == p1->edge[1]);
615 return (p0->edge[0] == p1->edge[3] &&
616 p0->edge[1] == p1->edge[0] &&
617 p0->edge[2] == p1->edge[1] &&
618 p0->edge[3] == p1->edge[2]);
625 /* Returns the proper rotation for the piece at the given position.
628 proper_rotation (jigsaw_configuration *jc, puzzle_piece *p,
634 if (cx != x) abort(); /* must be in integral position! */
635 if (cy != y) abort();
636 p1 = &jc->puzzle [cy * jc->puzzle_width + cx];
637 if (same_shape (p, p1, 0)) return 0;
638 if (same_shape (p, p1, 90)) return 90;
639 if (same_shape (p, p1, 180)) return 180;
640 if (same_shape (p, p1, 270)) return 270;
641 abort(); /* these two pieces don't match in any rotation! */
645 /* Returns the piece currently at the given position.
647 static puzzle_piece *
648 piece_at (jigsaw_configuration *jc, double x, double y)
650 int npieces = jc->puzzle_width * jc->puzzle_height;
654 if (cx != x) abort(); /* must be in integral position! */
655 if (cy != y) abort();
657 for (i = 0; i < npieces; i++)
659 puzzle_piece *p = &jc->puzzle [i];
660 if (p->current.x == cx &&
664 abort(); /* no piece at that position? */
669 shuffle_grid (ModeInfo *mi)
671 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
672 int max_tries = jc->puzzle_width * jc->puzzle_height;
673 int npieces = jc->puzzle_width * jc->puzzle_height;
676 for (i = 0; i < npieces; i++)
678 puzzle_piece *p0 = &jc->puzzle [i];
679 puzzle_piece *p1 = 0;
682 for (k = 0; k < max_tries; k++)
684 p1 = &jc->puzzle [random() % npieces];
685 if (same_shape (p0, p1, 0)) break;
686 if (same_shape (p0, p1, 90)) break;
687 if (same_shape (p0, p1, 180)) break;
688 if (same_shape (p0, p1, 270)) break;
689 p1 = 0; /* mismatch */
694 s = p0->current; p0->current = p1->current; p1->current = s;
696 proper_rotation (jc, p0, p0->current.x, p0->current.y);
698 proper_rotation (jc, p1, p1->current.x, p1->current.y);
704 /* We tend to accumulate floating point errors, e.g., z being 0.000001
705 after a move. This makes sure float values that should be integral are.
708 smooth_grid (ModeInfo *mi)
710 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
711 int npieces = jc->puzzle_width * jc->puzzle_height;
714 for (i = 0; i < npieces; i++)
716 puzzle_piece *p = &jc->puzzle [i];
718 P.x = (int) (P.x + 0.5); \
719 P.y = (int) (P.y + 0.5); \
720 P.z = (int) (P.z + 0.5); \
721 P.r = (int) (P.r + 0.5)
726 if (p->tick <= 0.0001) p->tick = 0.0;
727 if (p->tick >= 0.9999) p->tick = 1.0;
733 begin_scatter (ModeInfo *mi, Bool unscatter_p)
735 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
736 int npieces = jc->puzzle_width * jc->puzzle_height;
739 ctr.x = jc->puzzle_width / 2;
740 ctr.y = jc->puzzle_height / 2;
742 for (i = 0; i < npieces; i++)
744 puzzle_piece *p = &jc->puzzle [i];
747 p->tick = -frand(1.0);
748 p->from = p->current;
750 a.x = p->from.x - ctr.x; /* position relative to center */
751 a.y = p->from.y - ctr.y;
753 r = sqrt (a.x*a.x + a.y*a.y);
754 th = atan2 (a.x, a.y);
756 d = MAX (jc->puzzle_width, jc->puzzle_height) * 2;
759 p->to.x = ctr.x + (r * sin (th));
760 p->to.y = ctr.y + (r * cos (th));
762 p->to.r = ((int) p->from.r + (random() % 180)) % 360;
763 p->arc_height = frand(10.0);
767 XYZR s = p->to; p->to = p->from; p->from = s;
768 p->current = p->from;
775 solved_p (ModeInfo *mi)
777 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
778 int npieces = jc->puzzle_width * jc->puzzle_height;
781 for (i = 0; i < npieces; i++)
783 puzzle_piece *p = &jc->puzzle [i];
784 if (p->current.x != p->home.x ||
785 p->current.y != p->home.y ||
786 p->current.z != p->home.z)
794 move_one_piece (ModeInfo *mi)
796 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
797 int npieces = jc->puzzle_width * jc->puzzle_height;
800 for (i = 0; i < npieces * 100; i++) /* shouldn't take that long */
802 int i = random() % npieces;
803 puzzle_piece *p0 = &jc->puzzle [i];
806 if (p0->current.x == p0->home.x &&
807 p0->current.y == p0->home.y &&
808 p0->current.z == p0->home.z)
809 continue; /* piece already solved - try again */
811 /* swap with the piece occupying p0's home cell. */
812 p1 = piece_at (jc, p0->home.x, p0->home.y);
814 if (p0 == p1) abort(); /* should have caught this above */
817 p0->from = p0->current;
818 p0->to = p1->current;
819 p0->to.r = proper_rotation (jc, p0, p0->to.x, p0->to.y);
822 p1->from = p1->current;
823 p1->to = p0->current;
824 p1->to.r = proper_rotation (jc, p1, p1->to.x, p1->to.y);
826 p0->arc_height = 0.5 + frand(3.0);
827 p1->arc_height = 1.0 + frand(3.0);
830 V = 90 - BELLRAND(180); \
831 if (! (random() % 5)) V *= 2; \
832 if (! (random() % 5)) V *= 2; \
833 if (! (random() % 5)) V *= 2
834 RTILT (p0->max_tilt);
835 RTILT (p1->max_tilt);
839 fprintf (stderr, "%s: swapping %2d,%-2d with %2d,%d\n", progname,
840 (int) p0->from.x, (int) p0->from.y,
841 (int) p1->from.x, (int) p1->from.y);
845 abort(); /* infinite loop! */
850 anim_tick (ModeInfo *mi)
852 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
853 int npieces = jc->puzzle_width * jc->puzzle_height;
855 Bool finished_p = True;
859 jc->pausing -= jc->tick_speed * speed;
860 if (debug_p && jc->pausing <= 0)
861 fprintf (stderr, "%s: (done pausing)\n", progname);
865 for (i = 0; i < npieces; i++)
867 puzzle_piece *p = &jc->puzzle [i];
870 if (p->tick >= 1.0) continue; /* this piece is done */
871 finished_p = False; /* not done */
873 p->tick += jc->tick_speed * speed;
874 if (p->tick > 1.0) p->tick = 1.0;
876 if (p->tick < 0.0) continue; /* not yet started */
878 tt = 1 - sin (M_PI/2 - p->tick * M_PI/2);
880 p->current.x = p->from.x + ((p->to.x - p->from.x) * tt);
881 p->current.y = p->from.y + ((p->to.y - p->from.y) * tt);
882 p->current.z = p->from.z + ((p->to.z - p->from.z) * tt);
883 p->current.r = p->from.r + ((p->to.r - p->from.r) * tt);
885 p->current.z += p->arc_height * sin (p->tick * M_PI);
887 p->tilt = p->max_tilt * sin (p->tick * M_PI);
896 animate (ModeInfo *mi)
898 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
902 if (jc->button_down_p) return;
907 if (!jc->puzzle) break; /* still loading */
908 jc->tick_speed = slow;
911 begin_scatter (mi, True);
913 jc->state = PUZZLE_UNSCATTER;
914 if (debug_p) fprintf (stderr, "%s: unscattering\n", progname);
917 case PUZZLE_UNSCATTER:
918 jc->tick_speed = slow;
923 jc->state = PUZZLE_SOLVE;
924 if (debug_p) fprintf (stderr, "%s: solving\n", progname);
929 jc->tick_speed = fast;
935 if (debug_p) fprintf (stderr, "%s: solved!\n", progname);
936 begin_scatter (mi, False);
937 jc->state = PUZZLE_SCATTER;
939 if (debug_p) fprintf (stderr, "%s: scattering\n", progname);
950 jc->tick_speed = slow;
953 free_puzzle_grid (jc);
955 jc->state = PUZZLE_LOADING;
957 if (debug_p) fprintf (stderr, "%s: loading\n", progname);
967 /* Window management, etc
970 reshape_jigsaw (ModeInfo *mi, int width, int height)
972 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
973 GLfloat h = (GLfloat) height / (GLfloat) width;
975 glViewport (0, 0, (GLint) width, (GLint) height);
977 glMatrixMode(GL_PROJECTION);
979 gluPerspective (30.0, 1/h, 1.0, 100.0);
981 glMatrixMode(GL_MODELVIEW);
983 gluLookAt( 0.0, 0.0, 30.0,
987 glClear(GL_COLOR_BUFFER_BIT);
989 jc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
994 jigsaw_handle_event (ModeInfo *mi, XEvent *event)
996 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
998 if (event->xany.type == ButtonPress &&
999 event->xbutton.button == Button1)
1001 jc->button_down_p = True;
1002 gltrackball_start (jc->trackball,
1003 event->xbutton.x, event->xbutton.y,
1004 MI_WIDTH (mi), MI_HEIGHT (mi));
1007 else if (event->xany.type == ButtonRelease &&
1008 event->xbutton.button == Button1)
1010 jc->button_down_p = False;
1013 else if (event->xany.type == ButtonPress &&
1014 (event->xbutton.button == Button4 ||
1015 event->xbutton.button == Button5 ||
1016 event->xbutton.button == Button6 ||
1017 event->xbutton.button == Button7))
1019 gltrackball_mousewheel (jc->trackball, event->xbutton.button, 10,
1020 !!event->xbutton.state);
1023 else if (event->xany.type == MotionNotify &&
1026 gltrackball_track (jc->trackball,
1027 event->xmotion.x, event->xmotion.y,
1028 MI_WIDTH (mi), MI_HEIGHT (mi));
1031 else if (event->xany.type == KeyPress)
1035 XLookupString (&event->xkey, &c, 1, &keysym, 0);
1037 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
1039 begin_scatter (mi, False);
1040 jc->state = PUZZLE_SCATTER;
1051 init_jigsaw (ModeInfo *mi)
1053 jigsaw_configuration *jc;
1054 int wire = MI_IS_WIREFRAME(mi);
1057 sps = (jigsaw_configuration *)
1058 calloc (MI_NUM_SCREENS(mi), sizeof (jigsaw_configuration));
1060 fprintf(stderr, "%s: out of memory\n", progname);
1064 jc = &sps[MI_SCREEN(mi)];
1065 jc->glx_context = init_GL(mi);
1067 reshape_jigsaw (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1071 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
1072 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
1073 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
1074 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
1076 glLightfv(GL_LIGHT0, GL_POSITION, pos);
1077 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
1078 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
1079 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
1082 jc->trackball = gltrackball_init ();
1083 jc->rot = make_rotator (0, 0, 0, 0, speed * 0.002, True);
1085 jc->state = PUZZLE_LOADING;
1087 resolution_arg /= complexity_arg;
1090 make_puzzle_grid (mi);
1097 draw_jigsaw (ModeInfo *mi)
1099 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1100 Display *dpy = MI_DISPLAY(mi);
1101 Window window = MI_WINDOW(mi);
1102 int wire = MI_IS_WIREFRAME(mi);
1104 if (!jc->glx_context)
1109 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(jc->glx_context));
1111 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1115 gltrackball_rotate (jc->trackball);
1116 glRotatef(current_device_rotation(), 0, 0, 1);
1118 if (wobble_p && jc->puzzle)
1122 get_position (jc->rot, &x, &y, &z, !jc->button_down_p);
1123 x = 1; /* always lean back */
1124 glRotatef (max/2 - x*max, 1, 0, 0);
1125 glRotatef (max/2 - z*max, 0, 1, 0);
1128 mi->polygon_count = 0;
1130 glEnable(GL_CULL_FACE);
1131 glEnable(GL_DEPTH_TEST);
1132 glEnable(GL_NORMALIZE);
1133 glEnable(GL_LINE_SMOOTH);
1137 GLfloat s = 14.0 / jc->puzzle_height;
1141 glTranslatef (-jc->puzzle_width / 2.0, -jc->puzzle_height / 2.0, 0);
1145 glEnable (GL_TEXTURE_2D);
1146 glEnable (GL_BLEND);
1147 glEnable (GL_LIGHTING);
1148 glEnable (GL_LIGHT0);
1149 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1152 for (y = 0; y < jc->puzzle_height; y++)
1153 for (x = 0; x < jc->puzzle_width; x++)
1155 puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
1157 glTranslatef (p->current.x, p->current.y, p->current.z);
1158 glTranslatef (0.5, 0.5, 0);
1159 glRotatef (p->current.r, 0, 0, 1);
1160 glRotatef (p->tilt, 0, 1, 0);
1161 glTranslatef (-0.5, -0.5, 0);
1162 glCallList(p->dlist);
1163 mi->polygon_count += p->polys;
1170 if (mi->fps_p) do_fps (mi);
1173 glXSwapBuffers(dpy, window);
1176 XSCREENSAVER_MODULE ("Jigsaw", jigsaw)