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