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