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