From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / glx / klein.c
1 /* klein --- Shows a Klein bottle that rotates in 4d or on which you
2    can walk */
3
4 #if 0
5 static const char sccsid[] = "@(#)klein.c  1.1 08/10/04 xlockmore";
6 #endif
7
8 /* Copyright (c) 2005-2014 Carsten Steger <carsten@mirsanmir.org>. */
9
10 /*
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * REVISION HISTORY:
24  * C. Steger - 08/10/04: Initial version
25  * C. Steger - 09/08/03: Changes to the parameter handling
26  * C. Steger - 13/12/25: Added the squeezed torus Klein bottle
27  * C. Steger - 14/10/03: Moved the curlicue texture to curlicue.h
28  */
29
30 /*
31  * This program shows three different Klein bottles in 4d: the figure-8 Klein
32  * bottle, the squeezed torus Klein bottle, or the Lawson Klein bottle.  You
33  * can walk on the Klein bottle, see it turn in 4d, or walk on it while it
34  * turns in 4d.  The figure-8 Klein bottle is well known in its 3d form.  The
35  * 4d form used in this program is an extension of the 3d form to 4d that
36  * does not intersect itself in 4d (which can be seen in the depth colors
37  * mode).  The squeezed torus Klein bottle also does not intersect itself in
38  * 4d (which can be seen in the depth colors mode).  The Lawson Klein bottle,
39  * on the other hand, does intersect itself in 4d.  Its primary use is that
40  * it has a nice appearance for walking and for turning in 3d.  The Klein
41  * bottle is a non-orientable surface.  To make this apparent, the two-sided
42  * color mode can be used.  Alternatively, orientation markers (curling
43  * arrows) can be drawn as a texture map on the surface of the Klein bottle.
44  * While walking on the Klein bottle, you will notice that the orientation
45  * of the curling arrows changes (which it must because the Klein bottle is
46  * non-orientable).  The program projects the 4d Klein bottle to 3d using
47  * either a perspective or an orthographic projection.  Which of the two
48  * alternatives looks more appealing depends on the viewing mode and the
49  * Klein bottle.  For example, the Lawson Klein bottle looks nicest when
50  * projected perspectively.  The figure-8 Klein bottle, on the other
51  * hand, looks nicer while walking when projected orthographically from 4d.
52  * For the squeezed torus Klein bottle, both projection modes give equally
53  * acceptable projections.  The projected Klein bottle can then be projected
54  * to the screen either perspectively or orthographically.  When using the
55  * walking modes, perspective projection to the screen should be used.  There
56  * are three display modes for the Klein bottle: mesh (wireframe), solid, or
57  * transparent.  Furthermore, the appearance of the Klein bottle can be as
58  * a solid object or as a set of see-through bands.  Finally, the colors
59  * with with the Klein bottle is drawn can be set to two-sided, rainbow, or
60  * depth.  In the first case, the Klein bottle is drawn with red on one
61  * "side" and green on the "other side".  Of course, the Klein bottle only
62  * has one side, so the color jumps from red to green along a curve on the
63  * surface of the Klein bottle.  This mode enables you to see that the Klein
64  * bottle is non-orientable.  The second mode draws the Klein bottle with
65  * fully saturated rainbow colors.  This gives a very nice effect when
66  * combined with the see-through bands mode or with the orientation markers
67  * drawn.  The third mode draws the Klein bottle with colors that are chosen
68  * according to the 4d "depth" of the points.  This mode enables you to see
69  * that the figure-8 and squeezed torus Klein bottles do not intersect
70  * themselves in 4d, while the Lawson Klein bottle does intersect itself.
71  * The rotation speed for each of the six planes around which the Klein
72  * bottle rotates can be chosen.  For the walk-and-turn more, only the
73  * rotation speeds around the true 4d planes are used (the xy, xz, and yz
74  * planes).  Furthermore, in the walking modes the walking direction in the
75  * 2d base square of the Klein bottle and the walking speed can be chosen.
76  * This program is somewhat inspired by Thomas Banchoff's book "Beyond the
77  * Third Dimension: Geometry, Computer Graphics, and Higher Dimensions",
78  * Scientific American Library, 1990.
79  */
80
81 #include "curlicue.h"
82
83 #ifndef M_PI
84 #define M_PI 3.14159265358979323846
85 #endif
86
87 #define KLEIN_BOTTLE_FIGURE_8       0
88 #define KLEIN_BOTTLE_SQUEEZED_TORUS 1
89 #define KLEIN_BOTTLE_LAWSON         2
90 #define NUM_KLEIN_BOTTLES           3
91
92 #define DISP_WIREFRAME              0
93 #define DISP_SURFACE                1
94 #define DISP_TRANSPARENT            2
95 #define NUM_DISPLAY_MODES           3
96
97 #define APPEARANCE_SOLID            0
98 #define APPEARANCE_BANDS            1
99 #define NUM_APPEARANCES             2
100
101 #define COLORS_TWOSIDED             0
102 #define COLORS_RAINBOW              1
103 #define COLORS_DEPTH                2
104 #define NUM_COLORS                  3
105
106 #define VIEW_WALK                   0
107 #define VIEW_TURN                   1
108 #define VIEW_WALKTURN               2
109 #define NUM_VIEW_MODES              3
110
111 #define DISP_3D_PERSPECTIVE         0
112 #define DISP_3D_ORTHOGRAPHIC        1
113 #define NUM_DISP_3D_MODES           2
114
115 #define DISP_4D_PERSPECTIVE         0
116 #define DISP_4D_ORTHOGRAPHIC        1
117 #define NUM_DISP_4D_MODES           2
118
119 #define DEF_KLEIN_BOTTLE            "random"
120 #define DEF_DISPLAY_MODE            "random"
121 #define DEF_APPEARANCE              "random"
122 #define DEF_COLORS                  "random"
123 #define DEF_VIEW_MODE               "random"
124 #define DEF_MARKS                   "False"
125 #define DEF_PROJECTION_3D           "random"
126 #define DEF_PROJECTION_4D           "random"
127 #define DEF_SPEEDWX                 "1.1"
128 #define DEF_SPEEDWY                 "1.3"
129 #define DEF_SPEEDWZ                 "1.5"
130 #define DEF_SPEEDXY                 "1.7"
131 #define DEF_SPEEDXZ                 "1.9"
132 #define DEF_SPEEDYZ                 "2.1"
133 #define DEF_WALK_DIRECTION          "7.0"
134 #define DEF_WALK_SPEED              "20.0"
135
136 #ifdef STANDALONE
137 # define DEFAULTS           "*delay:      10000 \n" \
138                             "*showFPS:    False \n" \
139
140 # define refresh_klein 0
141 # include "xlockmore.h"         /* from the xscreensaver distribution */
142 #else  /* !STANDALONE */
143 # include "xlock.h"             /* from the xlockmore distribution */
144 #endif /* !STANDALONE */
145
146 #ifdef USE_GL
147
148 #ifndef HAVE_JWXYZ
149 # include <X11/keysym.h>
150 #endif
151
152 #include "gltrackball.h"
153
154
155 #ifdef USE_MODULES
156 ModStruct   klein_description =
157 {"klein", "init_klein", "draw_klein", "release_klein",
158  "draw_klein", "change_klein", NULL, &klein_opts,
159  25000, 1, 1, 1, 1.0, 4, "",
160  "Rotate a Klein bottle in 4d or walk on it", 0, NULL};
161
162 #endif
163
164
165 static char *klein_bottle;
166 static int bottle_type;
167 static char *mode;
168 static int display_mode;
169 static char *appear;
170 static int appearance;
171 static char *color_mode;
172 static int colors;
173 static char *view_mode;
174 static int view;
175 static Bool marks;
176 static char *proj_3d;
177 static int projection_3d;
178 static char *proj_4d;
179 static int projection_4d;
180 static float speed_wx;
181 static float speed_wy;
182 static float speed_wz;
183 static float speed_xy;
184 static float speed_xz;
185 static float speed_yz;
186 static float walk_direction;
187 static float walk_speed;
188
189
190 static XrmOptionDescRec opts[] =
191 {
192   {"-klein-bottle",      ".kleinBottle",   XrmoptionSepArg, 0 },
193   {"-figure-8",          ".kleinBottle",   XrmoptionNoArg,  "figure-8" },
194   {"-squeezed-torus",    ".kleinBottle",   XrmoptionNoArg,  "squeezed-torus" },
195   {"-lawson",            ".kleinBottle",   XrmoptionNoArg,  "lawson" },
196   {"-mode",              ".displayMode",   XrmoptionSepArg, 0 },
197   {"-wireframe",         ".displayMode",   XrmoptionNoArg,  "wireframe" },
198   {"-surface",           ".displayMode",   XrmoptionNoArg,  "surface" },
199   {"-transparent",       ".displayMode",   XrmoptionNoArg,  "transparent" },
200   {"-appearance",        ".appearance",    XrmoptionSepArg, 0 },
201   {"-solid",             ".appearance",    XrmoptionNoArg,  "solid" },
202   {"-bands",             ".appearance",    XrmoptionNoArg,  "bands" },
203   {"-colors",            ".colors",        XrmoptionSepArg, 0 },
204   {"-twosided",          ".colors",        XrmoptionNoArg,  "two-sided" },
205   {"-rainbow",           ".colors",        XrmoptionNoArg,  "rainbow" },
206   {"-depth",             ".colors",        XrmoptionNoArg,  "depth" },
207   {"-view-mode",         ".viewMode",      XrmoptionSepArg, 0 },
208   {"-walk",              ".viewMode",      XrmoptionNoArg,  "walk" },
209   {"-turn",              ".viewMode",      XrmoptionNoArg,  "turn" },
210   {"-walk-turn",         ".viewMode",      XrmoptionNoArg,  "walk-turn" },
211   {"-orientation-marks", ".marks",         XrmoptionNoArg, "on"},
212   {"+orientation-marks", ".marks",         XrmoptionNoArg, "off"},
213   {"-projection-3d",     ".projection3d",  XrmoptionSepArg, 0 },
214   {"-perspective-3d",    ".projection3d",  XrmoptionNoArg,  "perspective" },
215   {"-orthographic-3d",   ".projection3d",  XrmoptionNoArg,  "orthographic" },
216   {"-projection-4d",     ".projection4d",  XrmoptionSepArg, 0 },
217   {"-perspective-4d",    ".projection4d",  XrmoptionNoArg,  "perspective" },
218   {"-orthographic-4d",   ".projection4d",  XrmoptionNoArg,  "orthographic" },
219   {"-speed-wx",          ".speedwx",       XrmoptionSepArg, 0 },
220   {"-speed-wy",          ".speedwy",       XrmoptionSepArg, 0 },
221   {"-speed-wz",          ".speedwz",       XrmoptionSepArg, 0 },
222   {"-speed-xy",          ".speedxy",       XrmoptionSepArg, 0 },
223   {"-speed-xz",          ".speedxz",       XrmoptionSepArg, 0 },
224   {"-speed-yz",          ".speedyz",       XrmoptionSepArg, 0 },
225   {"-walk-direction",    ".walkDirection", XrmoptionSepArg, 0 },
226   {"-walk-speed",        ".walkSpeed",     XrmoptionSepArg, 0 }
227 };
228
229 static argtype vars[] =
230 {
231   { &klein_bottle,   "kleinBottle",   "KleinBottle",   DEF_KLEIN_BOTTLE,   t_String },
232   { &mode,           "displayMode",   "DisplayMode",   DEF_DISPLAY_MODE,   t_String },
233   { &appear,         "appearance",    "Appearance",    DEF_APPEARANCE,     t_String },
234   { &color_mode,     "colors",        "Colors",        DEF_COLORS,         t_String },
235   { &view_mode,      "viewMode",      "ViewMode",      DEF_VIEW_MODE,      t_String },
236   { &marks,          "marks",         "Marks",         DEF_MARKS,          t_Bool },
237   { &proj_3d,        "projection3d",  "Projection3d",  DEF_PROJECTION_3D,  t_String },
238   { &proj_4d,        "projection4d",  "Projection4d",  DEF_PROJECTION_4D,  t_String },
239   { &speed_wx,       "speedwx",       "Speedwx",       DEF_SPEEDWX,        t_Float},
240   { &speed_wy,       "speedwy",       "Speedwy",       DEF_SPEEDWY,        t_Float},
241   { &speed_wz,       "speedwz",       "Speedwz",       DEF_SPEEDWZ,        t_Float},
242   { &speed_xy,       "speedxy",       "Speedxy",       DEF_SPEEDXY,        t_Float},
243   { &speed_xz,       "speedxz",       "Speedxz",       DEF_SPEEDXZ,        t_Float},
244   { &speed_yz,       "speedyz",       "Speedyz",       DEF_SPEEDYZ,        t_Float},
245   { &walk_direction, "walkDirection", "WalkDirection", DEF_WALK_DIRECTION, t_Float},
246   { &walk_speed,     "walkSpeed",     "WalkSpeed",     DEF_WALK_SPEED,     t_Float}
247 };
248
249 ENTRYPOINT ModeSpecOpt klein_opts =
250 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, NULL};
251
252
253 /* Radius of the figure-8 Klein bottle */
254 #define FIGURE_8_RADIUS 2.0
255
256 /* Radius of the squeezed torus Klein bottle */
257 #define SQUEEZED_TORUS_RADIUS 2.0
258
259 /* Offset by which we walk above the Klein bottle */
260 #define DELTAY  0.02
261
262 /* Number of subdivisions of the Klein bottle */
263 #define NUMU 128
264 #define NUMV 128
265
266 /* Number of subdivisions per band */
267 #define NUMB 8
268
269
270 typedef struct {
271   GLint      WindH, WindW;
272   GLXContext *glx_context;
273   /* 4D rotation angles */
274   float alpha, beta, delta, zeta, eta, theta;
275   /* Movement parameters */
276   float umove, vmove, dumove, dvmove;
277   int side;
278   /* The viewing offset in 4d */
279   float offset4d[4];
280   /* The viewing offset in 3d */
281   float offset3d[4];
282   /* The 4d coordinates of the Klein bottle and their derivatives */
283   float x[(NUMU+1)*(NUMV+1)][4];
284   float xu[(NUMU+1)*(NUMV+1)][4];
285   float xv[(NUMU+1)*(NUMV+1)][4];
286   float pp[(NUMU+1)*(NUMV+1)][3];
287   float pn[(NUMU+1)*(NUMV+1)][3];
288   /* The precomputed colors of the Klein bottle */
289   float col[(NUMU+1)*(NUMV+1)][4];
290   /* The precomputed texture coordinates of the Klein bottle */
291   float tex[(NUMU+1)*(NUMV+1)][2];
292   /* The "curlicue" texture */
293   GLuint tex_name;
294   /* Aspect ratio of the current window */
295   float aspect;
296   /* Trackball states */
297   trackball_state *trackballs[2];
298   int current_trackball;
299   Bool button_pressed;
300   /* A random factor to modify the rotation speeds */
301   float speed_scale;
302 } kleinstruct;
303
304 static kleinstruct *klein = (kleinstruct *) NULL;
305
306
307 /* Add a rotation around the wx-plane to the matrix m. */
308 static void rotatewx(float m[4][4], float phi)
309 {
310   float c, s, u, v;
311   int i;
312
313   phi *= M_PI/180.0;
314   c = cos(phi);
315   s = sin(phi);
316   for (i=0; i<4; i++)
317   {
318     u = m[i][1];
319     v = m[i][2];
320     m[i][1] = c*u+s*v;
321     m[i][2] = -s*u+c*v;
322   }
323 }
324
325
326 /* Add a rotation around the wy-plane to the matrix m. */
327 static void rotatewy(float m[4][4], float phi)
328 {
329   float c, s, u, v;
330   int i;
331
332   phi *= M_PI/180.0;
333   c = cos(phi);
334   s = sin(phi);
335   for (i=0; i<4; i++)
336   {
337     u = m[i][0];
338     v = m[i][2];
339     m[i][0] = c*u-s*v;
340     m[i][2] = s*u+c*v;
341   }
342 }
343
344
345 /* Add a rotation around the wz-plane to the matrix m. */
346 static void rotatewz(float m[4][4], float phi)
347 {
348   float c, s, u, v;
349   int i;
350
351   phi *= M_PI/180.0;
352   c = cos(phi);
353   s = sin(phi);
354   for (i=0; i<4; i++)
355   {
356     u = m[i][0];
357     v = m[i][1];
358     m[i][0] = c*u+s*v;
359     m[i][1] = -s*u+c*v;
360   }
361 }
362
363
364 /* Add a rotation around the xy-plane to the matrix m. */
365 static void rotatexy(float m[4][4], float phi)
366 {
367   float c, s, u, v;
368   int i;
369
370   phi *= M_PI/180.0;
371   c = cos(phi);
372   s = sin(phi);
373   for (i=0; i<4; i++)
374   {
375     u = m[i][2];
376     v = m[i][3];
377     m[i][2] = c*u+s*v;
378     m[i][3] = -s*u+c*v;
379   }
380 }
381
382
383 /* Add a rotation around the xz-plane to the matrix m. */
384 static void rotatexz(float m[4][4], float phi)
385 {
386   float c, s, u, v;
387   int i;
388
389   phi *= M_PI/180.0;
390   c = cos(phi);
391   s = sin(phi);
392   for (i=0; i<4; i++)
393   {
394     u = m[i][1];
395     v = m[i][3];
396     m[i][1] = c*u-s*v;
397     m[i][3] = s*u+c*v;
398   }
399 }
400
401
402 /* Add a rotation around the yz-plane to the matrix m. */
403 static void rotateyz(float m[4][4], float phi)
404 {
405   float c, s, u, v;
406   int i;
407
408   phi *= M_PI/180.0;
409   c = cos(phi);
410   s = sin(phi);
411   for (i=0; i<4; i++)
412   {
413     u = m[i][0];
414     v = m[i][3];
415     m[i][0] = c*u-s*v;
416     m[i][3] = s*u+c*v;
417   }
418 }
419
420
421 /* Compute the rotation matrix m from the rotation angles. */
422 static void rotateall(float al, float be, float de, float ze, float et,
423                       float th, float m[4][4])
424 {
425   int i, j;
426
427   for (i=0; i<4; i++)
428     for (j=0; j<4; j++)
429       m[i][j] = (i==j);
430   rotatewx(m,al);
431   rotatewy(m,be);
432   rotatewz(m,de);
433   rotatexy(m,ze);
434   rotatexz(m,et);
435   rotateyz(m,th);
436 }
437
438
439 /* Compute the rotation matrix m from the 4d rotation angles. */
440 static void rotateall4d(float ze, float et, float th, float m[4][4])
441 {
442   int i, j;
443
444   for (i=0; i<4; i++)
445     for (j=0; j<4; j++)
446       m[i][j] = (i==j);
447   rotatexy(m,ze);
448   rotatexz(m,et);
449   rotateyz(m,th);
450 }
451
452
453 /* Multiply two rotation matrices: o=m*n. */
454 static void mult_rotmat(float m[4][4], float n[4][4], float o[4][4])
455 {
456   int i, j, k;
457
458   for (i=0; i<4; i++)
459   {
460     for (j=0; j<4; j++)
461     {
462       o[i][j] = 0.0;
463       for (k=0; k<4; k++)
464         o[i][j] += m[i][k]*n[k][j];
465     }
466   }
467 }
468
469
470 /* Compute a 4D rotation matrix from two unit quaternions. */
471 static void quats_to_rotmat(float p[4], float q[4], float m[4][4])
472 {
473   double al, be, de, ze, et, th;
474   double r00, r01, r02, r12, r22;
475
476   r00 = 1.0-2.0*(p[1]*p[1]+p[2]*p[2]);
477   r01 = 2.0*(p[0]*p[1]+p[2]*p[3]);
478   r02 = 2.0*(p[2]*p[0]-p[1]*p[3]);
479   r12 = 2.0*(p[1]*p[2]+p[0]*p[3]);
480   r22 = 1.0-2.0*(p[1]*p[1]+p[0]*p[0]);
481
482   al = atan2(-r12,r22)*180.0/M_PI;
483   be = atan2(r02,sqrt(r00*r00+r01*r01))*180.0/M_PI;
484   de = atan2(-r01,r00)*180.0/M_PI;
485
486   r00 = 1.0-2.0*(q[1]*q[1]+q[2]*q[2]);
487   r01 = 2.0*(q[0]*q[1]+q[2]*q[3]);
488   r02 = 2.0*(q[2]*q[0]-q[1]*q[3]);
489   r12 = 2.0*(q[1]*q[2]+q[0]*q[3]);
490   r22 = 1.0-2.0*(q[1]*q[1]+q[0]*q[0]);
491
492   et = atan2(-r12,r22)*180.0/M_PI;
493   th = atan2(r02,sqrt(r00*r00+r01*r01))*180.0/M_PI;
494   ze = atan2(-r01,r00)*180.0/M_PI;
495
496   rotateall(al,be,de,ze,et,-th,m);
497 }
498
499
500 /* Compute a fully saturated and bright color based on an angle. */
501 static void color(double angle, float col[4])
502 {
503   int s;
504   double t;
505
506   if (colors == COLORS_TWOSIDED)
507     return;
508
509   if (angle >= 0.0)
510     angle = fmod(angle,2.0*M_PI);
511   else
512     angle = fmod(angle,-2.0*M_PI);
513   s = floor(angle/(M_PI/3));
514   t = angle/(M_PI/3)-s;
515   if (s >= 6)
516     s = 0;
517   switch (s)
518   {
519     case 0:
520       col[0] = 1.0;
521       col[1] = t;
522       col[2] = 0.0;
523       break;
524     case 1:
525       col[0] = 1.0-t;
526       col[1] = 1.0;
527       col[2] = 0.0;
528       break;
529     case 2:
530       col[0] = 0.0;
531       col[1] = 1.0;
532       col[2] = t;
533       break;
534     case 3:
535       col[0] = 0.0;
536       col[1] = 1.0-t;
537       col[2] = 1.0;
538       break;
539     case 4:
540       col[0] = t;
541       col[1] = 0.0;
542       col[2] = 1.0;
543       break;
544     case 5:
545       col[0] = 1.0;
546       col[1] = 0.0;
547       col[2] = 1.0-t;
548       break;
549   }
550   if (display_mode == DISP_TRANSPARENT)
551     col[3] = 0.7;
552   else
553     col[3] = 1.0;
554 }
555
556
557 /* Set up the figure-8 Klein bottle coordinates, colors, and texture. */
558 static void setup_figure8(ModeInfo *mi, double umin, double umax, double vmin,
559                           double vmax)
560 {
561   int i, j, k, l;
562   double u, v, ur, vr;
563   double cu, su, cv, sv, cv2, sv2, c2u, s2u;
564   kleinstruct *kb = &klein[MI_SCREEN(mi)];
565
566   ur = umax-umin;
567   vr = vmax-vmin;
568   for (i=0; i<=NUMU; i++)
569   {
570     for (j=0; j<=NUMV; j++)
571     {
572       k = i*(NUMV+1)+j;
573       u = -ur*j/NUMU+umin;
574       v = vr*i/NUMV+vmin;
575       if (colors == COLORS_DEPTH)
576         color((cos(u)+1.0)*M_PI*2.0/3.0,kb->col[k]);
577       else
578         color(v,kb->col[k]);
579       kb->tex[k][0] = -32*u/(2.0*M_PI);
580       kb->tex[k][1] = 32*v/(2.0*M_PI);
581       cu = cos(u);
582       su = sin(u);
583       cv = cos(v);
584       sv = sin(v);
585       cv2 = cos(0.5*v);
586       sv2 = sin(0.5*v);
587       c2u = cos(2.0*u);
588       s2u = sin(2.0*u);
589       kb->x[k][0] = (su*cv2-s2u*sv2+FIGURE_8_RADIUS)*cv;
590       kb->x[k][1] = (su*cv2-s2u*sv2+FIGURE_8_RADIUS)*sv;
591       kb->x[k][2] = su*sv2+s2u*cv2;
592       kb->x[k][3] = cu;
593       kb->xu[k][0] = (cu*cv2-2.0*c2u*sv2)*cv;
594       kb->xu[k][1] = (cu*cv2-2.0*c2u*sv2)*sv;
595       kb->xu[k][2] = cu*sv2+2.0*c2u*cv2;
596       kb->xu[k][3] = -su;
597       kb->xv[k][0] = ((-0.5*su*sv2-0.5*s2u*cv2)*cv-
598                       (su*cv2-s2u*sv2+FIGURE_8_RADIUS)*sv);
599       kb->xv[k][1] = ((-0.5*su*sv2-0.5*s2u*cv2)*sv+
600                       (su*cv2-s2u*sv2+FIGURE_8_RADIUS)*cv);
601       kb->xv[k][2] = 0.5*su*cv2-0.5*s2u*sv2;
602       kb->xv[k][3] = 0.0;
603       for (l=0; l<4; l++)
604       {
605         kb->x[k][l] /= FIGURE_8_RADIUS+1.25;
606         kb->xu[k][l] /= FIGURE_8_RADIUS+1.25;
607         kb->xv[k][l] /= FIGURE_8_RADIUS+1.25;
608       }
609     }
610   }
611 }
612
613
614 /* Set up the squeezed torus Klein bottle coordinates, colors, and texture. */
615 static void setup_squeezed_torus(ModeInfo *mi, double umin, double umax,
616                                  double vmin, double vmax)
617 {
618   int i, j, k, l;
619   double u, v, ur, vr;
620   double cu, su, cv, sv, cv2, sv2;
621   kleinstruct *kb = &klein[MI_SCREEN(mi)];
622
623   ur = umax-umin;
624   vr = vmax-vmin;
625   for (i=0; i<=NUMU; i++)
626   {
627     for (j=0; j<=NUMV; j++)
628     {
629       k = i*(NUMV+1)+j;
630       u = -ur*j/NUMU+umin;
631       v = vr*i/NUMV+vmin;
632       if (colors == COLORS_DEPTH)
633         color((sin(u)*sin(0.5*v)+1.0)*M_PI*2.0/3.0,kb->col[k]);
634       else
635         color(v,kb->col[k]);
636       kb->tex[k][0] = -32*u/(2.0*M_PI);
637       kb->tex[k][1] = 32*v/(2.0*M_PI);
638       cu = cos(u);
639       su = sin(u);
640       cv = cos(v);
641       sv = sin(v);
642       cv2 = cos(0.5*v);
643       sv2 = sin(0.5*v);
644       kb->x[k][0] = (SQUEEZED_TORUS_RADIUS+cu)*cv;
645       kb->x[k][1] = (SQUEEZED_TORUS_RADIUS+cu)*sv;
646       kb->x[k][2] = su*cv2;
647       kb->x[k][3] = su*sv2;
648       kb->xu[k][0] = -su*cv;
649       kb->xu[k][1] = -su*sv;
650       kb->xu[k][2] = cu*cv2;
651       kb->xu[k][3] = cu*sv2;
652       kb->xv[k][0] = -(SQUEEZED_TORUS_RADIUS+cu)*sv;
653       kb->xv[k][1] = (SQUEEZED_TORUS_RADIUS+cu)*cv;
654       kb->xv[k][2] = -0.5*su*sv2;
655       kb->xv[k][3] = 0.5*su*cv2;
656       for (l=0; l<4; l++)
657       {
658         kb->x[k][l] /= SQUEEZED_TORUS_RADIUS+1.25;
659         kb->xu[k][l] /= SQUEEZED_TORUS_RADIUS+1.25;
660         kb->xv[k][l] /= SQUEEZED_TORUS_RADIUS+1.25;
661       }
662     }
663   }
664 }
665
666
667 /* Set up the Lawson Klein bottle coordinates, colors, and texture. */
668 static void setup_lawson(ModeInfo *mi, double umin, double umax, double vmin,
669                          double vmax)
670 {
671   int i, j, k;
672   double u, v, ur, vr;
673   double cu, su, cv, sv, cv2, sv2;
674   kleinstruct *kb = &klein[MI_SCREEN(mi)];
675
676   ur = umax-umin;
677   vr = vmax-vmin;
678   for (i=0; i<=NUMV; i++)
679   {
680     for (j=0; j<=NUMU; j++)
681     {
682       k = i*(NUMU+1)+j;
683       u = -ur*j/NUMU+umin;
684       v = vr*i/NUMV+vmin;
685       if (colors == COLORS_DEPTH)
686         color((sin(u)*cos(0.5*v)+1.0)*M_PI*2.0/3.0,kb->col[k]);
687       else
688         color(v,kb->col[k]);
689       kb->tex[k][0] = -32*u/(2.0*M_PI);
690       kb->tex[k][1] = 32*v/(2.0*M_PI);
691       cu = cos(u);
692       su = sin(u);
693       cv = cos(v);
694       sv = sin(v);
695       cv2 = cos(0.5*v);
696       sv2 = sin(0.5*v);
697       kb->x[k][0] = cu*cv;
698       kb->x[k][1] = cu*sv;
699       kb->x[k][2] = su*sv2;
700       kb->x[k][3] = su*cv2;
701       kb->xu[k][0] = -su*cv;
702       kb->xu[k][1] = -su*sv;
703       kb->xu[k][2] = cu*sv2;
704       kb->xu[k][3] = cu*cv2;
705       kb->xv[k][0] = -cu*sv;
706       kb->xv[k][1] = cu*cv;
707       kb->xv[k][2] = su*cv2*0.5;
708       kb->xv[k][3] = -su*sv2*0.5;
709     }
710   }
711 }
712
713
714 /* Draw a figure-8 Klein bottle projected into 3D. */
715 static int figure8(ModeInfo *mi, double umin, double umax, double vmin,
716                    double vmax)
717 {
718   int polys = 0;
719   static const GLfloat mat_diff_red[]         = { 1.0, 0.0, 0.0, 1.0 };
720   static const GLfloat mat_diff_green[]       = { 0.0, 1.0, 0.0, 1.0 };
721   static const GLfloat mat_diff_trans_red[]   = { 1.0, 0.0, 0.0, 0.7 };
722   static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
723   float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[4][4];
724   int i, j, k, l, m, o;
725   double u, v;
726   double xx[4], xxu[4], xxv[4], y[4], yu[4], yv[4];
727   double q, r, s, t;
728   double cu, su, cv, sv, cv2, sv2, c2u, s2u;
729   float q1[4], q2[4], r1[4][4], r2[4][4];
730   kleinstruct *kb = &klein[MI_SCREEN(mi)];
731
732   if (view == VIEW_WALK || view == VIEW_WALKTURN)
733   {
734     /* Compute the rotation that rotates the Klein bottle in 4D without the
735        trackball rotations. */
736     rotateall4d(kb->zeta,kb->eta,kb->theta,mat);
737
738     u = kb->umove;
739     v = kb->vmove;
740     cu = cos(u);
741     su = sin(u);
742     cv = cos(v);
743     sv = sin(v);
744     cv2 = cos(0.5*v);
745     sv2 = sin(0.5*v);
746     c2u = cos(2.0*u);
747     s2u = sin(2.0*u);
748     xx[0] = (su*cv2-s2u*sv2+FIGURE_8_RADIUS)*cv;
749     xx[1] = (su*cv2-s2u*sv2+FIGURE_8_RADIUS)*sv;
750     xx[2] = su*sv2+s2u*cv2;
751     xx[3] = cu;
752     xxu[0] = (cu*cv2-2.0*c2u*sv2)*cv;
753     xxu[1] = (cu*cv2-2.0*c2u*sv2)*sv;
754     xxu[2] = cu*sv2+2.0*c2u*cv2;
755     xxu[3] = -su;
756     xxv[0] = ((-0.5*su*sv2-0.5*s2u*cv2)*cv-
757               (su*cv2-s2u*sv2+FIGURE_8_RADIUS)*sv);
758     xxv[1] = ((-0.5*su*sv2-0.5*s2u*cv2)*sv+
759               (su*cv2-s2u*sv2+FIGURE_8_RADIUS)*cv);
760     xxv[2] = 0.5*su*cv2-0.5*s2u*sv2;
761     xxv[3] = 0.0;
762     for (l=0; l<4; l++)
763     {
764       xx[l] /= FIGURE_8_RADIUS+1.25;
765       xxu[l] /= FIGURE_8_RADIUS+1.25;
766       xxv[l] /= FIGURE_8_RADIUS+1.25;
767     }
768     for (l=0; l<4; l++)
769     {
770       y[l] = (mat[l][0]*xx[0]+mat[l][1]*xx[1]+
771               mat[l][2]*xx[2]+mat[l][3]*xx[3]);
772       yu[l] = (mat[l][0]*xxu[0]+mat[l][1]*xxu[1]+
773                mat[l][2]*xxu[2]+mat[l][3]*xxu[3]);
774       yv[l] = (mat[l][0]*xxv[0]+mat[l][1]*xxv[1]+
775                mat[l][2]*xxv[2]+mat[l][3]*xxv[3]);
776     }
777     if (projection_4d == DISP_4D_ORTHOGRAPHIC)
778     {
779       for (l=0; l<3; l++)
780       {
781         p[l] = y[l]+kb->offset4d[l];
782         pu[l] = yu[l];
783         pv[l] = yv[l];
784       }
785     }
786     else
787     {
788       s = y[3]+kb->offset4d[3];
789       q = 1.0/s;
790       t = q*q;
791       for (l=0; l<3; l++)
792       {
793         r = y[l]+kb->offset4d[l];
794         p[l] = r*q;
795         pu[l] = (yu[l]*s-r*yu[3])*t;
796         pv[l] = (yv[l]*s-r*yv[3])*t;
797       }
798     }
799     n[0] = pu[1]*pv[2]-pu[2]*pv[1];
800     n[1] = pu[2]*pv[0]-pu[0]*pv[2];
801     n[2] = pu[0]*pv[1]-pu[1]*pv[0];
802     t = 1.0/(kb->side*4.0*sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]));
803     n[0] *= t;
804     n[1] *= t;
805     n[2] *= t;
806     pm[0] = pu[0]*kb->dumove+pv[0]*kb->dvmove;
807     pm[1] = pu[1]*kb->dumove+pv[1]*kb->dvmove;
808     pm[2] = pu[2]*kb->dumove+pv[2]*kb->dvmove;
809     t = 1.0/(4.0*sqrt(pm[0]*pm[0]+pm[1]*pm[1]+pm[2]*pm[2]));
810     pm[0] *= t;
811     pm[1] *= t;
812     pm[2] *= t;
813     b[0] = n[1]*pm[2]-n[2]*pm[1];
814     b[1] = n[2]*pm[0]-n[0]*pm[2];
815     b[2] = n[0]*pm[1]-n[1]*pm[0];
816     t = 1.0/(4.0*sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]));
817     b[0] *= t;
818     b[1] *= t;
819     b[2] *= t;
820
821     /* Compute alpha, beta, delta from the three basis vectors.
822            |  -b[0]  -b[1]  -b[2] |
823        m = |   n[0]   n[1]   n[2] |
824            | -pm[0] -pm[1] -pm[2] |
825     */
826     kb->alpha = atan2(-n[2],-pm[2])*180/M_PI;
827     kb->beta = atan2(-b[2],sqrt(b[0]*b[0]+b[1]*b[1]))*180/M_PI;
828     kb->delta = atan2(b[1],-b[0])*180/M_PI;
829
830     /* Compute the rotation that rotates the Klein bottle in 4D. */
831     rotateall(kb->alpha,kb->beta,kb->delta,kb->zeta,kb->eta,kb->theta,mat);
832
833     u = kb->umove;
834     v = kb->vmove;
835     cu = cos(u);
836     su = sin(u);
837     cv = cos(v);
838     sv = sin(v);
839     cv2 = cos(0.5*v);
840     sv2 = sin(0.5*v);
841     /*c2u = cos(2.0*u);*/
842     s2u = sin(2.0*u);
843     xx[0] = (su*cv2-s2u*sv2+FIGURE_8_RADIUS)*cv;
844     xx[1] = (su*cv2-s2u*sv2+FIGURE_8_RADIUS)*sv;
845     xx[2] = su*sv2+s2u*cv2;
846     xx[3] = cu;
847     for (l=0; l<4; l++)
848       xx[l] /= FIGURE_8_RADIUS+1.25;
849     for (l=0; l<4; l++)
850     {
851       r = 0.0;
852       for (m=0; m<4; m++)
853         r += mat[l][m]*xx[m];
854       y[l] = r;
855     }
856     if (projection_4d == DISP_4D_ORTHOGRAPHIC)
857     {
858       for (l=0; l<3; l++)
859         p[l] = y[l]+kb->offset4d[l];
860     }
861     else
862     {
863       s = y[3]+kb->offset4d[3];
864       for (l=0; l<3; l++)
865         p[l] = (y[l]+kb->offset4d[l])/s;
866     }
867
868     kb->offset3d[0] = -p[0];
869     kb->offset3d[1] = -p[1]-DELTAY;
870     kb->offset3d[2] = -p[2];
871   }
872   else
873   {
874     /* Compute the rotation that rotates the Klein bottle in 4D, including
875        the trackball rotations. */
876     rotateall(kb->alpha,kb->beta,kb->delta,kb->zeta,kb->eta,kb->theta,r1);
877
878     gltrackball_get_quaternion(kb->trackballs[0],q1);
879     gltrackball_get_quaternion(kb->trackballs[1],q2);
880     quats_to_rotmat(q1,q2,r2);
881
882     mult_rotmat(r2,r1,mat);
883   }
884
885   /* Project the points from 4D to 3D. */
886   for (i=0; i<=NUMU; i++)
887   {
888     for (j=0; j<=NUMV; j++)
889     {
890       o = i*(NUMV+1)+j;
891       for (l=0; l<4; l++)
892       {
893         y[l] = (mat[l][0]*kb->x[o][0]+mat[l][1]*kb->x[o][1]+
894                 mat[l][2]*kb->x[o][2]+mat[l][3]*kb->x[o][3]);
895         yu[l] = (mat[l][0]*kb->xu[o][0]+mat[l][1]*kb->xu[o][1]+
896                  mat[l][2]*kb->xu[o][2]+mat[l][3]*kb->xu[o][3]);
897         yv[l] = (mat[l][0]*kb->xv[o][0]+mat[l][1]*kb->xv[o][1]+
898                  mat[l][2]*kb->xv[o][2]+mat[l][3]*kb->xv[o][3]);
899       }
900       if (projection_4d == DISP_4D_ORTHOGRAPHIC)
901       {
902         for (l=0; l<3; l++)
903         {
904           kb->pp[o][l] = (y[l]+kb->offset4d[l])+kb->offset3d[l];
905           pu[l] = yu[l];
906           pv[l] = yv[l];
907         }
908       }
909       else
910       {
911         s = y[3]+kb->offset4d[3];
912         q = 1.0/s;
913         t = q*q;
914         for (l=0; l<3; l++)
915         {
916           r = y[l]+kb->offset4d[l];
917           kb->pp[o][l] = r*q+kb->offset3d[l];
918           pu[l] = (yu[l]*s-r*yu[3])*t;
919           pv[l] = (yv[l]*s-r*yv[3])*t;
920         }
921       }
922       kb->pn[o][0] = pu[1]*pv[2]-pu[2]*pv[1];
923       kb->pn[o][1] = pu[2]*pv[0]-pu[0]*pv[2];
924       kb->pn[o][2] = pu[0]*pv[1]-pu[1]*pv[0];
925       t = 1.0/sqrt(kb->pn[o][0]*kb->pn[o][0]+kb->pn[o][1]*kb->pn[o][1]+
926                    kb->pn[o][2]*kb->pn[o][2]);
927       kb->pn[o][0] *= t;
928       kb->pn[o][1] *= t;
929       kb->pn[o][2] *= t;
930     }
931   }
932
933   if (colors == COLORS_TWOSIDED)
934   {
935     glColor3fv(mat_diff_red);
936     if (display_mode == DISP_TRANSPARENT)
937     {
938       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
939       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
940     }
941     else
942     {
943       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
944       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
945     }
946   }
947   glBindTexture(GL_TEXTURE_2D,kb->tex_name);
948
949   for (i=0; i<NUMU; i++)
950   {
951     if (appearance == APPEARANCE_BANDS && ((i & (NUMB-1)) >= NUMB/2))
952       continue;
953     if (display_mode == DISP_WIREFRAME)
954       glBegin(GL_QUAD_STRIP);
955     else
956       glBegin(GL_TRIANGLE_STRIP);
957     for (j=0; j<=NUMV; j++)
958     {
959       for (k=0; k<=1; k++)
960       {
961         l = (i+k);
962         m = j;
963         o = l*(NUMV+1)+m;
964         glNormal3fv(kb->pn[o]);
965         glTexCoord2fv(kb->tex[o]);
966         if (colors != COLORS_TWOSIDED)
967         {
968           glColor3fv(kb->col[o]);
969           glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,kb->col[o]);
970         }
971         glVertex3fv(kb->pp[o]);
972         polys++;
973       }
974     }
975     glEnd();
976   }
977   polys /= 2;
978   return polys;
979 }
980
981
982 /* Draw a squeezed torus Klein bottle projected into 3D. */
983 static int squeezed_torus(ModeInfo *mi, double umin, double umax, double vmin,
984                           double vmax)
985 {
986   int polys = 0;
987   static const GLfloat mat_diff_red[]         = { 1.0, 0.0, 0.0, 1.0 };
988   static const GLfloat mat_diff_green[]       = { 0.0, 1.0, 0.0, 1.0 };
989   static const GLfloat mat_diff_trans_red[]   = { 1.0, 0.0, 0.0, 0.7 };
990   static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
991   float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[4][4];
992   int i, j, k, l, m, o;
993   double u, v;
994   double xx[4], xxu[4], xxv[4], y[4], yu[4], yv[4];
995   double q, r, s, t;
996   double cu, su, cv, sv, cv2, sv2;
997   float q1[4], q2[4], r1[4][4], r2[4][4];
998   kleinstruct *kb = &klein[MI_SCREEN(mi)];
999
1000   if (view == VIEW_WALK || view == VIEW_WALKTURN)
1001   {
1002     /* Compute the rotation that rotates the Klein bottle in 4D without the
1003        trackball rotations. */
1004     rotateall4d(kb->zeta,kb->eta,kb->theta,mat);
1005
1006     u = kb->umove;
1007     v = kb->vmove;
1008     cu = cos(u);
1009     su = sin(u);
1010     cv = cos(v);
1011     sv = sin(v);
1012     cv2 = cos(0.5*v);
1013     sv2 = sin(0.5*v);
1014     xx[0] = (SQUEEZED_TORUS_RADIUS+cu)*cv;
1015     xx[1] = (SQUEEZED_TORUS_RADIUS+cu)*sv;
1016     xx[2] = su*cv2;
1017     xx[3] = su*sv2;
1018     xxu[0] = -su*cv;
1019     xxu[1] = -su*sv;
1020     xxu[2] = cu*cv2;
1021     xxu[3] = cu*sv2;
1022     xxv[0] = -(SQUEEZED_TORUS_RADIUS+cu)*sv;
1023     xxv[1] = (SQUEEZED_TORUS_RADIUS+cu)*cv;
1024     xxv[2] = -0.5*su*sv2;
1025     xxv[3] = 0.5*su*cv2;
1026     for (l=0; l<4; l++)
1027     {
1028       xx[l] /= SQUEEZED_TORUS_RADIUS+1.25;
1029       xxu[l] /= SQUEEZED_TORUS_RADIUS+1.25;
1030       xxv[l] /= SQUEEZED_TORUS_RADIUS+1.25;
1031     }
1032     for (l=0; l<4; l++)
1033     {
1034       y[l] = (mat[l][0]*xx[0]+mat[l][1]*xx[1]+
1035               mat[l][2]*xx[2]+mat[l][3]*xx[3]);
1036       yu[l] = (mat[l][0]*xxu[0]+mat[l][1]*xxu[1]+
1037                mat[l][2]*xxu[2]+mat[l][3]*xxu[3]);
1038       yv[l] = (mat[l][0]*xxv[0]+mat[l][1]*xxv[1]+
1039                mat[l][2]*xxv[2]+mat[l][3]*xxv[3]);
1040     }
1041     if (projection_4d == DISP_4D_ORTHOGRAPHIC)
1042     {
1043       for (l=0; l<3; l++)
1044       {
1045         p[l] = y[l]+kb->offset4d[l];
1046         pu[l] = yu[l];
1047         pv[l] = yv[l];
1048       }
1049     }
1050     else
1051     {
1052       s = y[3]+kb->offset4d[3];
1053       q = 1.0/s;
1054       t = q*q;
1055       for (l=0; l<3; l++)
1056       {
1057         r = y[l]+kb->offset4d[l];
1058         p[l] = r*q;
1059         pu[l] = (yu[l]*s-r*yu[3])*t;
1060         pv[l] = (yv[l]*s-r*yv[3])*t;
1061       }
1062     }
1063     n[0] = pu[1]*pv[2]-pu[2]*pv[1];
1064     n[1] = pu[2]*pv[0]-pu[0]*pv[2];
1065     n[2] = pu[0]*pv[1]-pu[1]*pv[0];
1066     t = 1.0/(kb->side*4.0*sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]));
1067     n[0] *= t;
1068     n[1] *= t;
1069     n[2] *= t;
1070     pm[0] = pu[0]*kb->dumove+pv[0]*kb->dvmove;
1071     pm[1] = pu[1]*kb->dumove+pv[1]*kb->dvmove;
1072     pm[2] = pu[2]*kb->dumove+pv[2]*kb->dvmove;
1073     t = 1.0/(4.0*sqrt(pm[0]*pm[0]+pm[1]*pm[1]+pm[2]*pm[2]));
1074     pm[0] *= t;
1075     pm[1] *= t;
1076     pm[2] *= t;
1077     b[0] = n[1]*pm[2]-n[2]*pm[1];
1078     b[1] = n[2]*pm[0]-n[0]*pm[2];
1079     b[2] = n[0]*pm[1]-n[1]*pm[0];
1080     t = 1.0/(4.0*sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]));
1081     b[0] *= t;
1082     b[1] *= t;
1083     b[2] *= t;
1084
1085     /* Compute alpha, beta, delta from the three basis vectors.
1086            |  -b[0]  -b[1]  -b[2] |
1087        m = |   n[0]   n[1]   n[2] |
1088            | -pm[0] -pm[1] -pm[2] |
1089     */
1090     kb->alpha = atan2(-n[2],-pm[2])*180/M_PI;
1091     kb->beta = atan2(-b[2],sqrt(b[0]*b[0]+b[1]*b[1]))*180/M_PI;
1092     kb->delta = atan2(b[1],-b[0])*180/M_PI;
1093
1094     /* Compute the rotation that rotates the Klein bottle in 4D. */
1095     rotateall(kb->alpha,kb->beta,kb->delta,kb->zeta,kb->eta,kb->theta,mat);
1096
1097     u = kb->umove;
1098     v = kb->vmove;
1099     cu = cos(u);
1100     su = sin(u);
1101     cv = cos(v);
1102     sv = sin(v);
1103     cv2 = cos(0.5*v);
1104     sv2 = sin(0.5*v);
1105     xx[0] = (SQUEEZED_TORUS_RADIUS+cu)*cv;
1106     xx[1] = (SQUEEZED_TORUS_RADIUS+cu)*sv;
1107     xx[2] = su*cv2;
1108     xx[3] = su*sv2;
1109     for (l=0; l<4; l++)
1110       xx[l] /= SQUEEZED_TORUS_RADIUS+1.25;
1111     for (l=0; l<4; l++)
1112     {
1113       r = 0.0;
1114       for (m=0; m<4; m++)
1115         r += mat[l][m]*xx[m];
1116       y[l] = r;
1117     }
1118     if (projection_4d == DISP_4D_ORTHOGRAPHIC)
1119     {
1120       for (l=0; l<3; l++)
1121         p[l] = y[l]+kb->offset4d[l];
1122     }
1123     else
1124     {
1125       s = y[3]+kb->offset4d[3];
1126       for (l=0; l<3; l++)
1127         p[l] = (y[l]+kb->offset4d[l])/s;
1128     }
1129
1130     kb->offset3d[0] = -p[0];
1131     kb->offset3d[1] = -p[1]-DELTAY;
1132     kb->offset3d[2] = -p[2];
1133   }
1134   else
1135   {
1136     /* Compute the rotation that rotates the Klein bottle in 4D, including
1137        the trackball rotations. */
1138     rotateall(kb->alpha,kb->beta,kb->delta,kb->zeta,kb->eta,kb->theta,r1);
1139
1140     gltrackball_get_quaternion(kb->trackballs[0],q1);
1141     gltrackball_get_quaternion(kb->trackballs[1],q2);
1142     quats_to_rotmat(q1,q2,r2);
1143
1144     mult_rotmat(r2,r1,mat);
1145   }
1146
1147   /* Project the points from 4D to 3D. */
1148   for (i=0; i<=NUMU; i++)
1149   {
1150     for (j=0; j<=NUMV; j++)
1151     {
1152       o = i*(NUMV+1)+j;
1153       for (l=0; l<4; l++)
1154       {
1155         y[l] = (mat[l][0]*kb->x[o][0]+mat[l][1]*kb->x[o][1]+
1156                 mat[l][2]*kb->x[o][2]+mat[l][3]*kb->x[o][3]);
1157         yu[l] = (mat[l][0]*kb->xu[o][0]+mat[l][1]*kb->xu[o][1]+
1158                  mat[l][2]*kb->xu[o][2]+mat[l][3]*kb->xu[o][3]);
1159         yv[l] = (mat[l][0]*kb->xv[o][0]+mat[l][1]*kb->xv[o][1]+
1160                  mat[l][2]*kb->xv[o][2]+mat[l][3]*kb->xv[o][3]);
1161       }
1162       if (projection_4d == DISP_4D_ORTHOGRAPHIC)
1163       {
1164         for (l=0; l<3; l++)
1165         {
1166           kb->pp[o][l] = (y[l]+kb->offset4d[l])+kb->offset3d[l];
1167           pu[l] = yu[l];
1168           pv[l] = yv[l];
1169         }
1170       }
1171       else
1172       {
1173         s = y[3]+kb->offset4d[3];
1174         q = 1.0/s;
1175         t = q*q;
1176         for (l=0; l<3; l++)
1177         {
1178           r = y[l]+kb->offset4d[l];
1179           kb->pp[o][l] = r*q+kb->offset3d[l];
1180           pu[l] = (yu[l]*s-r*yu[3])*t;
1181           pv[l] = (yv[l]*s-r*yv[3])*t;
1182         }
1183       }
1184       kb->pn[o][0] = pu[1]*pv[2]-pu[2]*pv[1];
1185       kb->pn[o][1] = pu[2]*pv[0]-pu[0]*pv[2];
1186       kb->pn[o][2] = pu[0]*pv[1]-pu[1]*pv[0];
1187       t = 1.0/sqrt(kb->pn[o][0]*kb->pn[o][0]+kb->pn[o][1]*kb->pn[o][1]+
1188                    kb->pn[o][2]*kb->pn[o][2]);
1189       kb->pn[o][0] *= t;
1190       kb->pn[o][1] *= t;
1191       kb->pn[o][2] *= t;
1192     }
1193   }
1194
1195   if (colors == COLORS_TWOSIDED)
1196   {
1197     glColor3fv(mat_diff_red);
1198     if (display_mode == DISP_TRANSPARENT)
1199     {
1200       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
1201       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
1202     }
1203     else
1204     {
1205       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
1206       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
1207     }
1208   }
1209   glBindTexture(GL_TEXTURE_2D,kb->tex_name);
1210
1211   for (i=0; i<NUMU; i++)
1212   {
1213     if (appearance == APPEARANCE_BANDS && ((i & (NUMB-1)) >= NUMB/2))
1214       continue;
1215     if (display_mode == DISP_WIREFRAME)
1216       glBegin(GL_QUAD_STRIP);
1217     else
1218       glBegin(GL_TRIANGLE_STRIP);
1219     for (j=0; j<=NUMV; j++)
1220     {
1221       for (k=0; k<=1; k++)
1222       {
1223         l = (i+k);
1224         m = j;
1225         o = l*(NUMV+1)+m;
1226         glNormal3fv(kb->pn[o]);
1227         glTexCoord2fv(kb->tex[o]);
1228         if (colors != COLORS_TWOSIDED)
1229         {
1230           glColor3fv(kb->col[o]);
1231           glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,kb->col[o]);
1232         }
1233         glVertex3fv(kb->pp[o]);
1234         polys++;
1235       }
1236     }
1237     glEnd();
1238   }
1239   polys /= 2;
1240   return polys;
1241 }
1242
1243
1244 /* Draw a Lawson Klein bottle projected into 3D. */
1245 static int lawson(ModeInfo *mi, double umin, double umax, double vmin,
1246                   double vmax)
1247 {
1248   int polys = 0;
1249   static const GLfloat mat_diff_red[]         = { 1.0, 0.0, 0.0, 1.0 };
1250   static const GLfloat mat_diff_green[]       = { 0.0, 1.0, 0.0, 1.0 };
1251   static const GLfloat mat_diff_trans_red[]   = { 1.0, 0.0, 0.0, 0.7 };
1252   static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
1253   float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[4][4];
1254   int i, j, k, l, m, o;
1255   double u, v;
1256   double cu, su, cv, sv, cv2, sv2;
1257   double xx[4], xxu[4], xxv[4], y[4], yu[4], yv[4];
1258   double q, r, s, t;
1259   float q1[4], q2[4], r1[4][4], r2[4][4];
1260   kleinstruct *kb = &klein[MI_SCREEN(mi)];
1261
1262   if (view == VIEW_WALK || view == VIEW_WALKTURN)
1263   {
1264     /* Compute the rotation that rotates the Klein bottle in 4D without the
1265        trackball rotations. */
1266     rotateall4d(kb->zeta,kb->eta,kb->theta,mat);
1267
1268     u = kb->umove;
1269     v = kb->vmove;
1270     cu = cos(u);
1271     su = sin(u);
1272     cv = cos(v);
1273     sv = sin(v);
1274     cv2 = cos(0.5*v);
1275     sv2 = sin(0.5*v);
1276     xx[0] = cu*cv;
1277     xx[1] = cu*sv;
1278     xx[2] = su*sv2;
1279     xx[3] = su*cv2;
1280     xxu[0] = -su*cv;
1281     xxu[1] = -su*sv;
1282     xxu[2] = cu*sv2;
1283     xxu[3] = cu*cv2;
1284     xxv[0] = -cu*sv;
1285     xxv[1] = cu*cv;
1286     xxv[2] = su*cv2*0.5;
1287     xxv[3] = -su*sv2*0.5;
1288     for (l=0; l<4; l++)
1289     {
1290       y[l] = (mat[l][0]*xx[0]+mat[l][1]*xx[1]+
1291               mat[l][2]*xx[2]+mat[l][3]*xx[3]);
1292       yu[l] = (mat[l][0]*xxu[0]+mat[l][1]*xxu[1]+
1293                mat[l][2]*xxu[2]+mat[l][3]*xxu[3]);
1294       yv[l] = (mat[l][0]*xxv[0]+mat[l][1]*xxv[1]+
1295                mat[l][2]*xxv[2]+mat[l][3]*xxv[3]);
1296     }
1297     if (projection_4d == DISP_4D_ORTHOGRAPHIC)
1298     {
1299       for (l=0; l<3; l++)
1300       {
1301         p[l] = y[l]+kb->offset4d[l];
1302         pu[l] = yu[l];
1303         pv[l] = yv[l];
1304       }
1305     }
1306     else
1307     {
1308       s = y[3]+kb->offset4d[3];
1309       q = 1.0/s;
1310       t = q*q;
1311       for (l=0; l<3; l++)
1312       {
1313         r = y[l]+kb->offset4d[l];
1314         p[l] = r*q;
1315         pu[l] = (yu[l]*s-r*yu[3])*t;
1316         pv[l] = (yv[l]*s-r*yv[3])*t;
1317       }
1318     }
1319     n[0] = pu[1]*pv[2]-pu[2]*pv[1];
1320     n[1] = pu[2]*pv[0]-pu[0]*pv[2];
1321     n[2] = pu[0]*pv[1]-pu[1]*pv[0];
1322     t = 1.0/(kb->side*4.0*sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]));
1323     n[0] *= t;
1324     n[1] *= t;
1325     n[2] *= t;
1326     pm[0] = pu[0]*kb->dumove+pv[0]*kb->dvmove;
1327     pm[1] = pu[1]*kb->dumove+pv[1]*kb->dvmove;
1328     pm[2] = pu[2]*kb->dumove+pv[2]*kb->dvmove;
1329     t = 1.0/(4.0*sqrt(pm[0]*pm[0]+pm[1]*pm[1]+pm[2]*pm[2]));
1330     pm[0] *= t;
1331     pm[1] *= t;
1332     pm[2] *= t;
1333     b[0] = n[1]*pm[2]-n[2]*pm[1];
1334     b[1] = n[2]*pm[0]-n[0]*pm[2];
1335     b[2] = n[0]*pm[1]-n[1]*pm[0];
1336     t = 1.0/(4.0*sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]));
1337     b[0] *= t;
1338     b[1] *= t;
1339     b[2] *= t;
1340
1341     /* Compute alpha, beta, delta from the three basis vectors.
1342            |  -b[0]  -b[1]  -b[2] |
1343        m = |   n[0]   n[1]   n[2] |
1344            | -pm[0] -pm[1] -pm[2] |
1345     */
1346     kb->alpha = atan2(-n[2],-pm[2])*180/M_PI;
1347     kb->beta = atan2(-b[2],sqrt(b[0]*b[0]+b[1]*b[1]))*180/M_PI;
1348     kb->delta = atan2(b[1],-b[0])*180/M_PI;
1349
1350     /* Compute the rotation that rotates the Klein bottle in 4D. */
1351     rotateall(kb->alpha,kb->beta,kb->delta,kb->zeta,kb->eta,kb->theta,mat);
1352
1353     u = kb->umove;
1354     v = kb->vmove;
1355     cu = cos(u);
1356     su = sin(u);
1357     cv = cos(v);
1358     sv = sin(v);
1359     cv2 = cos(0.5*v);
1360     sv2 = sin(0.5*v);
1361     xx[0] = cu*cv;
1362     xx[1] = cu*sv;
1363     xx[2] = su*sv2;
1364     xx[3] = su*cv2;
1365     for (l=0; l<4; l++)
1366     {
1367       r = 0.0;
1368       for (m=0; m<4; m++)
1369         r += mat[l][m]*xx[m];
1370       y[l] = r;
1371     }
1372     if (projection_4d == DISP_4D_ORTHOGRAPHIC)
1373     {
1374       for (l=0; l<3; l++)
1375         p[l] = y[l]+kb->offset4d[l];
1376     }
1377     else
1378     {
1379       s = y[3]+kb->offset4d[3];
1380       for (l=0; l<3; l++)
1381         p[l] = (y[l]+kb->offset4d[l])/s;
1382     }
1383
1384     kb->offset3d[0] = -p[0];
1385     kb->offset3d[1] = -p[1]-DELTAY;
1386     kb->offset3d[2] = -p[2];
1387   }
1388   else
1389   {
1390     /* Compute the rotation that rotates the Klein bottle in 4D, including
1391        the trackball rotations. */
1392     rotateall(kb->alpha,kb->beta,kb->delta,kb->zeta,kb->eta,kb->theta,r1);
1393
1394     gltrackball_get_quaternion(kb->trackballs[0],q1);
1395     gltrackball_get_quaternion(kb->trackballs[1],q2);
1396     quats_to_rotmat(q1,q2,r2);
1397
1398     mult_rotmat(r2,r1,mat);
1399   }
1400
1401   /* Project the points from 4D to 3D. */
1402   for (i=0; i<=NUMV; i++)
1403   {
1404     for (j=0; j<=NUMU; j++)
1405     {
1406       o = i*(NUMU+1)+j;
1407       for (l=0; l<4; l++)
1408       {
1409         y[l] = (mat[l][0]*kb->x[o][0]+mat[l][1]*kb->x[o][1]+
1410                 mat[l][2]*kb->x[o][2]+mat[l][3]*kb->x[o][3]);
1411         yu[l] = (mat[l][0]*kb->xu[o][0]+mat[l][1]*kb->xu[o][1]+
1412                  mat[l][2]*kb->xu[o][2]+mat[l][3]*kb->xu[o][3]);
1413         yv[l] = (mat[l][0]*kb->xv[o][0]+mat[l][1]*kb->xv[o][1]+
1414                  mat[l][2]*kb->xv[o][2]+mat[l][3]*kb->xv[o][3]);
1415       }
1416       if (projection_4d == DISP_4D_ORTHOGRAPHIC)
1417       {
1418         for (l=0; l<3; l++)
1419         {
1420           kb->pp[o][l] = (y[l]+kb->offset4d[l])+kb->offset3d[l];
1421           pu[l] = yu[l];
1422           pv[l] = yv[l];
1423         }
1424       }
1425       else
1426       {
1427         s = y[3]+kb->offset4d[3];
1428         q = 1.0/s;
1429         t = q*q;
1430         for (l=0; l<3; l++)
1431         {
1432           r = y[l]+kb->offset4d[l];
1433           kb->pp[o][l] = r*q+kb->offset3d[l];
1434           pu[l] = (yu[l]*s-r*yu[3])*t;
1435           pv[l] = (yv[l]*s-r*yv[3])*t;
1436         }
1437       }
1438       kb->pn[o][0] = pu[1]*pv[2]-pu[2]*pv[1];
1439       kb->pn[o][1] = pu[2]*pv[0]-pu[0]*pv[2];
1440       kb->pn[o][2] = pu[0]*pv[1]-pu[1]*pv[0];
1441       t = 1.0/sqrt(kb->pn[o][0]*kb->pn[o][0]+kb->pn[o][1]*kb->pn[o][1]+
1442                    kb->pn[o][2]*kb->pn[o][2]);
1443       kb->pn[o][0] *= t;
1444       kb->pn[o][1] *= t;
1445       kb->pn[o][2] *= t;
1446     }
1447   }
1448
1449   if (colors == COLORS_TWOSIDED)
1450   {
1451     glColor3fv(mat_diff_red);
1452     if (display_mode == DISP_TRANSPARENT)
1453     {
1454       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
1455       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
1456     }
1457     else
1458     {
1459       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
1460       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
1461     }
1462   }
1463   glBindTexture(GL_TEXTURE_2D,kb->tex_name);
1464
1465   for (i=0; i<NUMV; i++)
1466   {
1467     if (appearance == APPEARANCE_BANDS && ((i & (NUMB-1)) >= NUMB/2))
1468       continue;
1469     if (display_mode == DISP_WIREFRAME)
1470       glBegin(GL_QUAD_STRIP);
1471     else
1472       glBegin(GL_TRIANGLE_STRIP);
1473     for (j=0; j<=NUMU; j++)
1474     {
1475       for (k=0; k<=1; k++)
1476       {
1477         l = (i+k);
1478         m = j;
1479         o = l*(NUMU+1)+m;
1480         glNormal3fv(kb->pn[o]);
1481         glTexCoord2fv(kb->tex[o]);
1482         if (colors != COLORS_TWOSIDED)
1483         {
1484           glColor3fv(kb->col[o]);
1485           glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,kb->col[o]);
1486         }
1487         glVertex3fv(kb->pp[o]);
1488         polys++;
1489       }
1490     }
1491     glEnd();
1492   }
1493   polys /= 2;
1494   return polys;
1495 }
1496
1497
1498 /* Generate a texture image that shows the orientation reversal. */
1499 static void gen_texture(ModeInfo *mi)
1500 {
1501   kleinstruct *kb = &klein[MI_SCREEN(mi)];
1502
1503   glGenTextures(1,&kb->tex_name);
1504   glBindTexture(GL_TEXTURE_2D,kb->tex_name);
1505   glPixelStorei(GL_UNPACK_ALIGNMENT,1);
1506   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
1507   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
1508   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
1509   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
1510   glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
1511   glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,TEX_DIMENSION,TEX_DIMENSION,0,
1512                GL_LUMINANCE,GL_UNSIGNED_BYTE,texture);
1513 }
1514
1515
1516 static void init(ModeInfo *mi)
1517 {
1518   static const GLfloat light_ambient[]  = { 0.0, 0.0, 0.0, 1.0 };
1519   static const GLfloat light_diffuse[]  = { 1.0, 1.0, 1.0, 1.0 };
1520   static const GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
1521   static const GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
1522   static const GLfloat mat_specular[]   = { 1.0, 1.0, 1.0, 1.0 };
1523   kleinstruct *kb = &klein[MI_SCREEN(mi)];
1524
1525   if (walk_speed == 0.0)
1526     walk_speed = 20.0;
1527
1528   if (view == VIEW_TURN)
1529   {
1530     kb->alpha = frand(360.0);
1531     kb->beta = frand(360.0);
1532     kb->delta = frand(360.0);
1533   }
1534   else
1535   {
1536     kb->alpha = 0.0;
1537     kb->beta = 0.0;
1538     kb->delta = 0.0;
1539   }
1540   kb->zeta = 0.0;
1541   if (bottle_type == KLEIN_BOTTLE_FIGURE_8 ||
1542       bottle_type == KLEIN_BOTTLE_SQUEEZED_TORUS)
1543     kb->eta = 0.0;
1544   else
1545     kb->eta = 45.0;
1546   kb->theta = 0.0;
1547   kb->umove = frand(2.0*M_PI);
1548   kb->vmove = frand(2.0*M_PI);
1549   kb->dumove = 0.0;
1550   kb->dvmove = 0.0;
1551   kb->side = 1;
1552
1553   if (bottle_type == KLEIN_BOTTLE_FIGURE_8)
1554   {
1555     kb->offset4d[0] = 0.0;
1556     kb->offset4d[1] = 0.0;
1557     kb->offset4d[2] = 0.0;
1558     kb->offset4d[3] = 1.5;
1559     kb->offset3d[0] = 0.0;
1560     kb->offset3d[1] = 0.0;
1561     if (projection_4d == DISP_4D_ORTHOGRAPHIC)
1562       kb->offset3d[2] = -2.1;
1563     else
1564       kb->offset3d[2] = -1.9;
1565     kb->offset3d[3] = 0.0;
1566   }
1567   else if (bottle_type == KLEIN_BOTTLE_SQUEEZED_TORUS)
1568   {
1569     kb->offset4d[0] = 0.0;
1570     kb->offset4d[1] = 0.0;
1571     kb->offset4d[2] = 0.0;
1572     kb->offset4d[3] = 1.4;
1573     kb->offset3d[0] = 0.0;
1574     kb->offset3d[1] = 0.0;
1575     kb->offset3d[2] = -2.0;
1576     kb->offset3d[3] = 0.0;
1577   }
1578   else /* bottle_type == KLEIN_BOTTLE_LAWSON */
1579   {
1580     kb->offset4d[0] = 0.0;
1581     kb->offset4d[1] = 0.0;
1582     kb->offset4d[2] = 0.0;
1583     if (projection_4d == DISP_4D_PERSPECTIVE &&
1584         projection_3d == DISP_3D_ORTHOGRAPHIC)
1585       kb->offset4d[3] = 1.5;
1586     else
1587       kb->offset4d[3] = 1.1;
1588     kb->offset3d[0] = 0.0;
1589     kb->offset3d[1] = 0.0;
1590     if (projection_4d == DISP_4D_ORTHOGRAPHIC)
1591       kb->offset3d[2] = -2.0;
1592     else
1593       kb->offset3d[2] = -5.0;
1594     kb->offset3d[3] = 0.0;
1595   }
1596
1597   gen_texture(mi);
1598   if (bottle_type == KLEIN_BOTTLE_FIGURE_8)
1599     setup_figure8(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
1600   else if (bottle_type == KLEIN_BOTTLE_SQUEEZED_TORUS)
1601     setup_squeezed_torus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
1602   else /* bottle_type == KLEIN_BOTTLE_LAWSON */
1603     setup_lawson(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
1604
1605   if (marks)
1606     glEnable(GL_TEXTURE_2D);
1607   else
1608     glDisable(GL_TEXTURE_2D);
1609
1610   glMatrixMode(GL_PROJECTION);
1611   glLoadIdentity();
1612   if (projection_3d == DISP_3D_PERSPECTIVE ||
1613       view == VIEW_WALK || view == VIEW_WALKTURN)
1614   {
1615     if (view == VIEW_WALK || view == VIEW_WALKTURN)
1616       gluPerspective(60.0,1.0,0.01,10.0);
1617     else
1618       gluPerspective(60.0,1.0,0.1,10.0);
1619   }
1620   else
1621   {
1622     glOrtho(-1.0,1.0,-1.0,1.0,0.1,10.0);
1623   }
1624   glMatrixMode(GL_MODELVIEW);
1625   glLoadIdentity();
1626
1627 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
1628   if (display_mode == DISP_WIREFRAME)
1629     display_mode = DISP_SURFACE;
1630 # endif
1631
1632   if (display_mode == DISP_SURFACE)
1633   {
1634     glEnable(GL_DEPTH_TEST);
1635     glDepthFunc(GL_LESS);
1636     glShadeModel(GL_SMOOTH);
1637     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
1638     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
1639     glEnable(GL_LIGHTING);
1640     glEnable(GL_LIGHT0);
1641     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
1642     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
1643     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
1644     glLightfv(GL_LIGHT0,GL_POSITION,light_position);
1645     glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
1646     glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
1647     glDepthMask(GL_TRUE);
1648     glDisable(GL_BLEND);
1649   }
1650   else if (display_mode == DISP_TRANSPARENT)
1651   {
1652     glDisable(GL_DEPTH_TEST);
1653     glShadeModel(GL_SMOOTH);
1654     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
1655     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
1656     glEnable(GL_LIGHTING);
1657     glEnable(GL_LIGHT0);
1658     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
1659     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
1660     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
1661     glLightfv(GL_LIGHT0,GL_POSITION,light_position);
1662     glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
1663     glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
1664     glDepthMask(GL_FALSE);
1665     glEnable(GL_BLEND);
1666     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
1667   }
1668   else  /* display_mode == DISP_WIREFRAME */
1669   {
1670     glDisable(GL_DEPTH_TEST);
1671     glShadeModel(GL_FLAT);
1672     glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
1673     glDisable(GL_LIGHTING);
1674     glDisable(GL_LIGHT0);
1675     glDisable(GL_BLEND);
1676   }
1677 }
1678
1679
1680 /* Redisplay the Klein bottle. */
1681 static void display_klein(ModeInfo *mi)
1682 {
1683   kleinstruct *kb = &klein[MI_SCREEN(mi)];
1684
1685   if (!kb->button_pressed)
1686   {
1687     if (view == VIEW_TURN)
1688     {
1689       kb->alpha += speed_wx * kb->speed_scale;
1690       if (kb->alpha >= 360.0)
1691         kb->alpha -= 360.0;
1692       kb->beta += speed_wy * kb->speed_scale;
1693       if (kb->beta >= 360.0)
1694         kb->beta -= 360.0;
1695       kb->delta += speed_wz * kb->speed_scale;
1696       if (kb->delta >= 360.0)
1697         kb->delta -= 360.0;
1698       kb->zeta += speed_xy * kb->speed_scale;
1699       if (kb->zeta >= 360.0)
1700         kb->zeta -= 360.0;
1701       kb->eta += speed_xz * kb->speed_scale;
1702       if (kb->eta >= 360.0)
1703         kb->eta -= 360.0;
1704       kb->theta += speed_yz * kb->speed_scale;
1705       if (kb->theta >= 360.0)
1706         kb->theta -= 360.0;
1707     }
1708     if (view == VIEW_WALKTURN)
1709     {
1710       kb->zeta += speed_xy * kb->speed_scale;
1711       if (kb->zeta >= 360.0)
1712         kb->zeta -= 360.0;
1713       kb->eta += speed_xz * kb->speed_scale;
1714       if (kb->eta >= 360.0)
1715         kb->eta -= 360.0;
1716       kb->theta += speed_yz * kb->speed_scale;
1717       if (kb->theta >= 360.0)
1718         kb->theta -= 360.0;
1719     }
1720     if (view == VIEW_WALK || view == VIEW_WALKTURN)
1721     {
1722       kb->dvmove = cos(walk_direction*M_PI/180.0)*walk_speed*M_PI/4096.0;
1723       kb->vmove += kb->dvmove;
1724       if (kb->vmove >= 2.0*M_PI)
1725       {
1726         kb->vmove -= 2.0*M_PI;
1727         kb->umove = 2.0*M_PI-kb->umove;
1728         kb->side = -kb->side;
1729       }
1730       kb->dumove = (kb->side*sin(walk_direction*M_PI/180.0)*
1731                     walk_speed*M_PI/4096.0);
1732       kb->umove += kb->dumove;
1733       if (kb->umove >= 2.0*M_PI)
1734         kb->umove -= 2.0*M_PI;
1735       if (kb->umove < 0.0)
1736         kb->umove += 2.0*M_PI;
1737     }
1738   }
1739
1740   glMatrixMode(GL_PROJECTION);
1741   glLoadIdentity();
1742   if (projection_3d == DISP_3D_PERSPECTIVE ||
1743       view == VIEW_WALK || view == VIEW_WALKTURN)
1744   {
1745     if (view == VIEW_WALK || view == VIEW_WALKTURN)
1746       gluPerspective(60.0,kb->aspect,0.01,10.0);
1747     else
1748       gluPerspective(60.0,kb->aspect,0.1,10.0);
1749   }
1750   else
1751   {
1752     if (kb->aspect >= 1.0)
1753       glOrtho(-kb->aspect,kb->aspect,-1.0,1.0,0.1,10.0);
1754     else
1755       glOrtho(-1.0,1.0,-1.0/kb->aspect,1.0/kb->aspect,0.1,10.0);
1756   }
1757   glMatrixMode(GL_MODELVIEW);
1758   glLoadIdentity();
1759
1760   if (bottle_type == KLEIN_BOTTLE_FIGURE_8)
1761     mi->polygon_count = figure8(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
1762   else if (bottle_type == KLEIN_BOTTLE_SQUEEZED_TORUS)
1763     mi->polygon_count = squeezed_torus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
1764   else /* bottle_type == KLEIN_BOTTLE_LAWSON */
1765     mi->polygon_count = lawson(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
1766 }
1767
1768
1769 ENTRYPOINT void reshape_klein(ModeInfo *mi, int width, int height)
1770 {
1771   kleinstruct *kb = &klein[MI_SCREEN(mi)];
1772
1773   kb->WindW = (GLint)width;
1774   kb->WindH = (GLint)height;
1775   glViewport(0,0,width,height);
1776   kb->aspect = (GLfloat)width/(GLfloat)height;
1777 }
1778
1779
1780 ENTRYPOINT Bool klein_handle_event(ModeInfo *mi, XEvent *event)
1781 {
1782   kleinstruct *kb = &klein[MI_SCREEN(mi)];
1783   KeySym  sym = 0;
1784   char c = 0;
1785
1786   if (event->xany.type == KeyPress || event->xany.type == KeyRelease)
1787     XLookupString (&event->xkey, &c, 1, &sym, 0);
1788
1789   if (event->xany.type == ButtonPress &&
1790       event->xbutton.button == Button1)
1791   {
1792     kb->button_pressed = True;
1793     gltrackball_start(kb->trackballs[kb->current_trackball],
1794                       event->xbutton.x, event->xbutton.y,
1795                       MI_WIDTH(mi), MI_HEIGHT(mi));
1796     return True;
1797   }
1798   else if (event->xany.type == ButtonRelease &&
1799            event->xbutton.button == Button1)
1800   {
1801     kb->button_pressed = False;
1802     return True;
1803   }
1804   else if (event->xany.type == KeyPress)
1805   {
1806     if (sym == XK_Shift_L || sym == XK_Shift_R)
1807     {
1808       kb->current_trackball = 1;
1809       if (kb->button_pressed)
1810         gltrackball_start(kb->trackballs[kb->current_trackball],
1811                           event->xbutton.x, event->xbutton.y,
1812                           MI_WIDTH(mi), MI_HEIGHT(mi));
1813       return True;
1814     }
1815   }
1816   else if (event->xany.type == KeyRelease)
1817   {
1818     if (sym == XK_Shift_L || sym == XK_Shift_R)
1819     {
1820       kb->current_trackball = 0;
1821       if (kb->button_pressed)
1822         gltrackball_start(kb->trackballs[kb->current_trackball],
1823                           event->xbutton.x, event->xbutton.y,
1824                           MI_WIDTH(mi), MI_HEIGHT(mi));
1825       return True;
1826     }
1827   }
1828   else if (event->xany.type == MotionNotify && kb->button_pressed)
1829   {
1830     gltrackball_track(kb->trackballs[kb->current_trackball],
1831                       event->xmotion.x, event->xmotion.y,
1832                       MI_WIDTH(mi), MI_HEIGHT(mi));
1833     return True;
1834   }
1835
1836   return False;
1837 }
1838
1839
1840 /*
1841  *-----------------------------------------------------------------------------
1842  *-----------------------------------------------------------------------------
1843  *    Xlock hooks.
1844  *-----------------------------------------------------------------------------
1845  *-----------------------------------------------------------------------------
1846  */
1847
1848 /*
1849  *-----------------------------------------------------------------------------
1850  *    Initialize klein.  Called each time the window changes.
1851  *-----------------------------------------------------------------------------
1852  */
1853
1854 ENTRYPOINT void init_klein(ModeInfo *mi)
1855 {
1856   kleinstruct *kb;
1857
1858   if (klein == NULL)
1859   {
1860     klein = (kleinstruct *)calloc(MI_NUM_SCREENS(mi),
1861                                               sizeof(kleinstruct));
1862     if (klein == NULL)
1863       return;
1864   }
1865   kb = &klein[MI_SCREEN(mi)];
1866
1867   
1868   kb->trackballs[0] = gltrackball_init(True);
1869   kb->trackballs[1] = gltrackball_init(True);
1870   kb->current_trackball = 0;
1871   kb->button_pressed = False;
1872
1873   /* Set the Klein bottle. */
1874   if (!strcasecmp(klein_bottle,"random"))
1875   {
1876     bottle_type = random() % NUM_KLEIN_BOTTLES;
1877   }
1878   else if (!strcasecmp(klein_bottle,"figure-8"))
1879   {
1880     bottle_type = KLEIN_BOTTLE_FIGURE_8;
1881   }
1882   else if (!strcasecmp(klein_bottle,"squeezed-torus"))
1883   {
1884     bottle_type = KLEIN_BOTTLE_SQUEEZED_TORUS;
1885   }
1886   else if (!strcasecmp(klein_bottle,"lawson"))
1887   {
1888     bottle_type = KLEIN_BOTTLE_LAWSON;
1889   }
1890   else
1891   {
1892     bottle_type = random() % NUM_KLEIN_BOTTLES;
1893   }
1894
1895   /* Set the display mode. */
1896   if (!strcasecmp(mode,"random"))
1897   {
1898     display_mode = random() % NUM_DISPLAY_MODES;
1899   }
1900   else if (!strcasecmp(mode,"wireframe"))
1901   {
1902     display_mode = DISP_WIREFRAME;
1903   }
1904   else if (!strcasecmp(mode,"surface"))
1905   {
1906     display_mode = DISP_SURFACE;
1907   }
1908   else if (!strcasecmp(mode,"transparent"))
1909   {
1910     display_mode = DISP_TRANSPARENT;
1911   }
1912   else
1913   {
1914     display_mode = random() % NUM_DISPLAY_MODES;
1915   }
1916
1917   /* Orientation marks don't make sense in wireframe mode. */
1918   if (display_mode == DISP_WIREFRAME)
1919     marks = False;
1920
1921   /* Set the appearance. */
1922   if (!strcasecmp(appear,"random"))
1923   {
1924     appearance = random() % NUM_APPEARANCES;
1925   }
1926   else if (!strcasecmp(appear,"solid"))
1927   {
1928     appearance = APPEARANCE_SOLID;
1929   }
1930   else if (!strcasecmp(appear,"bands"))
1931   {
1932     appearance = APPEARANCE_BANDS;
1933   }
1934   else
1935   {
1936     appearance = random() % NUM_APPEARANCES;
1937   }
1938
1939   /* Set the color mode. */
1940   if (!strcasecmp(color_mode,"random"))
1941   {
1942     colors = random() % NUM_COLORS;
1943   }
1944   else if (!strcasecmp(color_mode,"two-sided"))
1945   {
1946     colors = COLORS_TWOSIDED;
1947   }
1948   else if (!strcasecmp(color_mode,"rainbow"))
1949   {
1950     colors = COLORS_RAINBOW;
1951   }
1952   else if (!strcasecmp(color_mode,"depth"))
1953   {
1954     colors = COLORS_DEPTH;
1955   }
1956   else
1957   {
1958     colors = random() % NUM_COLORS;
1959   }
1960
1961   /* Set the view mode. */
1962   if (!strcasecmp(view_mode,"random"))
1963   {
1964     view = random() % NUM_VIEW_MODES;
1965   }
1966   else if (!strcasecmp(view_mode,"walk"))
1967   {
1968     view = VIEW_WALK;
1969   }
1970   else if (!strcasecmp(view_mode,"turn"))
1971   {
1972     view = VIEW_TURN;
1973   }
1974   else if (!strcasecmp(view_mode,"walk-turn"))
1975   {
1976     view = VIEW_WALKTURN;
1977   }
1978   else
1979   {
1980     view = random() % NUM_VIEW_MODES;
1981   }
1982
1983   /* Set the 3d projection mode. */
1984   if (!strcasecmp(proj_3d,"random"))
1985   {
1986     /* Orthographic projection only makes sense in turn mode. */
1987     if (view == VIEW_TURN)
1988       projection_3d = random() % NUM_DISP_3D_MODES;
1989     else
1990       projection_3d = DISP_3D_PERSPECTIVE;
1991   }
1992   else if (!strcasecmp(proj_3d,"perspective"))
1993   {
1994     projection_3d = DISP_3D_PERSPECTIVE;
1995   }
1996   else if (!strcasecmp(proj_3d,"orthographic"))
1997   {
1998     projection_3d = DISP_3D_ORTHOGRAPHIC;
1999   }
2000   else
2001   {
2002     /* Orthographic projection only makes sense in turn mode. */
2003     if (view == VIEW_TURN)
2004       projection_3d = random() % NUM_DISP_3D_MODES;
2005     else
2006       projection_3d = DISP_3D_PERSPECTIVE;
2007   }
2008
2009   /* Set the 4d projection mode. */
2010   if (!strcasecmp(proj_4d,"random"))
2011   {
2012     projection_4d = random() % NUM_DISP_4D_MODES;
2013   }
2014   else if (!strcasecmp(proj_4d,"perspective"))
2015   {
2016     projection_4d = DISP_4D_PERSPECTIVE;
2017   }
2018   else if (!strcasecmp(proj_4d,"orthographic"))
2019   {
2020     projection_4d = DISP_4D_ORTHOGRAPHIC;
2021   }
2022   else
2023   {
2024     projection_4d = random() % NUM_DISP_4D_MODES;
2025   }
2026
2027   /* Modify the speeds to a useful range in walk-and-turn mode. */
2028   if (view == VIEW_WALKTURN)
2029   {
2030     speed_wx *= 0.2;
2031     speed_wy *= 0.2;
2032     speed_wz *= 0.2;
2033     speed_xy *= 0.2;
2034     speed_xz *= 0.2;
2035     speed_yz *= 0.2;
2036   }
2037
2038   /* make multiple screens rotate at slightly different rates. */
2039   kb->speed_scale = 0.9 + frand(0.3);
2040
2041   if ((kb->glx_context = init_GL(mi)) != NULL)
2042   {
2043     reshape_klein(mi,MI_WIDTH(mi),MI_HEIGHT(mi));
2044     glDrawBuffer(GL_BACK);
2045     init(mi);
2046   }
2047   else
2048   {
2049     MI_CLEARWINDOW(mi);
2050   }
2051 }
2052
2053 /*
2054  *-----------------------------------------------------------------------------
2055  *    Called by the mainline code periodically to update the display.
2056  *-----------------------------------------------------------------------------
2057  */
2058 ENTRYPOINT void draw_klein(ModeInfo *mi)
2059 {
2060   Display          *display = MI_DISPLAY(mi);
2061   Window           window = MI_WINDOW(mi);
2062   kleinstruct *kb;
2063
2064   if (klein == NULL)
2065     return;
2066   kb = &klein[MI_SCREEN(mi)];
2067
2068   MI_IS_DRAWN(mi) = True;
2069   if (!kb->glx_context)
2070     return;
2071
2072   glXMakeCurrent(display,window,*(kb->glx_context));
2073
2074   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
2075   glLoadIdentity();
2076
2077   display_klein(mi);
2078
2079   if (MI_IS_FPS(mi))
2080     do_fps (mi);
2081
2082   glFlush();
2083
2084   glXSwapBuffers(display,window);
2085 }
2086
2087
2088 /*
2089  *-----------------------------------------------------------------------------
2090  *    The display is being taken away from us.  Free up malloc'ed 
2091  *      memory and X resources that we've alloc'ed.  Only called
2092  *      once, we must zap everything for every screen.
2093  *-----------------------------------------------------------------------------
2094  */
2095
2096 ENTRYPOINT void release_klein(ModeInfo *mi)
2097 {
2098   if (klein != NULL)
2099   {
2100     int screen;
2101
2102     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
2103     {
2104       kleinstruct *kb = &klein[screen];
2105
2106       if (kb->glx_context)
2107         kb->glx_context = (GLXContext *)NULL;
2108     }
2109     (void) free((void *)klein);
2110     klein = (kleinstruct *)NULL;
2111   }
2112   FreeAllGL(mi);
2113 }
2114
2115 #ifndef STANDALONE
2116 ENTRYPOINT void change_klein(ModeInfo *mi)
2117 {
2118   kleinstruct *kb = &klein[MI_SCREEN(mi)];
2119
2120   if (!kb->glx_context)
2121     return;
2122
2123   glXMakeCurrent(MI_DISPLAY(mi),MI_WINDOW(mi),*(kb->glx_context));
2124   init(mi);
2125 }
2126 #endif /* !STANDALONE */
2127
2128 XSCREENSAVER_MODULE ("Klein", klein)
2129
2130 #endif /* USE_GL */