1 /* xscreensaver, Copyright (c) 1997-2012 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 DEF_FONT "-*-helvetica-bold-r-normal-*-240-*"
52 #define DEFAULTS "*delay: 20000 \n" \
53 "*showFPS: False \n" \
54 "*font: " DEF_FONT"\n" \
55 "*wireframe: False \n" \
56 "*desktopGrabber: xscreensaver-getimage -no-desktop %s\n" \
57 "*grabDesktopImages: False \n" \
58 "*chooseRandomImages: True \n"
61 # define refresh_jigsaw 0
62 # define release_jigsaw 0
64 #define countof(x) (sizeof((x))/sizeof((*x)))
69 # include <X11/Xlib.h>
74 #include "xlockmore.h"
76 #include "gltrackball.h"
79 #include "grab-ximage.h"
84 #else /* !HAVE_JWZGLES */
86 #endif /* !HAVE_JWZGLES */
89 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
91 #ifdef USE_GL /* whole file */
102 typedef struct jigsaw_configuration jigsaw_configuration;
105 double x,y,z,r; /* position and Z rotation (in degrees) */
110 jigsaw_configuration *jc;
115 XYZR home; /* correct position in puzzle */
116 XYZR current; /* where it is right now */
117 XYZR from, to; /* in transit from A to B */
118 double tick; /* 0-1.0, how far from A to B */
119 double arc_height; /* height of apex of curved path from A to B */
120 double tilt, max_tilt;
125 struct jigsaw_configuration {
126 GLXContext *glx_context;
127 trackball_state *trackball;
130 texture_font_data *texfont;
131 GLuint loading_dlist;
135 puzzle_piece *puzzle;
137 enum { PUZZLE_LOADING_MSG,
141 PUZZLE_SCATTER } state;
146 GLfloat tex_x, tex_y, tex_width, tex_height, aspect;
148 GLuint line_thickness;
151 static jigsaw_configuration *sps = NULL;
153 static GLfloat speed;
154 static GLfloat complexity_arg;
155 static int resolution_arg;
156 static GLfloat thickness_arg;
157 static Bool wobble_p;
160 static XrmOptionDescRec opts[] = {
161 { "-speed", ".speed", XrmoptionSepArg, 0 },
162 { "-complexity", ".complexity", XrmoptionSepArg, 0 },
163 { "-resolution", ".resolution", XrmoptionSepArg, 0 },
164 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
165 { "-wobble", ".wobble", XrmoptionNoArg, "True" },
166 { "+wobble", ".wobble", XrmoptionNoArg, "False" },
167 { "-debug", ".debug", XrmoptionNoArg, "True" },
170 static argtype vars[] = {
171 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
172 {&complexity_arg, "complexity", "Complexity", DEF_COMPLEXITY, t_Float},
173 {&resolution_arg, "resolution", "Resolution", DEF_RESOLUTION, t_Int},
174 {&thickness_arg, "thickness", "Thickness", DEF_THICKNESS, t_Float},
175 {&wobble_p, "wobble", "Wobble", DEF_WOBBLE, t_Bool},
176 {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
179 ENTRYPOINT ModeSpecOpt jigsaw_opts = {countof(opts), opts, countof(vars), vars, NULL};
182 /* Returns a spline describing one edge of a puzzle piece of the given length.
185 make_puzzle_curve (int pixels)
187 double x0 = 0.0000, y0 = 0.0000;
188 double x1 = 0.3333, y1 = 0.1000;
189 double x2 = 0.4333, y2 = 0.0333;
190 double x3 = 0.4666, y3 = -0.0666;
191 double x4 = 0.3333, y4 = -0.1666;
192 double x5 = 0.3666, y5 = -0.2900;
193 double x6 = 0.5000, y6 = -0.3333;
195 spline *s = make_spline(20);
199 s->control_x[s->n_controls] = pixels * (x); \
200 s->control_y[s->n_controls] = pixels * (y); \
225 tess_error_cb (GLenum errorCode)
227 fprintf (stderr, "%s: tesselation error: %s\n",
228 progname, gluErrorString(errorCode));
234 tess_combine_cb (GLdouble coords[3], GLdouble *d[4], GLfloat w[4],
237 GLdouble *new = (GLdouble *) malloc (3 * sizeof(*new));
246 tess_vertex_cb (void *vertex_data, void *closure)
248 puzzle_piece *p = (puzzle_piece *) closure;
249 GLdouble *v = (GLdouble *) vertex_data;
256 GLfloat pw = p->jc->puzzle_width;
257 GLfloat ph = p->jc->puzzle_height;
259 GLfloat xx = x / (GLfloat) resolution_arg; /* 0-1 from piece origin */
260 GLfloat yy = y / (GLfloat) resolution_arg;
261 GLdouble tx = (p->home.x + xx) / pw; /* 0-1 from puzzle origin */
262 GLdouble ty = (ph - p->home.y - yy) / ph;
264 tx = p->jc->tex_x + (tx * p->jc->tex_width);
265 ty = p->jc->tex_y + (ty * p->jc->tex_height);
267 glTexCoord2d (tx, ty);
270 glVertex3d (x, y, z);
273 #else /* HAVE_TESS */
275 /* Writes triangles into the array of floats.
276 Returns the number of floats written (triangles * 9).
279 make_piece_eighth (jigsaw_configuration *jc, const spline *s,
280 int resolution, int type, GLfloat *out,
281 Bool flip_x, Bool flip_y, Bool rotate_p)
284 int cx = resolution/2;
285 int cy = resolution/2;
286 int np = (s->n_points / 2) + 1;
287 int last_x = -999999, last_y = -999999;
288 Bool inflected = False;
308 for (i = (type == IN ? np-1 : 0);
309 (type == IN ? i >= 0 : i < np);
310 i += (type == IN ? -1 : 1))
312 int x = s->points[i].x;
313 int y = s->points[i].y;
318 if (last_x != -999999)
369 int count = out - oout;
373 for (i = 0; i < count; i += 3)
374 oout[i] = resolution - oout[i];
377 for (i = 0; i < count; i += 3)
378 oout[i+1] = resolution - oout[i+1];
381 if (flip_x) cw_p = !cw_p;
382 if (flip_y) cw_p = !cw_p;
385 for (i = 0; i < count; i += 9)
387 GLfloat x1 = oout[i+0];
388 GLfloat y1 = oout[i+1];
389 GLfloat x2 = oout[i+3];
390 GLfloat y2 = oout[i+4];
391 GLfloat x3 = oout[i+6];
392 GLfloat y3 = oout[i+7];
402 for (i = 0; i < count; i += 3)
405 GLfloat y = oout[i+1];
406 oout[i] = resolution - y;
414 #endif /* !HAVE_TESS */
418 /* Draws a puzzle piece. The top/right/bottom/left_type args
419 indicate the direction the tabs point: 1 for out, -1 for in, 0 for flat.
422 draw_piece (jigsaw_configuration *jc, puzzle_piece *p,
423 int resolution, GLfloat thickness,
424 int top_type, int right_type,
425 int bottom_type, int left_type,
428 spline *s = make_puzzle_curve (resolution);
429 GLdouble *pts = (GLdouble *) malloc (s->n_points * 4 * 3 * sizeof(*pts));
432 GLdouble z = resolution * thickness;
440 pts[o++] = resolution;
444 for (i = 0; i < s->n_points; i++) {
445 pts[o++] = s->points[i].x;
446 pts[o++] = s->points[i].y * top_type;
451 if (right_type == 0) {
452 pts[o++] = resolution;
453 pts[o++] = resolution;
456 for (i = 1; i < s->n_points; i++) {
457 pts[o++] = resolution + s->points[i].y * (-right_type);
458 pts[o++] = s->points[i].x;
463 if (bottom_type == 0) {
465 pts[o++] = resolution;
468 for (i = 1; i < s->n_points; i++) {
469 pts[o++] = s->points[s->n_points-i-1].x;
470 pts[o++] = resolution + s->points[s->n_points-i-1].y * (-bottom_type);
475 if (left_type == 0) {
480 for (i = 1; i < s->n_points; i++) {
481 pts[o++] = s->points[s->n_points-i-1].y * left_type;
482 pts[o++] = s->points[s->n_points-i-1].x;
487 { GLfloat ss = 1.0 / resolution; glScalef (ss, ss, ss); }
489 # ifndef HAVE_JWZGLES
490 glPolygonMode (GL_FRONT_AND_BACK, wire ? GL_LINE : GL_FILL);
495 glDisable (GL_TEXTURE_2D);
496 glDisable (GL_BLEND);
497 glDisable (GL_LIGHTING);
504 # define _GLUfuncptr void(*)(void)
506 GLUtesselator *tess = gluNewTess();
507 gluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)glBegin);
508 gluTessCallback(tess, GLU_TESS_VERTEX_DATA,(_GLUfuncptr)tess_vertex_cb);
509 gluTessCallback(tess, GLU_TESS_END, (_GLUfuncptr)glEnd);
510 gluTessCallback(tess, GLU_TESS_COMBINE, (_GLUfuncptr)tess_combine_cb);
511 gluTessCallback(tess, GLU_TESS_ERROR, (_GLUfuncptr)tess_error_cb);
514 glEnable (GL_TEXTURE_2D);
516 glEnable (GL_LIGHTING);
517 glBindTexture(GL_TEXTURE_2D, jc->texid);
518 glFrontFace (GL_CCW);
519 glNormal3f (0, 0, 1);
520 gluTessBeginPolygon (tess, p);
521 gluTessBeginContour (tess);
522 for (i = 0; i < o; i += 3)
524 GLdouble *p = pts + i;
525 gluTessVertex (tess, p, p);
526 polys++; /* not quite right but close */
528 gluTessEndContour(tess);
529 gluTessEndPolygon(tess);
532 glDisable (GL_TEXTURE_2D);
534 glNormal3f (0, 0, -1);
535 gluTessBeginPolygon (tess, 0);
536 gluTessBeginContour (tess);
537 for (i = 0; i < o; i += 3)
539 GLdouble *p = pts + i;
541 gluTessVertex (tess, p, p);
542 polys++; /* not quite right but close */
544 gluTessEndContour(tess);
545 gluTessEndPolygon(tess);
549 for (i = 0; i < o; i += 3)
551 GLdouble *p = pts + i;
555 # else /* !HAVE_TESS */
557 GLfloat *tri = (GLfloat *)
558 (GLfloat *) malloc (s->n_points * 4 * 3 * 3 * sizeof(*pts));
563 tri += make_piece_eighth (jc, s, resolution, top_type, tri, 0, 0, 0);
564 tri += make_piece_eighth (jc, s, resolution, top_type, tri, 1, 0, 0);
565 tri += make_piece_eighth (jc, s, resolution, left_type, tri, 0, 1, 1);
566 tri += make_piece_eighth (jc, s, resolution, left_type, tri, 1, 1, 1);
567 tri += make_piece_eighth (jc, s, resolution, bottom_type, tri, 0, 1, 0);
568 tri += make_piece_eighth (jc, s, resolution, bottom_type, tri, 1, 1, 0);
569 tri += make_piece_eighth (jc, s, resolution, right_type, tri, 0, 0, 1);
570 tri += make_piece_eighth (jc, s, resolution, right_type, tri, 1, 0, 1);
571 count = (tri - otri) / 9;
575 glEnable (GL_TEXTURE_2D);
577 glEnable (GL_LIGHTING);
578 glBindTexture(GL_TEXTURE_2D, jc->texid);
581 for (zz = z; zz >= -z; zz -= 2*z)
584 glFrontFace (zz > 0 ? GL_CCW : GL_CW);
585 glNormal3f (0, 0, (zz > 0 ? 1 : -1));
588 glDisable (GL_TEXTURE_2D); /* back face */
591 glTranslatef (0, 0, zz);
596 for (i = 0; i < count; i++)
598 glBegin (GL_LINE_LOOP);
599 glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
600 glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
601 glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
607 GLfloat pw = p->jc->puzzle_width;
608 GLfloat ph = p->jc->puzzle_height;
609 GLfloat r = resolution;
611 glBegin (GL_TRIANGLES);
612 for (i = 0; i < count * 3; i++)
618 /* 0-1 from piece origin */
622 /* 0-1 from puzzle origin */
623 GLfloat tx = (p->home.x + xx) / pw;
624 GLfloat ty = (ph - p->home.y - yy) / ph;
626 tx = p->jc->tex_x + (tx * p->jc->tex_width);
627 ty = p->jc->tex_y + (ty * p->jc->tex_height);
629 glTexCoord2f (tx, ty);
630 glVertex3f (x, y, z);
640 # endif /* !HAVE_TESS */
645 glFrontFace (GL_CCW);
646 glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
647 for (i = 0; i < o; i += 3)
651 GLdouble *p = pts + i;
652 GLdouble *pj = pts + j;
653 GLdouble *pk = pts + k;
655 do_normal (pj[0], pj[1], pj[2],
656 pj[0], pj[1], -pj[2],
657 pk[0], pk[1], pk[2]);
659 glVertex3f (p[0], p[1], p[2]);
660 glVertex3f (p[0], p[1], -p[2]);
666 glColor3f (0.3, 0.3, 0.3);
668 /* outline the edges in gray */
670 glDisable (GL_TEXTURE_2D);
671 glDisable (GL_LIGHTING);
672 glLineWidth (jc->line_thickness);
674 glBegin (GL_LINE_LOOP);
675 for (i = 0; i < o; i += 3)
676 glVertex3f (pts[i], pts[i+1], pts[i+2]);
680 glBegin (GL_LINE_LOOP);
681 for (i = 0; i < o; i += 3)
682 glVertex3f (pts[i], pts[i+1], -pts[i+2]);
694 free_puzzle_grid (jigsaw_configuration *jc)
697 for (i = 0; i < jc->puzzle_width * jc->puzzle_height; i++)
698 glDeleteLists (jc->puzzle[i].dlist, 1);
701 jc->puzzle_width = 0;
702 jc->puzzle_height = 0;
707 make_puzzle_grid (ModeInfo *mi)
709 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
710 int wire = MI_IS_WIREFRAME(mi);
712 GLfloat size = (8 + (random() % 8)) * complexity_arg;
715 free_puzzle_grid (jc);
718 jc->aspect = MI_WIDTH(mi) / (float) MI_HEIGHT(mi);
720 if (jc->aspect >= 1.0)
722 jc->puzzle_width = size;
723 jc->puzzle_height = (size + 0.5) / jc->aspect;
727 jc->puzzle_width = (size + 0.5) * jc->aspect;
728 jc->puzzle_height = size;
731 if (jc->puzzle_width < 1) jc->puzzle_width = 1;
732 if (jc->puzzle_height < 1) jc->puzzle_height = 1;
735 fprintf (stderr, "%s: grid %4d x %-4d (%.2f)\n", progname,
736 jc->puzzle_width, jc->puzzle_height,
737 ((float) jc->puzzle_width / jc->puzzle_height));
739 jc->puzzle = (puzzle_piece *)
740 calloc (jc->puzzle_width * (jc->puzzle_height+1), sizeof(*jc->puzzle));
742 /* Randomize the right and bottom edge of each piece.
743 Match the left edge of the piece to the right to our right edge.
744 Match the top edge of the piece to the bottom to our bottom edge.
746 for (y = 0; y < jc->puzzle_height; y++)
747 for (x = 0; x < jc->puzzle_width; x++)
749 puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
750 puzzle_piece *r = &jc->puzzle [y * jc->puzzle_width + x+1];
751 puzzle_piece *b = &jc->puzzle [(y+1) * jc->puzzle_width + x];
752 p->edge[RIGHT] = (random() & 1) ? IN : OUT;
753 p->edge[BOTTOM] = (random() & 1) ? IN : OUT;
754 r->edge[LEFT] = p->edge[RIGHT] == IN ? OUT : IN;
755 b->edge[TOP] = p->edge[BOTTOM] == IN ? OUT : IN;
758 /* tell each piece where it belongs. */
759 for (y = 0; y < jc->puzzle_height; y++)
760 for (x = 0; x < jc->puzzle_width; x++)
762 puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
766 p->current = p->home;
768 /* make sure the outer border is flat */
769 if (p->home.x == 0) p->edge[LEFT] = FLAT;
770 if (p->home.y == 0) p->edge[TOP] = FLAT;
771 if (p->home.x == jc->puzzle_width-1) p->edge[RIGHT] = FLAT;
772 if (p->home.y == jc->puzzle_height-1) p->edge[BOTTOM] = FLAT;
774 /* generate the polygons */
775 p->dlist = glGenLists (1);
776 check_gl_error ("generating lists");
777 if (p->dlist <= 0) abort();
779 glNewList (p->dlist, GL_COMPILE);
780 p->polys += draw_piece (jc, p,
781 resolution_arg, thickness_arg,
782 p->edge[TOP], p->edge[RIGHT],
783 p->edge[BOTTOM], p->edge[LEFT],
790 static void shuffle_grid (ModeInfo *mi);
794 image_loaded_cb (const char *filename, XRectangle *geometry,
795 int image_width, int image_height,
796 int texture_width, int texture_height,
799 ModeInfo *mi = (ModeInfo *) closure;
800 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
802 jc->tex_x = geometry->x / (float) texture_width;
803 jc->tex_y = geometry->y / (float) texture_height;
804 jc->tex_width = geometry->width / (float) texture_width;
805 jc->tex_height = geometry->height / (float) texture_height;
806 jc->aspect = geometry->width / (float) geometry->height;
810 fprintf (stderr, "%s: image %s\n", progname,
811 (filename ? filename : "(null)"));
812 fprintf (stderr, "%s: image %4d x %-4d + %4d + %-4d (%.2f)\n", progname,
813 geometry->width, geometry->height, geometry->x, geometry->y,
814 (float) geometry->width / geometry->height);
815 fprintf (stderr, "%s: tex %4d x %-4d\n", progname,
816 texture_width, texture_height);
817 fprintf (stderr, "%s: tex %4.2f x %4.2f + %4.2f + %4.2f (%.2f)\n",
819 jc->tex_width, jc->tex_height, jc->tex_x, jc->tex_y,
820 (jc->tex_width / jc->tex_height) *
821 (texture_width / texture_height));
824 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
825 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
826 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
827 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
829 make_puzzle_grid (mi);
834 load_image (ModeInfo *mi)
836 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
837 load_texture_async (mi->xgwa.screen, mi->window,
838 *jc->glx_context, 0, 0,
840 image_loaded_cb, mi);
844 /* Whether the two pieces are the same shape, when the second piece
845 is rotated by the given degrees.
848 same_shape (puzzle_piece *p0, puzzle_piece *p1, int rotated_by)
853 return (p0->edge[0] == p1->edge[0] &&
854 p0->edge[1] == p1->edge[1] &&
855 p0->edge[2] == p1->edge[2] &&
856 p0->edge[3] == p1->edge[3]);
858 return (p0->edge[0] == p1->edge[1] &&
859 p0->edge[1] == p1->edge[2] &&
860 p0->edge[2] == p1->edge[3] &&
861 p0->edge[3] == p1->edge[0]);
863 return (p0->edge[0] == p1->edge[2] &&
864 p0->edge[1] == p1->edge[3] &&
865 p0->edge[2] == p1->edge[0] &&
866 p0->edge[3] == p1->edge[1]);
868 return (p0->edge[0] == p1->edge[3] &&
869 p0->edge[1] == p1->edge[0] &&
870 p0->edge[2] == p1->edge[1] &&
871 p0->edge[3] == p1->edge[2]);
878 /* Returns the proper rotation for the piece at the given position.
881 proper_rotation (jigsaw_configuration *jc, puzzle_piece *p,
887 if (cx != x) abort(); /* must be in integral position! */
888 if (cy != y) abort();
889 p1 = &jc->puzzle [cy * jc->puzzle_width + cx];
890 if (same_shape (p, p1, 0)) return 0;
891 if (same_shape (p, p1, 90)) return 90;
892 if (same_shape (p, p1, 180)) return 180;
893 if (same_shape (p, p1, 270)) return 270;
894 abort(); /* these two pieces don't match in any rotation! */
898 /* Returns the piece currently at the given position.
900 static puzzle_piece *
901 piece_at (jigsaw_configuration *jc, double x, double y)
903 int npieces = jc->puzzle_width * jc->puzzle_height;
907 if (cx != x) abort(); /* must be in integral position! */
908 if (cy != y) abort();
910 for (i = 0; i < npieces; i++)
912 puzzle_piece *p = &jc->puzzle [i];
913 if (p->current.x == cx &&
917 abort(); /* no piece at that position? */
922 shuffle_grid (ModeInfo *mi)
924 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
925 int max_tries = jc->puzzle_width * jc->puzzle_height;
926 int npieces = jc->puzzle_width * jc->puzzle_height;
929 for (i = 0; i < npieces; i++)
931 puzzle_piece *p0 = &jc->puzzle [i];
932 puzzle_piece *p1 = 0;
935 for (k = 0; k < max_tries; k++)
937 p1 = &jc->puzzle [random() % npieces];
938 if (same_shape (p0, p1, 0)) break;
939 if (same_shape (p0, p1, 90)) break;
940 if (same_shape (p0, p1, 180)) break;
941 if (same_shape (p0, p1, 270)) break;
942 p1 = 0; /* mismatch */
947 s = p0->current; p0->current = p1->current; p1->current = s;
949 proper_rotation (jc, p0, p0->current.x, p0->current.y);
951 proper_rotation (jc, p1, p1->current.x, p1->current.y);
957 /* We tend to accumulate floating point errors, e.g., z being 0.000001
958 after a move. This makes sure float values that should be integral are.
961 smooth_grid (ModeInfo *mi)
963 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
964 int npieces = jc->puzzle_width * jc->puzzle_height;
967 for (i = 0; i < npieces; i++)
969 puzzle_piece *p = &jc->puzzle [i];
971 P.x = (int) (P.x + 0.5); \
972 P.y = (int) (P.y + 0.5); \
973 P.z = (int) (P.z + 0.5); \
974 P.r = (int) (P.r + 0.5)
979 if (p->tick <= 0.0001) p->tick = 0.0;
980 if (p->tick >= 0.9999) p->tick = 1.0;
986 begin_scatter (ModeInfo *mi, Bool unscatter_p)
988 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
989 int npieces = jc->puzzle_width * jc->puzzle_height;
992 ctr.x = jc->puzzle_width / 2;
993 ctr.y = jc->puzzle_height / 2;
995 for (i = 0; i < npieces; i++)
997 puzzle_piece *p = &jc->puzzle [i];
1000 p->tick = -frand(1.0);
1001 p->from = p->current;
1003 a.x = p->from.x - ctr.x; /* position relative to center */
1004 a.y = p->from.y - ctr.y;
1006 r = sqrt (a.x*a.x + a.y*a.y);
1007 th = atan2 (a.x, a.y);
1009 d = MAX (jc->puzzle_width, jc->puzzle_height) * 2;
1012 p->to.x = ctr.x + (r * sin (th));
1013 p->to.y = ctr.y + (r * cos (th));
1014 p->to.z = p->from.z;
1015 p->to.r = ((int) p->from.r + (random() % 180)) % 360;
1016 p->arc_height = frand(10.0);
1020 XYZR s = p->to; p->to = p->from; p->from = s;
1021 p->current = p->from;
1028 solved_p (ModeInfo *mi)
1030 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1031 int npieces = jc->puzzle_width * jc->puzzle_height;
1034 for (i = 0; i < npieces; i++)
1036 puzzle_piece *p = &jc->puzzle [i];
1037 if (p->current.x != p->home.x ||
1038 p->current.y != p->home.y ||
1039 p->current.z != p->home.z)
1047 move_one_piece (ModeInfo *mi)
1049 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1050 int npieces = jc->puzzle_width * jc->puzzle_height;
1053 for (i = 0; i < npieces * 100; i++) /* shouldn't take that long */
1055 int i = random() % npieces;
1056 puzzle_piece *p0 = &jc->puzzle [i];
1059 if (p0->current.x == p0->home.x &&
1060 p0->current.y == p0->home.y &&
1061 p0->current.z == p0->home.z)
1062 continue; /* piece already solved - try again */
1064 /* swap with the piece occupying p0's home cell. */
1065 p1 = piece_at (jc, p0->home.x, p0->home.y);
1067 if (p0 == p1) abort(); /* should have caught this above */
1070 p0->from = p0->current;
1071 p0->to = p1->current;
1072 p0->to.r = proper_rotation (jc, p0, p0->to.x, p0->to.y);
1075 p1->from = p1->current;
1076 p1->to = p0->current;
1077 p1->to.r = proper_rotation (jc, p1, p1->to.x, p1->to.y);
1079 /* Try to avoid having them intersect each other in the air. */
1082 while (fabs (p0->arc_height - p1->arc_height) < 1.5)
1084 p0->arc_height = 0.5 + frand(3.0);
1085 p1->arc_height = 1.0 + frand(3.0);
1089 V = 90 - BELLRAND(180); \
1090 if (! (random() % 5)) V *= 2; \
1091 if (! (random() % 5)) V *= 2; \
1092 if (! (random() % 5)) V *= 2
1093 RTILT (p0->max_tilt);
1094 RTILT (p1->max_tilt);
1098 fprintf (stderr, "%s: swapping %2d,%-2d with %2d,%d\n", progname,
1099 (int) p0->from.x, (int) p0->from.y,
1100 (int) p1->from.x, (int) p1->from.y);
1104 abort(); /* infinite loop! */
1109 anim_tick (ModeInfo *mi)
1111 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1112 int npieces = jc->puzzle_width * jc->puzzle_height;
1114 Bool finished_p = True;
1116 if (jc->pausing > 0)
1118 jc->pausing -= jc->tick_speed * speed;
1119 if (debug_p && jc->pausing <= 0)
1120 fprintf (stderr, "%s: (done pausing)\n", progname);
1124 for (i = 0; i < npieces; i++)
1126 puzzle_piece *p = &jc->puzzle [i];
1129 if (p->tick >= 1.0) continue; /* this piece is done */
1130 finished_p = False; /* not done */
1132 p->tick += jc->tick_speed * speed;
1133 if (p->tick > 1.0) p->tick = 1.0;
1135 if (p->tick < 0.0) continue; /* not yet started */
1137 tt = 1 - sin (M_PI/2 - p->tick * M_PI/2);
1139 p->current.x = p->from.x + ((p->to.x - p->from.x) * tt);
1140 p->current.y = p->from.y + ((p->to.y - p->from.y) * tt);
1141 p->current.z = p->from.z + ((p->to.z - p->from.z) * tt);
1142 p->current.r = p->from.r + ((p->to.r - p->from.r) * tt);
1144 p->current.z += p->arc_height * sin (p->tick * M_PI);
1146 p->tilt = p->max_tilt * sin (p->tick * M_PI);
1155 loading_msg (ModeInfo *mi)
1157 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1158 int wire = MI_IS_WIREFRAME(mi);
1159 const char *text = "Loading...";
1161 int w = texture_string_width (jc->texfont, text, &h);
1165 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1167 if (! jc->loading_dlist)
1169 GLfloat othick = jc->line_thickness;
1170 puzzle_piece P = { 0, };
1172 jc->loading_dlist = glGenLists (1);
1173 glNewList (jc->loading_dlist, GL_COMPILE);
1174 jc->line_thickness = 1;
1176 resolution_arg, thickness_arg,
1177 OUT, OUT, IN, OUT, True);
1178 jc->line_thickness = othick;
1182 glColor3f (0.2, 0.2, 0.4);
1187 get_position (jc->rot, &x, &y, &z, True);
1188 glRotatef (x * 360, 1, 0, 0);
1189 glRotatef (y * 360, 0, 1, 0);
1190 glRotatef (z * 360, 0, 0, 1);
1192 glTranslatef (-0.5, -0.5, 0);
1193 glCallList (jc->loading_dlist);
1197 glColor3f (0.7, 0.7, 1);
1200 glMatrixMode(GL_PROJECTION);
1204 glMatrixMode(GL_MODELVIEW);
1209 double rot = current_device_rotation();
1210 glRotatef(rot, 0, 0, 1);
1211 if ((rot > 45 && rot < 135) ||
1212 (rot < -45 && rot > -135))
1214 GLfloat s = MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
1215 glScalef (s, 1/s, 1);
1219 glOrtho(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi), -1, 1);
1220 glTranslatef ((MI_WIDTH(mi) - w) / 2,
1221 (MI_HEIGHT(mi) - h) / 2,
1223 glEnable (GL_TEXTURE_2D);
1224 glPolygonMode (GL_FRONT, GL_FILL);
1225 glDisable (GL_LIGHTING);
1226 glEnable (GL_BLEND);
1227 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1228 print_texture_string (jc->texfont, text);
1229 glEnable (GL_DEPTH_TEST);
1232 glMatrixMode(GL_PROJECTION);
1235 glMatrixMode(GL_MODELVIEW);
1240 animate (ModeInfo *mi)
1242 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1246 if (jc->button_down_p && jc->state != PUZZLE_LOADING_MSG)
1251 case PUZZLE_LOADING_MSG:
1256 case PUZZLE_LOADING:
1257 if (!jc->puzzle) break; /* still loading */
1258 jc->tick_speed = slow;
1261 begin_scatter (mi, True);
1263 jc->state = PUZZLE_UNSCATTER;
1264 if (debug_p) fprintf (stderr, "%s: unscattering\n", progname);
1267 case PUZZLE_UNSCATTER:
1268 jc->tick_speed = slow;
1273 jc->state = PUZZLE_SOLVE;
1274 if (debug_p) fprintf (stderr, "%s: solving\n", progname);
1279 jc->tick_speed = fast;
1285 if (debug_p) fprintf (stderr, "%s: solved!\n", progname);
1286 begin_scatter (mi, False);
1287 jc->state = PUZZLE_SCATTER;
1289 if (debug_p) fprintf (stderr, "%s: scattering\n", progname);
1293 move_one_piece (mi);
1299 case PUZZLE_SCATTER:
1300 jc->tick_speed = slow;
1303 free_puzzle_grid (jc);
1305 jc->state = PUZZLE_LOADING;
1307 if (debug_p) fprintf (stderr, "%s: loading\n", progname);
1317 /* Window management, etc
1320 reshape_jigsaw (ModeInfo *mi, int width, int height)
1322 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1323 GLfloat h = (GLfloat) height / (GLfloat) width;
1325 glViewport (0, 0, (GLint) width, (GLint) height);
1327 glMatrixMode(GL_PROJECTION);
1329 gluPerspective (30.0, 1/h, 1.0, 100.0);
1331 glMatrixMode(GL_MODELVIEW);
1333 gluLookAt( 0.0, 0.0, 30.0,
1337 glClear(GL_COLOR_BUFFER_BIT);
1339 jc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
1344 jigsaw_handle_event (ModeInfo *mi, XEvent *event)
1346 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1348 if (event->xany.type == ButtonPress &&
1349 event->xbutton.button == Button1)
1351 jc->button_down_p = True;
1352 gltrackball_start (jc->trackball,
1353 event->xbutton.x, event->xbutton.y,
1354 MI_WIDTH (mi), MI_HEIGHT (mi));
1357 else if (event->xany.type == ButtonRelease &&
1358 event->xbutton.button == Button1)
1360 jc->button_down_p = False;
1363 else if (event->xany.type == ButtonPress &&
1364 (event->xbutton.button == Button4 ||
1365 event->xbutton.button == Button5 ||
1366 event->xbutton.button == Button6 ||
1367 event->xbutton.button == Button7))
1369 gltrackball_mousewheel (jc->trackball, event->xbutton.button, 10,
1370 !!event->xbutton.state);
1373 else if (event->xany.type == MotionNotify &&
1376 gltrackball_track (jc->trackball,
1377 event->xmotion.x, event->xmotion.y,
1378 MI_WIDTH (mi), MI_HEIGHT (mi));
1381 else if (event->xany.type == KeyPress)
1385 XLookupString (&event->xkey, &c, 1, &keysym, 0);
1387 if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
1389 begin_scatter (mi, False);
1390 jc->state = PUZZLE_SCATTER;
1401 init_jigsaw (ModeInfo *mi)
1403 jigsaw_configuration *jc;
1404 int wire = MI_IS_WIREFRAME(mi);
1407 sps = (jigsaw_configuration *)
1408 calloc (MI_NUM_SCREENS(mi), sizeof (jigsaw_configuration));
1410 fprintf(stderr, "%s: out of memory\n", progname);
1414 jc = &sps[MI_SCREEN(mi)];
1415 jc->glx_context = init_GL(mi);
1417 reshape_jigsaw (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1421 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
1422 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
1423 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
1424 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
1426 glLightfv(GL_LIGHT0, GL_POSITION, pos);
1427 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
1428 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
1429 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
1432 jc->trackball = gltrackball_init ();
1433 jc->rot = make_rotator (0, 0, 0, 0, speed * 0.002, True);
1434 jc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
1436 jc->state = PUZZLE_LOADING_MSG;
1438 resolution_arg /= complexity_arg;
1441 /* If it's not even, we get crosses. */
1442 if (resolution_arg & 1)
1444 # endif /* !HAVE_TESS */
1447 make_puzzle_grid (mi);
1454 draw_jigsaw (ModeInfo *mi)
1456 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1457 Display *dpy = MI_DISPLAY(mi);
1458 Window window = MI_WINDOW(mi);
1459 int wire = MI_IS_WIREFRAME(mi);
1461 if (!jc->glx_context)
1464 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(jc->glx_context));
1466 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1468 mi->polygon_count = 0;
1471 glRotatef(current_device_rotation(), 0, 0, 1);
1472 gltrackball_rotate (jc->trackball);
1476 if (wobble_p && jc->puzzle)
1480 get_position (jc->rot, &x, &y, &z, !jc->button_down_p);
1481 x = 1; /* always lean back */
1482 glRotatef (max/2 - x*max, 1, 0, 0);
1483 glRotatef (max/2 - z*max, 0, 1, 0);
1488 GLfloat s = 14.0 / jc->puzzle_height;
1491 glEnable(GL_CULL_FACE);
1492 glEnable(GL_DEPTH_TEST);
1493 glEnable(GL_NORMALIZE);
1494 glEnable(GL_LINE_SMOOTH);
1497 glTranslatef (-jc->puzzle_width / 2.0, -jc->puzzle_height / 2.0, 0);
1501 glEnable (GL_TEXTURE_2D);
1502 glEnable (GL_BLEND);
1503 glEnable (GL_LIGHTING);
1504 glEnable (GL_LIGHT0);
1505 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1508 for (y = 0; y < jc->puzzle_height; y++)
1509 for (x = 0; x < jc->puzzle_width; x++)
1511 puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
1513 glTranslatef (p->current.x, p->current.y, p->current.z);
1514 glTranslatef (0.5, 0.5, 0);
1515 glRotatef (p->current.r, 0, 0, 1);
1516 glRotatef (p->tilt, 0, 1, 0);
1517 glTranslatef (-0.5, -0.5, 0);
1518 glCallList(p->dlist);
1519 mi->polygon_count += p->polys;
1526 if (mi->fps_p) do_fps (mi);
1529 glXSwapBuffers(dpy, window);
1532 XSCREENSAVER_MODULE ("Jigsaw", jigsaw)