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