1 /* xscreensaver, Copyright (c) 1997-2015 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" \
59 "*suppressRotationAnimation: True\n" \
62 # define refresh_jigsaw 0
63 # define release_jigsaw 0
65 #define countof(x) (sizeof((x))/sizeof((*x)))
70 # include <X11/Xlib.h>
75 #include "xlockmore.h"
77 #include "gltrackball.h"
80 #include "grab-ximage.h"
85 #else /* !HAVE_JWZGLES */
87 #endif /* !HAVE_JWZGLES */
90 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
92 #ifdef USE_GL /* whole file */
103 typedef struct jigsaw_configuration jigsaw_configuration;
106 double x,y,z,r; /* position and Z rotation (in degrees) */
111 jigsaw_configuration *jc;
116 XYZR home; /* correct position in puzzle */
117 XYZR current; /* where it is right now */
118 XYZR from, to; /* in transit from A to B */
119 double tick; /* 0-1.0, how far from A to B */
120 double arc_height; /* height of apex of curved path from A to B */
121 double tilt, max_tilt;
126 struct jigsaw_configuration {
127 GLXContext *glx_context;
128 trackball_state *trackball;
131 texture_font_data *texfont;
132 GLuint loading_dlist;
136 puzzle_piece *puzzle;
138 enum { PUZZLE_LOADING_MSG,
142 PUZZLE_SCATTER } state;
147 GLfloat tex_x, tex_y, tex_width, tex_height, aspect;
149 GLuint line_thickness;
152 static jigsaw_configuration *sps = NULL;
154 static GLfloat speed;
155 static GLfloat complexity_arg;
156 static int resolution_arg;
157 static GLfloat thickness_arg;
158 static Bool wobble_p;
161 static XrmOptionDescRec opts[] = {
162 { "-speed", ".speed", XrmoptionSepArg, 0 },
163 { "-complexity", ".complexity", XrmoptionSepArg, 0 },
164 { "-resolution", ".resolution", XrmoptionSepArg, 0 },
165 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
166 { "-wobble", ".wobble", XrmoptionNoArg, "True" },
167 { "+wobble", ".wobble", XrmoptionNoArg, "False" },
168 { "-debug", ".debug", XrmoptionNoArg, "True" },
171 static argtype vars[] = {
172 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
173 {&complexity_arg, "complexity", "Complexity", DEF_COMPLEXITY, t_Float},
174 {&resolution_arg, "resolution", "Resolution", DEF_RESOLUTION, t_Int},
175 {&thickness_arg, "thickness", "Thickness", DEF_THICKNESS, t_Float},
176 {&wobble_p, "wobble", "Wobble", DEF_WOBBLE, t_Bool},
177 {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
180 ENTRYPOINT ModeSpecOpt jigsaw_opts = {countof(opts), opts, countof(vars), vars, NULL};
183 /* Returns a spline describing one edge of a puzzle piece of the given length.
186 make_puzzle_curve (int pixels)
188 double x0 = 0.0000, y0 = 0.0000;
189 double x1 = 0.3333, y1 = 0.1000;
190 double x2 = 0.4333, y2 = 0.0333;
191 double x3 = 0.4666, y3 = -0.0666;
192 double x4 = 0.3333, y4 = -0.1666;
193 double x5 = 0.3666, y5 = -0.2900;
194 double x6 = 0.5000, y6 = -0.3333;
196 spline *s = make_spline(20);
200 s->control_x[s->n_controls] = pixels * (x); \
201 s->control_y[s->n_controls] = pixels * (y); \
226 tess_error_cb (GLenum errorCode)
228 fprintf (stderr, "%s: tesselation error: %s\n",
229 progname, gluErrorString(errorCode));
235 tess_combine_cb (GLdouble coords[3], GLdouble *d[4], GLfloat w[4],
238 GLdouble *new = (GLdouble *) malloc (3 * sizeof(*new));
247 tess_vertex_cb (void *vertex_data, void *closure)
249 puzzle_piece *p = (puzzle_piece *) closure;
250 GLdouble *v = (GLdouble *) vertex_data;
257 GLfloat pw = p->jc->puzzle_width;
258 GLfloat ph = p->jc->puzzle_height;
260 GLfloat xx = x / (GLfloat) resolution_arg; /* 0-1 from piece origin */
261 GLfloat yy = y / (GLfloat) resolution_arg;
262 GLdouble tx = (p->home.x + xx) / pw; /* 0-1 from puzzle origin */
263 GLdouble ty = (ph - p->home.y - yy) / ph;
265 tx = p->jc->tex_x + (tx * p->jc->tex_width);
266 ty = p->jc->tex_y + (ty * p->jc->tex_height);
268 glTexCoord2d (tx, ty);
271 glVertex3d (x, y, z);
274 #else /* HAVE_TESS */
276 /* Writes triangles into the array of floats.
277 Returns the number of floats written (triangles * 9).
280 make_piece_eighth (jigsaw_configuration *jc, const spline *s,
281 int resolution, int type, GLfloat *out,
282 Bool flip_x, Bool flip_y, Bool rotate_p)
285 int cx = resolution/2;
286 int cy = resolution/2;
287 int np = (s->n_points / 2) + 1;
288 int last_x = -999999, last_y = -999999;
289 Bool inflected = False;
309 for (i = (type == IN ? np-1 : 0);
310 (type == IN ? i >= 0 : i < np);
311 i += (type == IN ? -1 : 1))
313 int x = s->points[i].x;
314 int y = s->points[i].y;
319 if (last_x != -999999)
370 int count = out - oout;
374 for (i = 0; i < count; i += 3)
375 oout[i] = resolution - oout[i];
378 for (i = 0; i < count; i += 3)
379 oout[i+1] = resolution - oout[i+1];
382 if (flip_x) cw_p = !cw_p;
383 if (flip_y) cw_p = !cw_p;
386 for (i = 0; i < count; i += 9)
388 GLfloat x1 = oout[i+0];
389 GLfloat y1 = oout[i+1];
390 GLfloat x2 = oout[i+3];
391 GLfloat y2 = oout[i+4];
392 GLfloat x3 = oout[i+6];
393 GLfloat y3 = oout[i+7];
403 for (i = 0; i < count; i += 3)
406 GLfloat y = oout[i+1];
407 oout[i] = resolution - y;
415 #endif /* !HAVE_TESS */
419 /* Draws a puzzle piece. The top/right/bottom/left_type args
420 indicate the direction the tabs point: 1 for out, -1 for in, 0 for flat.
423 draw_piece (jigsaw_configuration *jc, puzzle_piece *p,
424 int resolution, GLfloat thickness,
425 int top_type, int right_type,
426 int bottom_type, int left_type,
429 spline *s = make_puzzle_curve (resolution);
430 GLdouble *pts = (GLdouble *) malloc (s->n_points * 4 * 3 * sizeof(*pts));
433 GLdouble z = resolution * thickness;
441 pts[o++] = resolution;
445 for (i = 0; i < s->n_points; i++) {
446 pts[o++] = s->points[i].x;
447 pts[o++] = s->points[i].y * top_type;
452 if (right_type == 0) {
453 pts[o++] = resolution;
454 pts[o++] = resolution;
457 for (i = 1; i < s->n_points; i++) {
458 pts[o++] = resolution + s->points[i].y * (-right_type);
459 pts[o++] = s->points[i].x;
464 if (bottom_type == 0) {
466 pts[o++] = resolution;
469 for (i = 1; i < s->n_points; i++) {
470 pts[o++] = s->points[s->n_points-i-1].x;
471 pts[o++] = resolution + s->points[s->n_points-i-1].y * (-bottom_type);
476 if (left_type == 0) {
481 for (i = 1; i < s->n_points; i++) {
482 pts[o++] = s->points[s->n_points-i-1].y * left_type;
483 pts[o++] = s->points[s->n_points-i-1].x;
488 { GLfloat ss = 1.0 / resolution; glScalef (ss, ss, ss); }
490 # ifndef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
491 glPolygonMode (GL_FRONT_AND_BACK, wire ? GL_LINE : GL_FILL);
496 glDisable (GL_TEXTURE_2D);
497 glDisable (GL_BLEND);
498 glDisable (GL_LIGHTING);
505 # define _GLUfuncptr void(*)(void)
507 GLUtesselator *tess = gluNewTess();
508 gluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)glBegin);
509 gluTessCallback(tess, GLU_TESS_VERTEX_DATA,(_GLUfuncptr)tess_vertex_cb);
510 gluTessCallback(tess, GLU_TESS_END, (_GLUfuncptr)glEnd);
511 gluTessCallback(tess, GLU_TESS_COMBINE, (_GLUfuncptr)tess_combine_cb);
512 gluTessCallback(tess, GLU_TESS_ERROR, (_GLUfuncptr)tess_error_cb);
515 glEnable (GL_TEXTURE_2D);
517 glEnable (GL_LIGHTING);
518 glBindTexture(GL_TEXTURE_2D, jc->texid);
519 glFrontFace (GL_CCW);
520 glNormal3f (0, 0, 1);
521 gluTessBeginPolygon (tess, p);
522 gluTessBeginContour (tess);
523 for (i = 0; i < o; i += 3)
525 GLdouble *p = pts + i;
526 gluTessVertex (tess, p, p);
527 polys++; /* not quite right but close */
529 gluTessEndContour(tess);
530 gluTessEndPolygon(tess);
533 glDisable (GL_TEXTURE_2D);
535 glNormal3f (0, 0, -1);
536 gluTessBeginPolygon (tess, 0);
537 gluTessBeginContour (tess);
538 for (i = 0; i < o; i += 3)
540 GLdouble *p = pts + i;
542 gluTessVertex (tess, p, p);
543 polys++; /* not quite right but close */
545 gluTessEndContour(tess);
546 gluTessEndPolygon(tess);
550 for (i = 0; i < o; i += 3)
552 GLdouble *p = pts + i;
556 # else /* !HAVE_TESS */
558 GLfloat *tri = (GLfloat *)
559 (GLfloat *) malloc (s->n_points * 4 * 3 * 3 * sizeof(*pts));
564 tri += make_piece_eighth (jc, s, resolution, top_type, tri, 0, 0, 0);
565 tri += make_piece_eighth (jc, s, resolution, top_type, tri, 1, 0, 0);
566 tri += make_piece_eighth (jc, s, resolution, left_type, tri, 0, 1, 1);
567 tri += make_piece_eighth (jc, s, resolution, left_type, tri, 1, 1, 1);
568 tri += make_piece_eighth (jc, s, resolution, bottom_type, tri, 0, 1, 0);
569 tri += make_piece_eighth (jc, s, resolution, bottom_type, tri, 1, 1, 0);
570 tri += make_piece_eighth (jc, s, resolution, right_type, tri, 0, 0, 1);
571 tri += make_piece_eighth (jc, s, resolution, right_type, tri, 1, 0, 1);
572 count = (tri - otri) / 9;
576 glEnable (GL_TEXTURE_2D);
578 glEnable (GL_LIGHTING);
579 glBindTexture(GL_TEXTURE_2D, jc->texid);
582 for (zz = z; zz >= -z; zz -= 2*z)
585 glFrontFace (zz > 0 ? GL_CCW : GL_CW);
586 glNormal3f (0, 0, (zz > 0 ? 1 : -1));
589 glDisable (GL_TEXTURE_2D); /* back face */
592 glTranslatef (0, 0, zz);
597 for (i = 0; i < count; i++)
599 glBegin (GL_LINE_LOOP);
600 glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
601 glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
602 glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
608 GLfloat pw = p->jc->puzzle_width;
609 GLfloat ph = p->jc->puzzle_height;
610 GLfloat r = resolution;
612 glBegin (GL_TRIANGLES);
613 for (i = 0; i < count * 3; i++)
619 /* 0-1 from piece origin */
623 /* 0-1 from puzzle origin */
624 GLfloat tx = (p->home.x + xx) / pw;
625 GLfloat ty = (ph - p->home.y - yy) / ph;
627 tx = p->jc->tex_x + (tx * p->jc->tex_width);
628 ty = p->jc->tex_y + (ty * p->jc->tex_height);
630 glTexCoord2f (tx, ty);
631 glVertex3f (x, y, z);
641 # endif /* !HAVE_TESS */
646 glFrontFace (GL_CCW);
647 glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
648 for (i = 0; i < o; i += 3)
652 GLdouble *p = pts + i;
653 GLdouble *pj = pts + j;
654 GLdouble *pk = pts + k;
656 do_normal (pj[0], pj[1], pj[2],
657 pj[0], pj[1], -pj[2],
658 pk[0], pk[1], pk[2]);
660 glVertex3f (p[0], p[1], p[2]);
661 glVertex3f (p[0], p[1], -p[2]);
667 glColor3f (0.3, 0.3, 0.3);
669 /* outline the edges in gray */
671 glDisable (GL_TEXTURE_2D);
672 glDisable (GL_LIGHTING);
673 glLineWidth (jc->line_thickness);
675 glBegin (GL_LINE_LOOP);
676 for (i = 0; i < o; i += 3)
677 glVertex3f (pts[i], pts[i+1], pts[i+2]);
681 glBegin (GL_LINE_LOOP);
682 for (i = 0; i < o; i += 3)
683 glVertex3f (pts[i], pts[i+1], -pts[i+2]);
695 free_puzzle_grid (jigsaw_configuration *jc)
698 for (i = 0; i < jc->puzzle_width * jc->puzzle_height; i++)
699 glDeleteLists (jc->puzzle[i].dlist, 1);
702 jc->puzzle_width = 0;
703 jc->puzzle_height = 0;
708 make_puzzle_grid (ModeInfo *mi)
710 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
711 int wire = MI_IS_WIREFRAME(mi);
713 GLfloat size = (8 + (random() % 8)) * complexity_arg;
716 free_puzzle_grid (jc);
719 jc->aspect = MI_WIDTH(mi) / (float) MI_HEIGHT(mi);
721 if (jc->aspect >= 1.0)
723 jc->puzzle_width = size;
724 jc->puzzle_height = (size + 0.5) / jc->aspect;
728 jc->puzzle_width = (size + 0.5) * jc->aspect;
729 jc->puzzle_height = size;
732 if (jc->puzzle_width < 1) jc->puzzle_width = 1;
733 if (jc->puzzle_height < 1) jc->puzzle_height = 1;
736 fprintf (stderr, "%s: grid %4d x %-4d (%.2f)\n", progname,
737 jc->puzzle_width, jc->puzzle_height,
738 ((float) jc->puzzle_width / jc->puzzle_height));
740 jc->puzzle = (puzzle_piece *)
741 calloc (jc->puzzle_width * (jc->puzzle_height+1), sizeof(*jc->puzzle));
743 /* Randomize the right and bottom edge of each piece.
744 Match the left edge of the piece to the right to our right edge.
745 Match the top edge of the piece to the bottom to our bottom edge.
747 for (y = 0; y < jc->puzzle_height; y++)
748 for (x = 0; x < jc->puzzle_width; x++)
750 puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
751 puzzle_piece *r = &jc->puzzle [y * jc->puzzle_width + x+1];
752 puzzle_piece *b = &jc->puzzle [(y+1) * jc->puzzle_width + x];
753 p->edge[RIGHT] = (random() & 1) ? IN : OUT;
754 p->edge[BOTTOM] = (random() & 1) ? IN : OUT;
755 r->edge[LEFT] = p->edge[RIGHT] == IN ? OUT : IN;
756 b->edge[TOP] = p->edge[BOTTOM] == IN ? OUT : IN;
759 /* tell each piece where it belongs. */
760 for (y = 0; y < jc->puzzle_height; y++)
761 for (x = 0; x < jc->puzzle_width; x++)
763 puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
767 p->current = p->home;
769 /* make sure the outer border is flat */
770 if (p->home.x == 0) p->edge[LEFT] = FLAT;
771 if (p->home.y == 0) p->edge[TOP] = FLAT;
772 if (p->home.x == jc->puzzle_width-1) p->edge[RIGHT] = FLAT;
773 if (p->home.y == jc->puzzle_height-1) p->edge[BOTTOM] = FLAT;
775 /* generate the polygons */
776 p->dlist = glGenLists (1);
777 check_gl_error ("generating lists");
778 if (p->dlist <= 0) abort();
780 glNewList (p->dlist, GL_COMPILE);
781 p->polys += draw_piece (jc, p,
782 resolution_arg, thickness_arg,
783 p->edge[TOP], p->edge[RIGHT],
784 p->edge[BOTTOM], p->edge[LEFT],
791 static void shuffle_grid (ModeInfo *mi);
795 image_loaded_cb (const char *filename, XRectangle *geometry,
796 int image_width, int image_height,
797 int texture_width, int texture_height,
800 ModeInfo *mi = (ModeInfo *) closure;
801 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
803 jc->tex_x = geometry->x / (float) texture_width;
804 jc->tex_y = geometry->y / (float) texture_height;
805 jc->tex_width = geometry->width / (float) texture_width;
806 jc->tex_height = geometry->height / (float) texture_height;
807 jc->aspect = geometry->width / (float) geometry->height;
811 fprintf (stderr, "%s: image %s\n", progname,
812 (filename ? filename : "(null)"));
813 fprintf (stderr, "%s: image %4d x %-4d + %4d + %-4d (%.2f)\n", progname,
814 geometry->width, geometry->height, geometry->x, geometry->y,
815 (float) geometry->width / geometry->height);
816 fprintf (stderr, "%s: tex %4d x %-4d\n", progname,
817 texture_width, texture_height);
818 fprintf (stderr, "%s: tex %4.2f x %4.2f + %4.2f + %4.2f (%.2f)\n",
820 jc->tex_width, jc->tex_height, jc->tex_x, jc->tex_y,
821 (jc->tex_width / jc->tex_height) *
822 (texture_width / texture_height));
825 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
826 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
827 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
828 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
830 make_puzzle_grid (mi);
835 load_image (ModeInfo *mi)
837 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
838 load_texture_async (mi->xgwa.screen, mi->window,
839 *jc->glx_context, 0, 0,
841 image_loaded_cb, mi);
845 /* Whether the two pieces are the same shape, when the second piece
846 is rotated by the given degrees.
849 same_shape (puzzle_piece *p0, puzzle_piece *p1, int rotated_by)
854 return (p0->edge[0] == p1->edge[0] &&
855 p0->edge[1] == p1->edge[1] &&
856 p0->edge[2] == p1->edge[2] &&
857 p0->edge[3] == p1->edge[3]);
859 return (p0->edge[0] == p1->edge[1] &&
860 p0->edge[1] == p1->edge[2] &&
861 p0->edge[2] == p1->edge[3] &&
862 p0->edge[3] == p1->edge[0]);
864 return (p0->edge[0] == p1->edge[2] &&
865 p0->edge[1] == p1->edge[3] &&
866 p0->edge[2] == p1->edge[0] &&
867 p0->edge[3] == p1->edge[1]);
869 return (p0->edge[0] == p1->edge[3] &&
870 p0->edge[1] == p1->edge[0] &&
871 p0->edge[2] == p1->edge[1] &&
872 p0->edge[3] == p1->edge[2]);
879 /* Returns the proper rotation for the piece at the given position.
882 proper_rotation (jigsaw_configuration *jc, puzzle_piece *p,
888 if (cx != x) abort(); /* must be in integral position! */
889 if (cy != y) abort();
890 p1 = &jc->puzzle [cy * jc->puzzle_width + cx];
891 if (same_shape (p, p1, 0)) return 0;
892 if (same_shape (p, p1, 90)) return 90;
893 if (same_shape (p, p1, 180)) return 180;
894 if (same_shape (p, p1, 270)) return 270;
895 abort(); /* these two pieces don't match in any rotation! */
899 /* Returns the piece currently at the given position.
901 static puzzle_piece *
902 piece_at (jigsaw_configuration *jc, double x, double y)
904 int npieces = jc->puzzle_width * jc->puzzle_height;
908 if (cx != x) abort(); /* must be in integral position! */
909 if (cy != y) abort();
911 for (i = 0; i < npieces; i++)
913 puzzle_piece *p = &jc->puzzle [i];
914 if (p->current.x == cx &&
918 abort(); /* no piece at that position? */
923 shuffle_grid (ModeInfo *mi)
925 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
926 int max_tries = jc->puzzle_width * jc->puzzle_height;
927 int npieces = jc->puzzle_width * jc->puzzle_height;
930 for (i = 0; i < npieces; i++)
932 puzzle_piece *p0 = &jc->puzzle [i];
933 puzzle_piece *p1 = 0;
936 for (k = 0; k < max_tries; k++)
938 p1 = &jc->puzzle [random() % npieces];
939 if (same_shape (p0, p1, 0)) break;
940 if (same_shape (p0, p1, 90)) break;
941 if (same_shape (p0, p1, 180)) break;
942 if (same_shape (p0, p1, 270)) break;
943 p1 = 0; /* mismatch */
948 s = p0->current; p0->current = p1->current; p1->current = s;
950 proper_rotation (jc, p0, p0->current.x, p0->current.y);
952 proper_rotation (jc, p1, p1->current.x, p1->current.y);
958 /* We tend to accumulate floating point errors, e.g., z being 0.000001
959 after a move. This makes sure float values that should be integral are.
962 smooth_grid (ModeInfo *mi)
964 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
965 int npieces = jc->puzzle_width * jc->puzzle_height;
968 for (i = 0; i < npieces; i++)
970 puzzle_piece *p = &jc->puzzle [i];
972 P.x = (int) (P.x + 0.5); \
973 P.y = (int) (P.y + 0.5); \
974 P.z = (int) (P.z + 0.5); \
975 P.r = (int) (P.r + 0.5)
980 if (p->tick <= 0.0001) p->tick = 0.0;
981 if (p->tick >= 0.9999) p->tick = 1.0;
987 begin_scatter (ModeInfo *mi, Bool unscatter_p)
989 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
990 int npieces = jc->puzzle_width * jc->puzzle_height;
993 ctr.x = jc->puzzle_width / 2;
994 ctr.y = jc->puzzle_height / 2;
996 for (i = 0; i < npieces; i++)
998 puzzle_piece *p = &jc->puzzle [i];
1001 p->tick = -frand(1.0);
1002 p->from = p->current;
1004 a.x = p->from.x - ctr.x; /* position relative to center */
1005 a.y = p->from.y - ctr.y;
1007 r = sqrt (a.x*a.x + a.y*a.y);
1008 th = atan2 (a.x, a.y);
1010 d = MAX (jc->puzzle_width, jc->puzzle_height) * 2;
1013 p->to.x = ctr.x + (r * sin (th));
1014 p->to.y = ctr.y + (r * cos (th));
1015 p->to.z = p->from.z;
1016 p->to.r = ((int) p->from.r + (random() % 180)) % 360;
1017 p->arc_height = frand(10.0);
1021 XYZR s = p->to; p->to = p->from; p->from = s;
1022 p->current = p->from;
1029 solved_p (ModeInfo *mi)
1031 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1032 int npieces = jc->puzzle_width * jc->puzzle_height;
1035 for (i = 0; i < npieces; i++)
1037 puzzle_piece *p = &jc->puzzle [i];
1038 if (p->current.x != p->home.x ||
1039 p->current.y != p->home.y ||
1040 p->current.z != p->home.z)
1048 move_one_piece (ModeInfo *mi)
1050 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1051 int npieces = jc->puzzle_width * jc->puzzle_height;
1054 for (i = 0; i < npieces * 100; i++) /* shouldn't take that long */
1056 int i = random() % npieces;
1057 puzzle_piece *p0 = &jc->puzzle [i];
1060 if (p0->current.x == p0->home.x &&
1061 p0->current.y == p0->home.y &&
1062 p0->current.z == p0->home.z)
1063 continue; /* piece already solved - try again */
1065 /* swap with the piece occupying p0's home cell. */
1066 p1 = piece_at (jc, p0->home.x, p0->home.y);
1068 if (p0 == p1) abort(); /* should have caught this above */
1071 p0->from = p0->current;
1072 p0->to = p1->current;
1073 p0->to.r = proper_rotation (jc, p0, p0->to.x, p0->to.y);
1076 p1->from = p1->current;
1077 p1->to = p0->current;
1078 p1->to.r = proper_rotation (jc, p1, p1->to.x, p1->to.y);
1080 /* Try to avoid having them intersect each other in the air. */
1083 while (fabs (p0->arc_height - p1->arc_height) < 1.5)
1085 p0->arc_height = 0.5 + frand(3.0);
1086 p1->arc_height = 1.0 + frand(3.0);
1090 V = 90 - BELLRAND(180); \
1091 if (! (random() % 5)) V *= 2; \
1092 if (! (random() % 5)) V *= 2; \
1093 if (! (random() % 5)) V *= 2
1094 RTILT (p0->max_tilt);
1095 RTILT (p1->max_tilt);
1099 fprintf (stderr, "%s: swapping %2d,%-2d with %2d,%d\n", progname,
1100 (int) p0->from.x, (int) p0->from.y,
1101 (int) p1->from.x, (int) p1->from.y);
1105 abort(); /* infinite loop! */
1110 anim_tick (ModeInfo *mi)
1112 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1113 int npieces = jc->puzzle_width * jc->puzzle_height;
1115 Bool finished_p = True;
1117 if (jc->pausing > 0)
1119 jc->pausing -= jc->tick_speed * speed;
1120 if (debug_p && jc->pausing <= 0)
1121 fprintf (stderr, "%s: (done pausing)\n", progname);
1125 for (i = 0; i < npieces; i++)
1127 puzzle_piece *p = &jc->puzzle [i];
1130 if (p->tick >= 1.0) continue; /* this piece is done */
1131 finished_p = False; /* not done */
1133 p->tick += jc->tick_speed * speed;
1134 if (p->tick > 1.0) p->tick = 1.0;
1136 if (p->tick < 0.0) continue; /* not yet started */
1138 tt = 1 - sin (M_PI/2 - p->tick * M_PI/2);
1140 p->current.x = p->from.x + ((p->to.x - p->from.x) * tt);
1141 p->current.y = p->from.y + ((p->to.y - p->from.y) * tt);
1142 p->current.z = p->from.z + ((p->to.z - p->from.z) * tt);
1143 p->current.r = p->from.r + ((p->to.r - p->from.r) * tt);
1145 p->current.z += p->arc_height * sin (p->tick * M_PI);
1147 p->tilt = p->max_tilt * sin (p->tick * M_PI);
1156 loading_msg (ModeInfo *mi)
1158 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1159 int wire = MI_IS_WIREFRAME(mi);
1160 const char *text = "Loading...";
1163 texture_string_metrics (jc->texfont, text, &e, 0, 0);
1165 h = e.ascent + e.descent;
1169 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1171 if (! jc->loading_dlist)
1173 GLfloat othick = jc->line_thickness;
1174 puzzle_piece P = { 0, };
1176 jc->loading_dlist = glGenLists (1);
1177 glNewList (jc->loading_dlist, GL_COMPILE);
1178 jc->line_thickness = 1;
1180 resolution_arg, thickness_arg,
1181 OUT, OUT, IN, OUT, True);
1182 jc->line_thickness = othick;
1186 glColor3f (0.2, 0.2, 0.4);
1191 get_position (jc->rot, &x, &y, &z, True);
1192 glRotatef (x * 360, 1, 0, 0);
1193 glRotatef (y * 360, 0, 1, 0);
1194 glRotatef (z * 360, 0, 0, 1);
1196 glTranslatef (-0.5, -0.5, 0);
1197 glCallList (jc->loading_dlist);
1201 glColor3f (0.7, 0.7, 1);
1204 glMatrixMode(GL_PROJECTION);
1208 glMatrixMode(GL_MODELVIEW);
1213 double rot = current_device_rotation();
1214 glRotatef(rot, 0, 0, 1);
1215 if ((rot > 45 && rot < 135) ||
1216 (rot < -45 && rot > -135))
1218 GLfloat s = MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
1219 glScalef (s, 1/s, 1);
1223 glOrtho(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi), -1, 1);
1224 glTranslatef ((MI_WIDTH(mi) - w) / 2,
1225 (MI_HEIGHT(mi) - h) / 2,
1227 glEnable (GL_TEXTURE_2D);
1228 glPolygonMode (GL_FRONT, GL_FILL);
1229 glDisable (GL_LIGHTING);
1230 glEnable (GL_BLEND);
1231 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1232 print_texture_string (jc->texfont, text);
1233 glEnable (GL_DEPTH_TEST);
1236 glMatrixMode(GL_PROJECTION);
1239 glMatrixMode(GL_MODELVIEW);
1244 animate (ModeInfo *mi)
1246 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1250 if (jc->button_down_p && jc->state != PUZZLE_LOADING_MSG)
1255 case PUZZLE_LOADING_MSG:
1260 case PUZZLE_LOADING:
1261 if (!jc->puzzle) break; /* still loading */
1262 jc->tick_speed = slow;
1265 begin_scatter (mi, True);
1267 jc->state = PUZZLE_UNSCATTER;
1268 if (debug_p) fprintf (stderr, "%s: unscattering\n", progname);
1271 case PUZZLE_UNSCATTER:
1272 jc->tick_speed = slow;
1277 jc->state = PUZZLE_SOLVE;
1278 if (debug_p) fprintf (stderr, "%s: solving\n", progname);
1283 jc->tick_speed = fast;
1289 if (debug_p) fprintf (stderr, "%s: solved!\n", progname);
1290 begin_scatter (mi, False);
1291 jc->state = PUZZLE_SCATTER;
1293 if (debug_p) fprintf (stderr, "%s: scattering\n", progname);
1297 move_one_piece (mi);
1303 case PUZZLE_SCATTER:
1304 jc->tick_speed = slow;
1307 free_puzzle_grid (jc);
1309 jc->state = PUZZLE_LOADING;
1311 if (debug_p) fprintf (stderr, "%s: loading\n", progname);
1321 /* Window management, etc
1324 reshape_jigsaw (ModeInfo *mi, int width, int height)
1326 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1327 GLfloat h = (GLfloat) height / (GLfloat) width;
1329 glViewport (0, 0, (GLint) width, (GLint) height);
1331 glMatrixMode(GL_PROJECTION);
1333 gluPerspective (30.0, 1/h, 1.0, 100.0);
1335 glMatrixMode(GL_MODELVIEW);
1337 gluLookAt( 0.0, 0.0, 30.0,
1341 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
1343 int o = (int) current_device_rotation();
1344 if (o != 0 && o != 180 && o != -180)
1345 glScalef (1/h, 1/h, 1/h);
1349 glClear(GL_COLOR_BUFFER_BIT);
1351 jc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
1356 jigsaw_handle_event (ModeInfo *mi, XEvent *event)
1358 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1360 if (gltrackball_event_handler (event, jc->trackball,
1361 MI_WIDTH (mi), MI_HEIGHT (mi),
1362 &jc->button_down_p))
1364 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
1366 begin_scatter (mi, False);
1367 jc->state = PUZZLE_SCATTER;
1376 init_jigsaw (ModeInfo *mi)
1378 jigsaw_configuration *jc;
1379 int wire = MI_IS_WIREFRAME(mi);
1382 sps = (jigsaw_configuration *)
1383 calloc (MI_NUM_SCREENS(mi), sizeof (jigsaw_configuration));
1385 fprintf(stderr, "%s: out of memory\n", progname);
1389 jc = &sps[MI_SCREEN(mi)];
1390 jc->glx_context = init_GL(mi);
1392 reshape_jigsaw (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1396 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
1397 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
1398 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
1399 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
1401 glLightfv(GL_LIGHT0, GL_POSITION, pos);
1402 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
1403 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
1404 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
1407 jc->trackball = gltrackball_init (False);
1408 jc->rot = make_rotator (0, 0, 0, 0, speed * 0.002, True);
1409 jc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
1411 jc->state = PUZZLE_LOADING_MSG;
1413 resolution_arg /= complexity_arg;
1416 /* If it's not even, we get crosses. */
1417 if (resolution_arg & 1)
1419 # endif /* !HAVE_TESS */
1422 make_puzzle_grid (mi);
1429 draw_jigsaw (ModeInfo *mi)
1431 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1432 Display *dpy = MI_DISPLAY(mi);
1433 Window window = MI_WINDOW(mi);
1434 int wire = MI_IS_WIREFRAME(mi);
1436 if (!jc->glx_context)
1439 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(jc->glx_context));
1441 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1443 mi->polygon_count = 0;
1446 /* glRotatef(current_device_rotation(), 0, 0, 1); */
1447 gltrackball_rotate (jc->trackball);
1451 if (wobble_p && jc->puzzle)
1455 get_position (jc->rot, &x, &y, &z, !jc->button_down_p);
1456 x = 1; /* always lean back */
1457 glRotatef (max/2 - x*max, 1, 0, 0);
1458 glRotatef (max/2 - z*max, 0, 1, 0);
1463 GLfloat s = 14.0 / jc->puzzle_height;
1466 glEnable(GL_CULL_FACE);
1467 glEnable(GL_DEPTH_TEST);
1468 glEnable(GL_NORMALIZE);
1469 glEnable(GL_LINE_SMOOTH);
1472 glTranslatef (-jc->puzzle_width / 2.0, -jc->puzzle_height / 2.0, 0);
1476 glEnable (GL_TEXTURE_2D);
1477 glEnable (GL_BLEND);
1478 glEnable (GL_LIGHTING);
1479 glEnable (GL_LIGHT0);
1480 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1483 for (y = 0; y < jc->puzzle_height; y++)
1484 for (x = 0; x < jc->puzzle_width; x++)
1486 puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
1488 glTranslatef (p->current.x, p->current.y, p->current.z);
1489 glTranslatef (0.5, 0.5, 0);
1490 glRotatef (p->current.r, 0, 0, 1);
1491 glRotatef (p->tilt, 0, 1, 0);
1492 glTranslatef (-0.5, -0.5, 0);
1493 glCallList(p->dlist);
1494 mi->polygon_count += p->polys;
1501 if (mi->fps_p) do_fps (mi);
1504 glXSwapBuffers(dpy, window);
1507 XSCREENSAVER_MODULE ("Jigsaw", jigsaw)