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"
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 /* #### glPolygonMode other than GL_FILL unimplemented */
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...";
1162 texture_string_metrics (jc->texfont, text, &e, 0, 0);
1164 h = e.ascent + e.descent;
1168 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1170 if (! jc->loading_dlist)
1172 GLfloat othick = jc->line_thickness;
1173 puzzle_piece P = { 0, };
1175 jc->loading_dlist = glGenLists (1);
1176 glNewList (jc->loading_dlist, GL_COMPILE);
1177 jc->line_thickness = 1;
1179 resolution_arg, thickness_arg,
1180 OUT, OUT, IN, OUT, True);
1181 jc->line_thickness = othick;
1185 glColor3f (0.2, 0.2, 0.4);
1190 get_position (jc->rot, &x, &y, &z, True);
1191 glRotatef (x * 360, 1, 0, 0);
1192 glRotatef (y * 360, 0, 1, 0);
1193 glRotatef (z * 360, 0, 0, 1);
1195 glTranslatef (-0.5, -0.5, 0);
1196 glCallList (jc->loading_dlist);
1200 glColor3f (0.7, 0.7, 1);
1203 glMatrixMode(GL_PROJECTION);
1207 glMatrixMode(GL_MODELVIEW);
1212 double rot = current_device_rotation();
1213 glRotatef(rot, 0, 0, 1);
1214 if ((rot > 45 && rot < 135) ||
1215 (rot < -45 && rot > -135))
1217 GLfloat s = MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
1218 glScalef (s, 1/s, 1);
1222 glOrtho(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi), -1, 1);
1223 glTranslatef ((MI_WIDTH(mi) - w) / 2,
1224 (MI_HEIGHT(mi) - h) / 2,
1226 glEnable (GL_TEXTURE_2D);
1227 glPolygonMode (GL_FRONT, GL_FILL);
1228 glDisable (GL_LIGHTING);
1229 glEnable (GL_BLEND);
1230 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1231 print_texture_string (jc->texfont, text);
1232 glEnable (GL_DEPTH_TEST);
1235 glMatrixMode(GL_PROJECTION);
1238 glMatrixMode(GL_MODELVIEW);
1243 animate (ModeInfo *mi)
1245 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1249 if (jc->button_down_p && jc->state != PUZZLE_LOADING_MSG)
1254 case PUZZLE_LOADING_MSG:
1259 case PUZZLE_LOADING:
1260 if (!jc->puzzle) break; /* still loading */
1261 jc->tick_speed = slow;
1264 begin_scatter (mi, True);
1266 jc->state = PUZZLE_UNSCATTER;
1267 if (debug_p) fprintf (stderr, "%s: unscattering\n", progname);
1270 case PUZZLE_UNSCATTER:
1271 jc->tick_speed = slow;
1276 jc->state = PUZZLE_SOLVE;
1277 if (debug_p) fprintf (stderr, "%s: solving\n", progname);
1282 jc->tick_speed = fast;
1288 if (debug_p) fprintf (stderr, "%s: solved!\n", progname);
1289 begin_scatter (mi, False);
1290 jc->state = PUZZLE_SCATTER;
1292 if (debug_p) fprintf (stderr, "%s: scattering\n", progname);
1296 move_one_piece (mi);
1302 case PUZZLE_SCATTER:
1303 jc->tick_speed = slow;
1306 free_puzzle_grid (jc);
1308 jc->state = PUZZLE_LOADING;
1310 if (debug_p) fprintf (stderr, "%s: loading\n", progname);
1320 /* Window management, etc
1323 reshape_jigsaw (ModeInfo *mi, int width, int height)
1325 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1326 GLfloat h = (GLfloat) height / (GLfloat) width;
1328 glViewport (0, 0, (GLint) width, (GLint) height);
1330 glMatrixMode(GL_PROJECTION);
1332 gluPerspective (30.0, 1/h, 1.0, 100.0);
1334 glMatrixMode(GL_MODELVIEW);
1336 gluLookAt( 0.0, 0.0, 30.0,
1340 glClear(GL_COLOR_BUFFER_BIT);
1342 jc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
1347 jigsaw_handle_event (ModeInfo *mi, XEvent *event)
1349 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1351 if (gltrackball_event_handler (event, jc->trackball,
1352 MI_WIDTH (mi), MI_HEIGHT (mi),
1353 &jc->button_down_p))
1355 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
1357 begin_scatter (mi, False);
1358 jc->state = PUZZLE_SCATTER;
1367 init_jigsaw (ModeInfo *mi)
1369 jigsaw_configuration *jc;
1370 int wire = MI_IS_WIREFRAME(mi);
1373 sps = (jigsaw_configuration *)
1374 calloc (MI_NUM_SCREENS(mi), sizeof (jigsaw_configuration));
1376 fprintf(stderr, "%s: out of memory\n", progname);
1380 jc = &sps[MI_SCREEN(mi)];
1381 jc->glx_context = init_GL(mi);
1383 reshape_jigsaw (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1387 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
1388 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
1389 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
1390 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
1392 glLightfv(GL_LIGHT0, GL_POSITION, pos);
1393 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
1394 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
1395 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
1398 jc->trackball = gltrackball_init (False);
1399 jc->rot = make_rotator (0, 0, 0, 0, speed * 0.002, True);
1400 jc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
1402 jc->state = PUZZLE_LOADING_MSG;
1404 resolution_arg /= complexity_arg;
1407 /* If it's not even, we get crosses. */
1408 if (resolution_arg & 1)
1410 # endif /* !HAVE_TESS */
1413 make_puzzle_grid (mi);
1420 draw_jigsaw (ModeInfo *mi)
1422 jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1423 Display *dpy = MI_DISPLAY(mi);
1424 Window window = MI_WINDOW(mi);
1425 int wire = MI_IS_WIREFRAME(mi);
1427 if (!jc->glx_context)
1430 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(jc->glx_context));
1432 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1434 mi->polygon_count = 0;
1437 glRotatef(current_device_rotation(), 0, 0, 1);
1438 gltrackball_rotate (jc->trackball);
1442 if (wobble_p && jc->puzzle)
1446 get_position (jc->rot, &x, &y, &z, !jc->button_down_p);
1447 x = 1; /* always lean back */
1448 glRotatef (max/2 - x*max, 1, 0, 0);
1449 glRotatef (max/2 - z*max, 0, 1, 0);
1454 GLfloat s = 14.0 / jc->puzzle_height;
1457 glEnable(GL_CULL_FACE);
1458 glEnable(GL_DEPTH_TEST);
1459 glEnable(GL_NORMALIZE);
1460 glEnable(GL_LINE_SMOOTH);
1463 glTranslatef (-jc->puzzle_width / 2.0, -jc->puzzle_height / 2.0, 0);
1467 glEnable (GL_TEXTURE_2D);
1468 glEnable (GL_BLEND);
1469 glEnable (GL_LIGHTING);
1470 glEnable (GL_LIGHT0);
1471 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1474 for (y = 0; y < jc->puzzle_height; y++)
1475 for (x = 0; x < jc->puzzle_width; x++)
1477 puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
1479 glTranslatef (p->current.x, p->current.y, p->current.z);
1480 glTranslatef (0.5, 0.5, 0);
1481 glRotatef (p->current.r, 0, 0, 1);
1482 glRotatef (p->tilt, 0, 1, 0);
1483 glTranslatef (-0.5, -0.5, 0);
1484 glCallList(p->dlist);
1485 mi->polygon_count += p->polys;
1492 if (mi->fps_p) do_fps (mi);
1495 glXSwapBuffers(dpy, window);
1498 XSCREENSAVER_MODULE ("Jigsaw", jigsaw)