From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / projectiveplane.c
1 /* projectiveplane --- Shows a 4d embedding of the real projective plane
2    that rotates in 4d or on which you can walk */
3
4 #if 0
5 static const char sccsid[] = "@(#)projectiveplane.c  1.1 14/01/01 xlockmore";
6 #endif
7
8 /* Copyright (c) 2005-2014 Carsten Steger <carsten@mirsanmir.org>. */
9
10 /*
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * REVISION HISTORY:
24  * C. Steger - 14/01/03: Initial version
25  */
26
27 /*
28  * This program shows a 4d embedding of the real projective plane.
29  * You can walk on the projective plane, see it turn in 4d, or walk on
30  * it while it turns in 4d.  The fact that the surface is an embedding
31  * of the real projective plane in 4d can be seen in the depth colors
32  * mode: set all rotation speeds to 0 and the projection mode to 4d
33  * orthographic projection.  In its default orientation, the embedding
34  * of the real projective plane will then project to the Roman
35  * surface, which has three lines of self-intersection.  However, at
36  * the three lines of self-intersection the parts of the surface that
37  * intersect have different colors, i.e., different 4d depths.
38  *
39  * The real projective plane is a non-orientable surface.  To make
40  * this apparent, the two-sided color mode can be used.
41  * Alternatively, orientation markers (curling arrows) can be drawn as
42  * a texture map on the surface of the projective plane.  While
43  * walking on the projective plane, you will notice that the
44  * orientation of the curling arrows changes (which it must because
45  * the projective plane is non-orientable).
46  *
47  * The real projective plane is a model for the projective geometry in
48  * 2d space.  One point can be singled out as the origin.  A line can
49  * be singled out as the line at infinity, i.e., a line that lies at
50  * an infinite distance to the origin.  The line at infinity is
51  * topologically a circle.  Points on the line at infinity are also
52  * used to model directions in projective geometry.  The origin can be
53  * visualized in different manners.  When using distance colors, the
54  * origin is the point that is displayed as fully saturated red, which
55  * is easier to see as the center of the reddish area on the
56  * projective plane.  Alternatively, when using distance bands, the
57  * origin is the center of the only band that projects to a disc.
58  * When using direction bands, the origin is the point where all
59  * direction bands collapse to a point.  Finally, when orientation
60  * markers are being displayed, the origin the the point where all
61  * orientation markers are compressed to a point.  The line at
62  * infinity can also be visualized in different ways.  When using
63  * distance colors, the line at infinity is the line that is displayed
64  * as fully saturated magenta.  When two-sided colors are used, the
65  * line at infinity lies at the points where the red and green "sides"
66  * of the projective plane meet (of course, the real projective plane
67  * only has one side, so this is a design choice of the
68  * visualization).  Alternatively, when orientation markers are being
69  * displayed, the line at infinity is the place where the orientation
70  * markers change their orientation.
71  *
72  * Note that when the projective plane is displayed with bands, the
73  * orientation markers are placed in the middle of the bands.  For
74  * distance bands, the bands are chosen in such a way that the band at
75  * the origin is only half as wide as the remaining bands, which
76  * results in a disc being displayed at the origin that has the same
77  * diameter as the remaining bands.  This choice, however, also
78  * implies that the band at infinity is half as wide as the other
79  * bands.  Since the projective plane is attached to itself (in a
80  * complicated fashion) at the line at infinity, effectively the band
81  * at infinity is again as wide as the remaining bands.  However,
82  * since the orientation markers are displayed in the middle of the
83  * bands, this means that only one half of the orientation markers
84  * will be displayed twice at the line at infinity if distance bands
85  * are used.  If direction bands are used or if the projective plane
86  * is displayed as a solid surface, the orientation markers are
87  * displayed fully at the respective sides of the line at infinity.
88  *
89  * The program projects the 4d projective plane to 3d using either a
90  * perspective or an orthographic projection.  Which of the two
91  * alternatives looks more appealing is up to you.  However, two
92  * famous surfaces are obtained if orthographic 4d projection is used:
93  * The Roman surface and the cross cap.  If the projective plane is
94  * rotated in 4d, the result of the projection for certain rotations
95  * is a Roman surface and for certain rotations it is a cross cap.
96  * The easiest way to see this is to set all rotation speeds to 0 and
97  * the rotation speed around the yz plane to a value different from 0.
98  * However, for any 4d rotation speeds, the projections will generally
99  * cycle between the Roman surface and the cross cap.  The difference
100  * is where the origin and the line at infinity will lie with respect
101  * to the self-intersections in the projections to 3d.
102  *
103  * The projected projective plane can then be projected to the screen
104  * either perspectively or orthographically.  When using the walking
105  * modes, perspective projection to the screen will be used.
106  *
107  * There are three display modes for the projective plane: mesh
108  * (wireframe), solid, or transparent.  Furthermore, the appearance of
109  * the projective plane can be as a solid object or as a set of
110  * see-through bands.  The bands can be distance bands, i.e., bands
111  * that lie at increasing distances from the origin, or direction
112  * bands, i.e., bands that lie at increasing angles with respect to
113  * the origin.
114  *
115  * When the projective plane is displayed with direction bands, you
116  * will be able to see that each direction band (modulo the "pinching"
117  * at the origin) is a Moebius strip, which also shows that the
118  * projective plane is non-orientable.
119  *
120  * Finally, the colors with with the projective plane is drawn can be
121  * set to two-sided, distance, direction, or depth.  In two-sided
122  * mode, the projective plane is drawn with red on one "side" and
123  * green on the "other side".  As described above, the projective
124  * plane only has one side, so the color jumps from red to green along
125  * the line at infinity.  This mode enables you to see that the
126  * projective plane is non-orientable.  In distance mode, the
127  * projective plane is displayed with fully saturated colors that
128  * depend on the distance of the points on the projective plane to the
129  * origin.  The origin is displayed in red, the line at infinity is
130  * displayed in magenta.  If the projective plane is displayed as
131  * distance bands, each band will be displayed with a different color.
132  * In direction mode, the projective plane is displayed with fully
133  * saturated colors that depend on the angle of the points on the
134  * projective plane with respect to the origin.  Angles in opposite
135  * directions to the origin (e.g., 15 and 205 degrees) are displayed
136  * in the same color since they are projectively equivalent.  If the
137  * projective plane is displayed as direction bands, each band will be
138  * displayed with a different color.  Finally, in depth mode the
139  * projective plane with colors chosen depending on the 4d "depth"
140  * (i.e., the w coordinate) of the points on the projective plane at
141  * its default orientation in 4d.  As discussed above, this mode
142  * enables you to see that the projective plane does not intersect
143  * itself in 4d.
144  *
145  * The rotation speed for each of the six planes around which the
146  * projective plane rotates can be chosen.  For the walk-and-turn
147  * more, only the rotation speeds around the true 4d planes are used
148  * (the xy, xz, and yz planes).
149  *
150  * Furthermore, in the walking modes the walking direction in the 2d
151  * base square of the projective plane and the walking speed can be
152  * chosen.  The walking direction is measured as an angle in degrees
153  * in the 2d square that forms the coordinate system of the surface of
154  * the projective plane.  A value of 0 or 180 means that the walk is
155  * along a circle at a randomly chosen distance from the origin
156  * (parallel to a distance band).  A value of 90 or 270 means that the
157  * walk is directly from the origin to the line at infinity and back
158  * (analogous to a direction band).  Any other value results in a
159  * curved path from the origin to the line at infinity and back.
160  *
161  * This program is somewhat inspired by Thomas Banchoff's book "Beyond
162  * the Third Dimension: Geometry, Computer Graphics, and Higher
163  * Dimensions", Scientific American Library, 1990.
164  */
165
166 #ifndef M_PI
167 #define M_PI 3.14159265358979323846
168 #endif
169
170 #define DISP_WIREFRAME             0
171 #define DISP_SURFACE               1
172 #define DISP_TRANSPARENT           2
173 #define NUM_DISPLAY_MODES          3
174
175 #define APPEARANCE_SOLID           0
176 #define APPEARANCE_DISTANCE_BANDS  1
177 #define APPEARANCE_DIRECTION_BANDS 2
178 #define NUM_APPEARANCES            3
179
180 #define COLORS_TWOSIDED            0
181 #define COLORS_DISTANCE            1
182 #define COLORS_DIRECTION           2
183 #define COLORS_DEPTH               3
184 #define NUM_COLORS                 4
185
186 #define VIEW_WALK                  0
187 #define VIEW_TURN                  1
188 #define VIEW_WALKTURN              2
189 #define NUM_VIEW_MODES             3
190
191 #define DISP_3D_PERSPECTIVE        0
192 #define DISP_3D_ORTHOGRAPHIC       1
193 #define NUM_DISP_3D_MODES          2
194
195 #define DISP_4D_PERSPECTIVE        0
196 #define DISP_4D_ORTHOGRAPHIC       1
197 #define NUM_DISP_4D_MODES          2
198
199 #define DEF_DISPLAY_MODE           "random"
200 #define DEF_APPEARANCE             "random"
201 #define DEF_COLORS                 "random"
202 #define DEF_VIEW_MODE              "random"
203 #define DEF_MARKS                  "False"
204 #define DEF_PROJECTION_3D          "random"
205 #define DEF_PROJECTION_4D          "random"
206 #define DEF_SPEEDWX                "1.1"
207 #define DEF_SPEEDWY                "1.3"
208 #define DEF_SPEEDWZ                "1.5"
209 #define DEF_SPEEDXY                "1.7"
210 #define DEF_SPEEDXZ                "1.9"
211 #define DEF_SPEEDYZ                "2.1"
212 #define DEF_WALK_DIRECTION         "83.0"
213 #define DEF_WALK_SPEED             "20.0"
214
215 #ifdef STANDALONE
216 # define DEFAULTS           "*delay:      10000 \n" \
217                             "*showFPS:    False \n" \
218
219 # define refresh_projectiveplane 0
220 # include "xlockmore.h"         /* from the xscreensaver distribution */
221 #else  /* !STANDALONE */
222 # include "xlock.h"             /* from the xlockmore distribution */
223 #endif /* !STANDALONE */
224
225 #ifdef USE_GL
226
227 #ifndef HAVE_COCOA
228 # include <X11/keysym.h>
229 #endif
230
231 #include "gltrackball.h"
232
233 #include <float.h>
234
235
236 #ifdef USE_MODULES
237 ModStruct projectiveplane_description =
238 {"projectiveplane", "init_projectiveplane", "draw_projectiveplane",
239  "release_projectiveplane", "draw_projectiveplane", "change_projectiveplane",
240  NULL, &projectiveplane_opts, 25000, 1, 1, 1, 1.0, 4, "",
241  "Rotate a 4d embedding of the real projective plane in 4d or walk on it",
242  0, NULL};
243
244 #endif
245
246
247 static char *mode;
248 static int display_mode;
249 static char *appear;
250 static int appearance;
251 static char *color_mode;
252 static int colors;
253 static char *view_mode;
254 static int view;
255 static Bool marks;
256 static char *proj_3d;
257 static int projection_3d;
258 static char *proj_4d;
259 static int projection_4d;
260 static float speed_wx;
261 static float speed_wy;
262 static float speed_wz;
263 static float speed_xy;
264 static float speed_xz;
265 static float speed_yz;
266 static float walk_direction;
267 static float walk_speed;
268
269
270 static XrmOptionDescRec opts[] =
271 {
272   {"-mode",              ".displayMode",   XrmoptionSepArg, 0 },
273   {"-wireframe",         ".displayMode",   XrmoptionNoArg,  "wireframe" },
274   {"-surface",           ".displayMode",   XrmoptionNoArg,  "surface" },
275   {"-transparent",       ".displayMode",   XrmoptionNoArg,  "transparent" },
276   {"-appearance",        ".appearance",    XrmoptionSepArg, 0 },
277   {"-solid",             ".appearance",    XrmoptionNoArg,  "solid" },
278   {"-distance-bands",    ".appearance",    XrmoptionNoArg,  "distance-bands" },
279   {"-direction-bands",   ".appearance",    XrmoptionNoArg,  "direction-bands" },
280   {"-colors",            ".colors",        XrmoptionSepArg, 0 },
281   {"-twosided-colors",   ".colors",        XrmoptionNoArg,  "two-sided" },
282   {"-distance-colors",   ".colors",        XrmoptionNoArg,  "distance" },
283   {"-direction-colors",  ".colors",        XrmoptionNoArg,  "direction" },
284   {"-depth-colors",      ".colors",        XrmoptionNoArg,  "depth" },
285   {"-view-mode",         ".viewMode",      XrmoptionSepArg, 0 },
286   {"-walk",              ".viewMode",      XrmoptionNoArg,  "walk" },
287   {"-turn",              ".viewMode",      XrmoptionNoArg,  "turn" },
288   {"-walk-turn",         ".viewMode",      XrmoptionNoArg,  "walk-turn" },
289   {"-orientation-marks", ".marks",         XrmoptionNoArg, "on"},
290   {"+orientation-marks", ".marks",         XrmoptionNoArg, "off"},
291   {"-projection-3d",     ".projection3d",  XrmoptionSepArg, 0 },
292   {"-perspective-3d",    ".projection3d",  XrmoptionNoArg,  "perspective" },
293   {"-orthographic-3d",   ".projection3d",  XrmoptionNoArg,  "orthographic" },
294   {"-projection-4d",     ".projection4d",  XrmoptionSepArg, 0 },
295   {"-perspective-4d",    ".projection4d",  XrmoptionNoArg,  "perspective" },
296   {"-orthographic-4d",   ".projection4d",  XrmoptionNoArg,  "orthographic" },
297   {"-speed-wx",          ".speedwx",       XrmoptionSepArg, 0 },
298   {"-speed-wy",          ".speedwy",       XrmoptionSepArg, 0 },
299   {"-speed-wz",          ".speedwz",       XrmoptionSepArg, 0 },
300   {"-speed-xy",          ".speedxy",       XrmoptionSepArg, 0 },
301   {"-speed-xz",          ".speedxz",       XrmoptionSepArg, 0 },
302   {"-speed-yz",          ".speedyz",       XrmoptionSepArg, 0 },
303   {"-walk-direction",    ".walkDirection", XrmoptionSepArg, 0 },
304   {"-walk-speed",        ".walkSpeed",     XrmoptionSepArg, 0 }
305 };
306
307 static argtype vars[] =
308 {
309   { &mode,           "displayMode",   "DisplayMode",   DEF_DISPLAY_MODE,   t_String },
310   { &appear,         "appearance",    "Appearance",    DEF_APPEARANCE,     t_String },
311   { &color_mode,     "colors",        "Colors",        DEF_COLORS,         t_String },
312   { &view_mode,      "viewMode",      "ViewMode",      DEF_VIEW_MODE,      t_String },
313   { &marks,          "marks",         "Marks",         DEF_MARKS,          t_Bool },
314   { &proj_3d,        "projection3d",  "Projection3d",  DEF_PROJECTION_3D,  t_String },
315   { &proj_4d,        "projection4d",  "Projection4d",  DEF_PROJECTION_4D,  t_String },
316   { &speed_wx,       "speedwx",       "Speedwx",       DEF_SPEEDWX,        t_Float},
317   { &speed_wy,       "speedwy",       "Speedwy",       DEF_SPEEDWY,        t_Float},
318   { &speed_wz,       "speedwz",       "Speedwz",       DEF_SPEEDWZ,        t_Float},
319   { &speed_xy,       "speedxy",       "Speedxy",       DEF_SPEEDXY,        t_Float},
320   { &speed_xz,       "speedxz",       "Speedxz",       DEF_SPEEDXZ,        t_Float},
321   { &speed_yz,       "speedyz",       "Speedyz",       DEF_SPEEDYZ,        t_Float},
322   { &walk_direction, "walkDirection", "WalkDirection", DEF_WALK_DIRECTION, t_Float},
323   { &walk_speed,     "walkSpeed",     "WalkSpeed",     DEF_WALK_SPEED,     t_Float}
324 };
325
326 ENTRYPOINT ModeSpecOpt projectiveplane_opts =
327 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, NULL};
328
329
330 /* Offset by which we walk above the projective plane */
331 #define DELTAY  0.01
332
333 /* Number of subdivisions of the projective plane */
334 #define NUMU 128
335 #define NUMV 128
336
337 /* Number of subdivisions per band */
338 #define NUMB 8
339
340
341 typedef struct {
342   GLint      WindH, WindW;
343   GLXContext *glx_context;
344   /* 4D rotation angles */
345   float alpha, beta, delta, zeta, eta, theta;
346   /* Movement parameters */
347   float umove, vmove, dumove, dvmove;
348   int side, dir;
349   /* The viewing offset in 4d */
350   float offset4d[4];
351   /* The viewing offset in 3d */
352   float offset3d[4];
353   /* The 4d coordinates of the projective plane and their derivatives */
354   float x[(NUMU+1)*(NUMV+1)][4];
355   float xu[(NUMU+1)*(NUMV+1)][4];
356   float xv[(NUMU+1)*(NUMV+1)][4];
357   float pp[(NUMU+1)*(NUMV+1)][3];
358   float pn[(NUMU+1)*(NUMV+1)][3];
359   /* The precomputed colors of the projective plane */
360   float col[(NUMU+1)*(NUMV+1)][4];
361   /* The precomputed texture coordinates of the projective plane */
362   float tex[(NUMU+1)*(NUMV+1)][2];
363   /* The "curlicue" texture */
364   GLuint tex_name;
365   /* Aspect ratio of the current window */
366   float aspect;
367   /* Trackball states */
368   trackball_state *trackballs[2];
369   int current_trackball;
370   Bool button_pressed;
371   /* A random factor to modify the rotation speeds */
372   float speed_scale;
373 } projectiveplanestruct;
374
375 static projectiveplanestruct *projectiveplane = (projectiveplanestruct *) NULL;
376
377
378 /* A texture map containing a "curlicue" */
379 #define TEX_DIMENSION 64
380 static const unsigned char texture[TEX_DIMENSION*TEX_DIMENSION] = {
381   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
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,255,255,255,255,255,255,255,255,255,
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,255,255,255,255,255,255,255,255,255,
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,255,255,255,255,255,255,255,255,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,255,255,255,255,255,255,255,255,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,255,255,255,255,255,255,255,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,255,255,255,255,255,255,255,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,255,255,255,255,255,255,255,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,255,255,255,255,255,255,255,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,255,255,255,255,255,255,255,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,255,255,255,255,255,255,255,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,255,255,255,255,255,255,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,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,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,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,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,255,255,255,255,255,255,255,255,255,255,255,255,255,
415   255,255,255,255,255,255,255, 58, 43, 43, 43, 43, 45, 70, 70, 70,
416    70, 70, 70, 70, 74, 98, 98, 98,100,194,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,255,255,255,255,255,255,255,255,255,255,255,255,255,
419   255,255,255,255,255,255,255, 18,  0,  0,  0,  0,  0,  0,  0,  0,
420     0,  0,  0,  0,  0,  0,  0, 30,186,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,255,255,255,255,255,255,255,255,255,255,255,255,255,
423   255,255,255,255,255,255,255, 18,  0,  0,  0,  0,  0,  0,  0,  0,
424     0,  0,  0,  0,  0,  1,111,244,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,255,255,255,255,255,255,255,255,255,255,255,255,255,
427   255,255,255,255,255,255,255, 18,  0,  0,  0,  0,  0,  0,  0,  0,
428     0,  0,  0,  0, 43,198,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,255,255,255,255,255,255,255,255,255,255,255,255,255,
431   255,255,255,255,255,255,255, 18,  0,  0,  0,  0,  0,  0,  0,  0,
432     0,  0,  5,123,248,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,255,255,255,255,255,255,255,255,255,255,255,255,255,
435   255,255,255,255,255,255,255, 18,  0,  0,  0,  0,  0,  0,  0,  0,
436     0, 50,209,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,246,
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, 18,  0,  0,  0,  0,  0,  0,  0,  0,
440    74,252,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,138,  4,
442    66,229,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
443   255,255,255,255,255,255,255, 18,  0,  0,  0,  0,  0,  0,  0,  0,
444     1,170,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,153,  0,  0,
446     0, 53,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
447   255,255,255,255,255,255,255, 18,  0,  0,  0,  0,  0,  0,  0,  0,
448     0,  6,188,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,213,  7,  0,  0,
450     0,  0,226,255,255,255,255,255,255,255,255,255,255,255,255,255,
451   255,255,255,255,255,255,255, 45,  0,  0,  0,  0,  0, 47,  0,  0,
452     0,  0, 22,225,255,255,255,255,255,255,255,255,255,255,255,255,
453   255,255,255,255,255,255,255,255,255,255,255,254, 54,  0,  0,  0,
454     0, 81,254,255,255,255,255,255,255,255,255,255,255,255,255,255,
455   255,255,255,255,255,255,255, 45,  0,  0,  0,  0, 56,247, 82,  0,
456     0,  0,  0, 59,253,255,255,255,255,255,255,255,255,255,255,255,
457   255,255,255,255,255,255,255,255,255,255,255,152,  0,  0,  0,  0,
458    52,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
459   255,255,255,255,255,255,255, 45,  0,  0,  0,  8,215,255,250, 56,
460     0,  0,  0,  0,142,255,255,255,255,255,255,255,255,255,255,255,
461   255,255,255,255,255,255,255,255,255,255,241, 19,  0,  0,  0, 15,
462   220,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
463   255,255,255,255,255,255,255, 45,  0,  0,  0,129,255,255,255,230,
464    23,  0,  0,  0, 12,230,255,255,255,255,255,255,255,255,255,255,
465   255,255,255,255,255,255,255,255,255,255,131,  0,  0,  0,  0,157,
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, 45,  0,  0, 49,250,255,255,255,255,
468   171,  0,  0,  0,  0,112,255,255,255,255,255,255,255,255,255,255,
469   255,255,255,255,255,255,255,255,255,246, 19,  0,  0,  0, 54,253,
470   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
471   255,255,255,255,255,255,255, 45,  0,  5,208,255,255,255,255,255,
472   255, 77,  0,  0,  0,  9,231,255,255,255,255,255,255,255,255,255,
473   255,255,255,255,255,255,255,255,255,163,  0,  0,  0,  0,186,255,
474   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
475   255,255,255,255,255,255,255, 45,  0,121,255,255,255,255,255,255,
476   255,211,  2,  0,  0,  0,134,255,255,255,255,255,255,255,255,255,
477   255,255,255,255,255,255,255,255,255, 69,  0,  0,  0, 50,255,255,
478   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
479   255,255,255,255,255,255,255, 45, 41,247,255,255,255,255,255,255,
480   255,255, 73,  0,  0,  0, 38,254,255,255,255,255,255,255,255,255,
481   255,255,255,255,255,255,255,255,237,  4,  0,  0,  0,145,255,255,
482   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
483   255,255,255,255,255,255,255, 52,201,255,255,255,255,255,255,255,
484   255,255,169,  0,  0,  0,  0,216,255,255,255,255,255,255,255,255,
485   255,255,255,255,255,255,255,255,181,  0,  0,  0,  0,229,255,255,
486   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
487   255,255,255,255,255,255,255,186,255,255,255,255,255,255,255,255,
488   255,255,247,  7,  0,  0,  0,150,255,255,255,255,255,255,255,255,
489   255,255,255,255,255,255,255,255,130,  0,  0,  0, 42,255,255,255,
490   255,255,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,255,
492   255,255,255, 67,  0,  0,  0, 91,255,255,255,255,255,255,255,255,
493   255,255,255,255,255,255,255,255, 79,  0,  0,  0, 95,255,255,255,
494   255,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   255,255,255,120,  0,  0,  0, 56,255,255,255,255,255,255,255,255,
497   255,255,255,255,255,255,255,255, 55,  0,  0,  0,130,255,255,255,
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,255,255,157,  0,  0,  0, 21,255,255,255,255,255,255,255,255,
501   255,255,255,255,255,255,255,255, 34,  0,  0,  0,161,255,255,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,255,179,  0,  0,  0,  2,250,255,255,255,255,255,255,255,
505   255,255,255,255,255,255,255,255, 27,  0,  0,  0,168,255,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,200,  0,  0,  0,  0,249,255,255,255,255,255,255,255,
509   255,255,255,255,255,255,255,255, 27,  0,  0,  0,168,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,200,  0,  0,  0,  0,249,255,255,255,255,255,255,255,
513   255,255,255,255,255,255,255,255, 27,  0,  0,  0,163,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,183,  0,  0,  0,  0,249,255,255,255,255,255,255,255,
517   255,255,255,255,255,255,255,255, 42,  0,  0,  0,135,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,161,  0,  0,  0, 17,254,255,255,255,255,255,255,255,
521   255,255,255,255,255,255,255,255, 76,  0,  0,  0,100,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,126,  0,  0,  0, 48,255,255,255,255,255,255,255,255,
525   255,255,255,255,255,255,255,255,114,  0,  0,  0, 53,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, 78,  0,  0,  0, 84,255,255,255,255,255,255,255,255,
529   255,255,255,255,255,255,255,255,165,  0,  0,  0,  3,241,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,252, 16,  0,  0,  0,139,255,255,255,255,255,255,255,255,
533   255,255,255,255,255,255,255,255,228,  0,  0,  0,  0,161,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,192,  0,  0,  0,  0,198,255,255,255,255,255,255,255,255,
537   255,255,255,255,255,255,255,255,255, 46,  0,  0,  0, 67,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, 93,  0,  0,  0, 21,250,255,255,255,255,255,255,255,255,
541   255,255,255,255,255,255,255,255,255,139,  0,  0,  0,  1,211,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,226,  7,  0,  0,  0,108,255,255,255,255,255,255,255,255,255,
545   255,255,255,255,255,255,255,255,255,230,  6,  0,  0,  0, 79,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,106,  0,  0,  0,  1,206,255,255,255,255,255,255,255,255,255,
549   255,255,255,255,255,255,255,255,255,255, 97,  0,  0,  0,  0,183,
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   202,  3,  0,  0,  0, 67,255,255,255,255,255,255,255,255,255,255,
553   255,255,255,255,255,255,255,255,255,255,221,  8,  0,  0,  0, 27,
554   235,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
555   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,243,
556    40,  0,  0,  0,  0,198,255,255,255,255,255,255,255,255,255,255,
557   255,255,255,255,255,255,255,255,255,255,255,126,  0,  0,  0,  0,
558    71,252,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
559   255,255,255,255,255,255,255,255,255,255,255,255,255,255,253, 85,
560     0,  0,  0,  0, 96,255,255,255,255,255,255,255,255,255,255,255,
561   255,255,255,255,255,255,255,255,255,255,255,247, 44,  0,  0,  0,
562     0, 91,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
563   255,255,255,255,255,255,255,255,255,255,255,255,255,255,116,  0,
564     0,  0,  0, 25,233,255,255,255,255,255,255,255,255,255,255,255,
565   255,255,255,255,255,255,255,255,255,255,255,255,216, 11,  0,  0,
566     0,  0, 90,251,255,255,255,255,255,255,255,255,255,255,255,255,
567   255,255,255,255,255,255,255,255,255,255,255,255,252,112,  0,  0,
568     0,  0,  4,191,255,255,255,255,255,255,255,255,255,255,255,255,
569   255,255,255,255,255,255,255,255,255,255,255,255,255,174,  4,  0,
570     0,  0,  0, 72,235,255,255,255,255,255,255,255,255,255,255,255,
571   255,255,255,255,255,255,255,255,255,255,255,242, 84,  0,  0,  0,
572     0,  0,146,255,255,255,255,255,255,255,255,255,255,255,255,255,
573   255,255,255,255,255,255,255,255,255,255,255,255,255,255,150,  1,
574     0,  0,  0,  0, 27,181,255,255,255,255,255,255,255,255,255,255,
575   255,255,255,255,255,255,255,255,255,255,194, 39,  0,  0,  0,  0,
576     0,120,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
577   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,151,
578     4,  0,  0,  0,  0,  0, 77,209,255,255,255,255,255,255,255,255,
579   255,255,255,255,255,255,255,255,216, 92,  1,  0,  0,  0,  0,  0,
580   125,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
581   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
582   175, 12,  0,  0,  0,  0,  0,  1, 70,164,241,255,255,255,255,255,
583   255,255,255,255,255,242,171, 77,  2,  0,  0,  0,  0,  0,  4,150,
584   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
585   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
586   255,214, 41,  0,  0,  0,  0,  0,  0,  0,  4, 48, 98,138,163,163,
587   163,163,140,103, 55,  5,  0,  0,  0,  0,  0,  0,  0, 30,199,255,
588   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
589   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
590   255,255,245,125,  8,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
591     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  3,105,240,255,255,
592   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
593   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
594   255,255,255,255,222,100,  5,  0,  0,  0,  0,  0,  0,  0,  0,  0,
595     0,  0,  0,  0,  0,  0,  0,  0,  0,  2, 83,210,255,255,255,255,
596   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
597   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
598   255,255,255,255,255,255,228,136, 45,  0,  0,  0,  0,  0,  0,  0,
599     0,  0,  0,  0,  0,  0,  0, 37,125,220,255,255,255,255,255,255,
600   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
601   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
602   255,255,255,255,255,255,255,255,255,225,166,112, 74, 43, 32, 12,
603     8, 32, 40, 71,105,162,218,255,255,255,255,255,255,255,255,255,
604   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
605   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
606   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
607   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
608   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
609   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
610   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
611   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
612   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
613   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
614   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
615   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
616   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
617   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
618   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
619   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
620   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
621   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
622   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
623   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
624   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
625   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
626   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
627   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
628   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
629   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
630   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
631   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
632   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
633   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
634   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
635   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
636   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
637 };
638
639
640 /* Add a rotation around the wx-plane to the matrix m. */
641 static void rotatewx(float m[4][4], float phi)
642 {
643   float c, s, u, v;
644   int i;
645
646   phi *= M_PI/180.0;
647   c = cos(phi);
648   s = sin(phi);
649   for (i=0; i<4; i++)
650   {
651     u = m[i][1];
652     v = m[i][2];
653     m[i][1] = c*u+s*v;
654     m[i][2] = -s*u+c*v;
655   }
656 }
657
658
659 /* Add a rotation around the wy-plane to the matrix m. */
660 static void rotatewy(float m[4][4], float phi)
661 {
662   float c, s, u, v;
663   int i;
664
665   phi *= M_PI/180.0;
666   c = cos(phi);
667   s = sin(phi);
668   for (i=0; i<4; i++)
669   {
670     u = m[i][0];
671     v = m[i][2];
672     m[i][0] = c*u-s*v;
673     m[i][2] = s*u+c*v;
674   }
675 }
676
677
678 /* Add a rotation around the wz-plane to the matrix m. */
679 static void rotatewz(float m[4][4], float phi)
680 {
681   float c, s, u, v;
682   int i;
683
684   phi *= M_PI/180.0;
685   c = cos(phi);
686   s = sin(phi);
687   for (i=0; i<4; i++)
688   {
689     u = m[i][0];
690     v = m[i][1];
691     m[i][0] = c*u+s*v;
692     m[i][1] = -s*u+c*v;
693   }
694 }
695
696
697 /* Add a rotation around the xy-plane to the matrix m. */
698 static void rotatexy(float m[4][4], float phi)
699 {
700   float c, s, u, v;
701   int i;
702
703   phi *= M_PI/180.0;
704   c = cos(phi);
705   s = sin(phi);
706   for (i=0; i<4; i++)
707   {
708     u = m[i][2];
709     v = m[i][3];
710     m[i][2] = c*u+s*v;
711     m[i][3] = -s*u+c*v;
712   }
713 }
714
715
716 /* Add a rotation around the xz-plane to the matrix m. */
717 static void rotatexz(float m[4][4], float phi)
718 {
719   float c, s, u, v;
720   int i;
721
722   phi *= M_PI/180.0;
723   c = cos(phi);
724   s = sin(phi);
725   for (i=0; i<4; i++)
726   {
727     u = m[i][1];
728     v = m[i][3];
729     m[i][1] = c*u-s*v;
730     m[i][3] = s*u+c*v;
731   }
732 }
733
734
735 /* Add a rotation around the yz-plane to the matrix m. */
736 static void rotateyz(float m[4][4], float phi)
737 {
738   float c, s, u, v;
739   int i;
740
741   phi *= M_PI/180.0;
742   c = cos(phi);
743   s = sin(phi);
744   for (i=0; i<4; i++)
745   {
746     u = m[i][0];
747     v = m[i][3];
748     m[i][0] = c*u-s*v;
749     m[i][3] = s*u+c*v;
750   }
751 }
752
753
754 /* Compute the rotation matrix m from the rotation angles. */
755 static void rotateall(float al, float be, float de, float ze, float et,
756                       float th, float m[4][4])
757 {
758   int i, j;
759
760   for (i=0; i<4; i++)
761     for (j=0; j<4; j++)
762       m[i][j] = (i==j);
763   rotatewx(m,al);
764   rotatewy(m,be);
765   rotatewz(m,de);
766   rotatexy(m,ze);
767   rotatexz(m,et);
768   rotateyz(m,th);
769 }
770
771
772 /* Compute the rotation matrix m from the 4d rotation angles. */
773 static void rotateall4d(float ze, float et, float th, float m[4][4])
774 {
775   int i, j;
776
777   for (i=0; i<4; i++)
778     for (j=0; j<4; j++)
779       m[i][j] = (i==j);
780   rotatexy(m,ze);
781   rotatexz(m,et);
782   rotateyz(m,th);
783 }
784
785
786 /* Multiply two rotation matrices: o=m*n. */
787 static void mult_rotmat(float m[4][4], float n[4][4], float o[4][4])
788 {
789   int i, j, k;
790
791   for (i=0; i<4; i++)
792   {
793     for (j=0; j<4; j++)
794     {
795       o[i][j] = 0.0;
796       for (k=0; k<4; k++)
797         o[i][j] += m[i][k]*n[k][j];
798     }
799   }
800 }
801
802
803 /* Compute a 4D rotation matrix from two unit quaternions. */
804 static void quats_to_rotmat(float p[4], float q[4], float m[4][4])
805 {
806   double al, be, de, ze, et, th;
807   double r00, r01, r02, r12, r22;
808
809   r00 = 1.0-2.0*(p[1]*p[1]+p[2]*p[2]);
810   r01 = 2.0*(p[0]*p[1]+p[2]*p[3]);
811   r02 = 2.0*(p[2]*p[0]-p[1]*p[3]);
812   r12 = 2.0*(p[1]*p[2]+p[0]*p[3]);
813   r22 = 1.0-2.0*(p[1]*p[1]+p[0]*p[0]);
814
815   al = atan2(-r12,r22)*180.0/M_PI;
816   be = atan2(r02,sqrt(r00*r00+r01*r01))*180.0/M_PI;
817   de = atan2(-r01,r00)*180.0/M_PI;
818
819   r00 = 1.0-2.0*(q[1]*q[1]+q[2]*q[2]);
820   r01 = 2.0*(q[0]*q[1]+q[2]*q[3]);
821   r02 = 2.0*(q[2]*q[0]-q[1]*q[3]);
822   r12 = 2.0*(q[1]*q[2]+q[0]*q[3]);
823   r22 = 1.0-2.0*(q[1]*q[1]+q[0]*q[0]);
824
825   et = atan2(-r12,r22)*180.0/M_PI;
826   th = atan2(r02,sqrt(r00*r00+r01*r01))*180.0/M_PI;
827   ze = atan2(-r01,r00)*180.0/M_PI;
828
829   rotateall(al,be,de,ze,et,-th,m);
830 }
831
832
833 /* Compute a fully saturated and bright color based on an angle. */
834 static void color(double angle, float col[4])
835 {
836   int s;
837   double t;
838
839   if (colors == COLORS_TWOSIDED)
840     return;
841
842   if (angle >= 0.0)
843     angle = fmod(angle,2.0*M_PI);
844   else
845     angle = fmod(angle,-2.0*M_PI);
846   s = floor(angle/(M_PI/3));
847   t = angle/(M_PI/3)-s;
848   if (s >= 6)
849     s = 0;
850   switch (s)
851   {
852     case 0:
853       col[0] = 1.0;
854       col[1] = t;
855       col[2] = 0.0;
856       break;
857     case 1:
858       col[0] = 1.0-t;
859       col[1] = 1.0;
860       col[2] = 0.0;
861       break;
862     case 2:
863       col[0] = 0.0;
864       col[1] = 1.0;
865       col[2] = t;
866       break;
867     case 3:
868       col[0] = 0.0;
869       col[1] = 1.0-t;
870       col[2] = 1.0;
871       break;
872     case 4:
873       col[0] = t;
874       col[1] = 0.0;
875       col[2] = 1.0;
876       break;
877     case 5:
878       col[0] = 1.0;
879       col[1] = 0.0;
880       col[2] = 1.0-t;
881       break;
882   }
883   if (display_mode == DISP_TRANSPARENT)
884     col[3] = 0.7;
885   else
886     col[3] = 1.0;
887 }
888
889
890 /* Set up the projective plane coordinates, colors, and texture. */
891 static void setup_projective_plane(ModeInfo *mi, double umin, double umax,
892                                    double vmin, double vmax)
893 {
894   int i, j, k;
895   double u, v, ur, vr;
896   double cu, su, cv2, sv2, cv4, sv4, c2u, s2u;
897   projectiveplanestruct *pp = &projectiveplane[MI_SCREEN(mi)];
898
899   ur = umax-umin;
900   vr = vmax-vmin;
901   for (i=0; i<=NUMV; i++)
902   {
903     for (j=0; j<=NUMU; j++)
904     {
905       k = i*(NUMU+1)+j;
906       if (appearance != APPEARANCE_DIRECTION_BANDS)
907         u = -ur*j/NUMU+umin;
908       else
909         u = ur*j/NUMU+umin;
910       v = vr*i/NUMV+vmin;
911       cu = cos(u);
912       su = sin(u);
913       c2u = cos(2.0*u);
914       s2u = sin(2.0*u);
915       sv2 = sin(0.5*v);
916       cv4 = cos(0.25*v);
917       sv4 = sin(0.25*v);
918       if (colors == COLORS_DEPTH)
919         color(((su*su*sv4*sv4-cv4*cv4)+1.0)*M_PI*2.0/3.0,pp->col[k]);
920       else if (colors == COLORS_DIRECTION)
921         color(2.0*M_PI+fmod(2.0*u,2.0*M_PI),pp->col[k]);
922       else /* colors == COLORS_DISTANCE */
923         color(v*(5.0/6.0),pp->col[k]);
924       pp->tex[k][0] = -32*u/(2.0*M_PI);
925       if (appearance != APPEARANCE_DISTANCE_BANDS)
926         pp->tex[k][1] = 32*v/(2.0*M_PI);
927       else
928         pp->tex[k][1] = 32*v/(2.0*M_PI)-0.5;
929       pp->x[k][0] = 0.5*s2u*sv4*sv4;
930       pp->x[k][1] = 0.5*su*sv2;
931       pp->x[k][2] = 0.5*cu*sv2;
932       pp->x[k][3] = 0.5*(su*su*sv4*sv4-cv4*cv4);
933       /* Avoid degenerate tangential plane basis vectors. */
934       if (v < FLT_EPSILON)
935         v = FLT_EPSILON;
936       cv2 = cos(0.5*v);
937       sv2 = sin(0.5*v);
938       sv4 = sin(0.25*v);
939       pp->xu[k][0] = c2u*sv4*sv4;
940       pp->xu[k][1] = 0.5*cu*sv2;
941       pp->xu[k][2] = -0.5*su*sv2;
942       pp->xu[k][3] = 0.5*s2u*sv4*sv4;
943       pp->xv[k][0] = 0.125*s2u*sv2;
944       pp->xv[k][1] = 0.25*su*cv2;
945       pp->xv[k][2] = 0.25*cu*cv2;
946       pp->xv[k][3] = 0.125*(su*su+1.0)*sv2;
947     }
948   }
949 }
950
951
952 /* Draw a 4d embedding of the projective plane projected into 3D. */
953 static int projective_plane(ModeInfo *mi, double umin, double umax,
954                             double vmin, double vmax)
955 {
956   int polys = 0;
957   static const GLfloat mat_diff_red[]         = { 1.0, 0.0, 0.0, 1.0 };
958   static const GLfloat mat_diff_green[]       = { 0.0, 1.0, 0.0, 1.0 };
959   static const GLfloat mat_diff_trans_red[]   = { 1.0, 0.0, 0.0, 0.7 };
960   static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
961   float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[4][4];
962   int i, j, k, l, m, o;
963   double u, v;
964   double xx[4], xxu[4], xxv[4], y[4], yu[4], yv[4];
965   double q, r, s, t;
966   double cu, su, cv2, sv2, cv4, sv4, c2u, s2u;
967   float q1[4], q2[4], r1[4][4], r2[4][4];
968   projectiveplanestruct *pp = &projectiveplane[MI_SCREEN(mi)];
969
970   if (view == VIEW_WALK || view == VIEW_WALKTURN)
971   {
972     /* Compute the rotation that rotates the projective plane in 4D without
973        the trackball rotations. */
974     rotateall4d(pp->zeta,pp->eta,pp->theta,mat);
975
976     u = pp->umove;
977     v = pp->vmove;
978     cu = cos(u);
979     su = sin(u);
980     c2u = cos(2.0*u);
981     s2u = sin(2.0*u);
982     sv2 = sin(0.5*v);
983     cv4 = cos(0.25*v);
984     sv4 = sin(0.25*v);
985     xx[0] = 0.5*s2u*sv4*sv4;
986     xx[1] = 0.5*su*sv2;
987     xx[2] = 0.5*cu*sv2;
988     xx[3] = 0.5*(su*su*sv4*sv4-cv4*cv4);
989     /* Avoid degenerate tangential plane basis vectors. */
990     if (v < FLT_EPSILON)
991       v = FLT_EPSILON;
992     cv2 = cos(0.5*v);
993     sv2 = sin(0.5*v);
994     sv4 = sin(0.25*v);
995     xxu[0] = c2u*sv4*sv4;
996     xxu[1] = 0.5*cu*sv2;
997     xxu[2] = -0.5*su*sv2;
998     xxu[3] = 0.5*s2u*sv4*sv4;
999     xxv[0] = 0.125*s2u*sv2;
1000     xxv[1] = 0.25*su*cv2;
1001     xxv[2] = 0.25*cu*cv2;
1002     xxv[3] = 0.125*(su*su+1.0)*sv2;
1003     for (l=0; l<4; l++)
1004     {
1005       y[l] = (mat[l][0]*xx[0]+mat[l][1]*xx[1]+
1006               mat[l][2]*xx[2]+mat[l][3]*xx[3]);
1007       yu[l] = (mat[l][0]*xxu[0]+mat[l][1]*xxu[1]+
1008                mat[l][2]*xxu[2]+mat[l][3]*xxu[3]);
1009       yv[l] = (mat[l][0]*xxv[0]+mat[l][1]*xxv[1]+
1010                mat[l][2]*xxv[2]+mat[l][3]*xxv[3]);
1011     }
1012     if (projection_4d == DISP_4D_ORTHOGRAPHIC)
1013     {
1014       for (l=0; l<3; l++)
1015       {
1016         p[l] = y[l]+pp->offset4d[l];
1017         pu[l] = yu[l];
1018         pv[l] = yv[l];
1019       }
1020     }
1021     else
1022     {
1023       s = y[3]+pp->offset4d[3];
1024       q = 1.0/s;
1025       t = q*q;
1026       for (l=0; l<3; l++)
1027       {
1028         r = y[l]+pp->offset4d[l];
1029         p[l] = r*q;
1030         pu[l] = (yu[l]*s-r*yu[3])*t;
1031         pv[l] = (yv[l]*s-r*yv[3])*t;
1032       }
1033     }
1034     n[0] = pu[1]*pv[2]-pu[2]*pv[1];
1035     n[1] = pu[2]*pv[0]-pu[0]*pv[2];
1036     n[2] = pu[0]*pv[1]-pu[1]*pv[0];
1037     t = 1.0/(pp->side*4.0*sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]));
1038     n[0] *= t;
1039     n[1] *= t;
1040     n[2] *= t;
1041     pm[0] = pu[0]*pp->dumove+pv[0]*pp->dvmove;
1042     pm[1] = pu[1]*pp->dumove+pv[1]*pp->dvmove;
1043     pm[2] = pu[2]*pp->dumove+pv[2]*pp->dvmove;
1044     t = 1.0/(4.0*sqrt(pm[0]*pm[0]+pm[1]*pm[1]+pm[2]*pm[2]));
1045     pm[0] *= t;
1046     pm[1] *= t;
1047     pm[2] *= t;
1048     b[0] = n[1]*pm[2]-n[2]*pm[1];
1049     b[1] = n[2]*pm[0]-n[0]*pm[2];
1050     b[2] = n[0]*pm[1]-n[1]*pm[0];
1051     t = 1.0/(4.0*sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]));
1052     b[0] *= t;
1053     b[1] *= t;
1054     b[2] *= t;
1055
1056     /* Compute alpha, beta, delta from the three basis vectors.
1057            |  -b[0]  -b[1]  -b[2] |
1058        m = |   n[0]   n[1]   n[2] |
1059            | -pm[0] -pm[1] -pm[2] |
1060     */
1061     pp->alpha = atan2(-n[2],-pm[2])*180/M_PI;
1062     pp->beta = atan2(-b[2],sqrt(b[0]*b[0]+b[1]*b[1]))*180/M_PI;
1063     pp->delta = atan2(b[1],-b[0])*180/M_PI;
1064
1065     /* Compute the rotation that rotates the projective plane in 4D. */
1066     rotateall(pp->alpha,pp->beta,pp->delta,pp->zeta,pp->eta,pp->theta,mat);
1067
1068     u = pp->umove;
1069     v = pp->vmove;
1070     cu = cos(u);
1071     su = sin(u);
1072     s2u = sin(2.0*u);
1073     sv2 = sin(0.5*v);
1074     cv4 = cos(0.25*v);
1075     sv4 = sin(0.25*v);
1076     xx[0] = 0.5*s2u*sv4*sv4;
1077     xx[1] = 0.5*su*sv2;
1078     xx[2] = 0.5*cu*sv2;
1079     xx[3] = 0.5*(su*su*sv4*sv4-cv4*cv4);
1080     for (l=0; l<4; l++)
1081     {
1082       r = 0.0;
1083       for (m=0; m<4; m++)
1084         r += mat[l][m]*xx[m];
1085       y[l] = r;
1086     }
1087     if (projection_4d == DISP_4D_ORTHOGRAPHIC)
1088     {
1089       for (l=0; l<3; l++)
1090         p[l] = y[l]+pp->offset4d[l];
1091     }
1092     else
1093     {
1094       s = y[3]+pp->offset4d[3];
1095       for (l=0; l<3; l++)
1096         p[l] = (y[l]+pp->offset4d[l])/s;
1097     }
1098
1099     pp->offset3d[0] = -p[0];
1100     pp->offset3d[1] = -p[1]-DELTAY;
1101     pp->offset3d[2] = -p[2];
1102   }
1103   else
1104   {
1105     /* Compute the rotation that rotates the projective plane in 4D,
1106        including the trackball rotations. */
1107     rotateall(pp->alpha,pp->beta,pp->delta,pp->zeta,pp->eta,pp->theta,r1);
1108
1109     gltrackball_get_quaternion(pp->trackballs[0],q1);
1110     gltrackball_get_quaternion(pp->trackballs[1],q2);
1111     quats_to_rotmat(q1,q2,r2);
1112
1113     mult_rotmat(r2,r1,mat);
1114   }
1115
1116   /* Project the points from 4D to 3D. */
1117   for (i=0; i<=NUMV; i++)
1118   {
1119     for (j=0; j<=NUMU; j++)
1120     {
1121       o = i*(NUMU+1)+j;
1122       for (l=0; l<4; l++)
1123       {
1124         y[l] = (mat[l][0]*pp->x[o][0]+mat[l][1]*pp->x[o][1]+
1125                 mat[l][2]*pp->x[o][2]+mat[l][3]*pp->x[o][3]);
1126         yu[l] = (mat[l][0]*pp->xu[o][0]+mat[l][1]*pp->xu[o][1]+
1127                  mat[l][2]*pp->xu[o][2]+mat[l][3]*pp->xu[o][3]);
1128         yv[l] = (mat[l][0]*pp->xv[o][0]+mat[l][1]*pp->xv[o][1]+
1129                  mat[l][2]*pp->xv[o][2]+mat[l][3]*pp->xv[o][3]);
1130       }
1131       if (projection_4d == DISP_4D_ORTHOGRAPHIC)
1132       {
1133         for (l=0; l<3; l++)
1134         {
1135           pp->pp[o][l] = (y[l]+pp->offset4d[l])+pp->offset3d[l];
1136           pu[l] = yu[l];
1137           pv[l] = yv[l];
1138         }
1139       }
1140       else
1141       {
1142         s = y[3]+pp->offset4d[3];
1143         q = 1.0/s;
1144         t = q*q;
1145         for (l=0; l<3; l++)
1146         {
1147           r = y[l]+pp->offset4d[l];
1148           pp->pp[o][l] = r*q+pp->offset3d[l];
1149           pu[l] = (yu[l]*s-r*yu[3])*t;
1150           pv[l] = (yv[l]*s-r*yv[3])*t;
1151         }
1152       }
1153       pp->pn[o][0] = pu[1]*pv[2]-pu[2]*pv[1];
1154       pp->pn[o][1] = pu[2]*pv[0]-pu[0]*pv[2];
1155       pp->pn[o][2] = pu[0]*pv[1]-pu[1]*pv[0];
1156       t = 1.0/sqrt(pp->pn[o][0]*pp->pn[o][0]+pp->pn[o][1]*pp->pn[o][1]+
1157                    pp->pn[o][2]*pp->pn[o][2]);
1158       pp->pn[o][0] *= t;
1159       pp->pn[o][1] *= t;
1160       pp->pn[o][2] *= t;
1161     }
1162   }
1163
1164   if (colors == COLORS_TWOSIDED)
1165   {
1166     glColor3fv(mat_diff_red);
1167     if (display_mode == DISP_TRANSPARENT)
1168     {
1169       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
1170       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
1171     }
1172     else
1173     {
1174       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
1175       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
1176     }
1177   }
1178   glBindTexture(GL_TEXTURE_2D,pp->tex_name);
1179
1180   if (appearance != APPEARANCE_DIRECTION_BANDS)
1181   {
1182     for (i=0; i<NUMV; i++)
1183     {
1184       if (appearance == APPEARANCE_DISTANCE_BANDS &&
1185           ((i & (NUMB-1)) >= NUMB/4) && ((i & (NUMB-1)) < 3*NUMB/4))
1186         continue;
1187       if (display_mode == DISP_WIREFRAME)
1188         glBegin(GL_QUAD_STRIP);
1189       else
1190         glBegin(GL_TRIANGLE_STRIP);
1191       for (j=0; j<=NUMU; j++)
1192       {
1193         for (k=0; k<=1; k++)
1194         {
1195           l = (i+k);
1196           m = j;
1197           o = l*(NUMU+1)+m;
1198           glNormal3fv(pp->pn[o]);
1199           glTexCoord2fv(pp->tex[o]);
1200           if (colors != COLORS_TWOSIDED)
1201           {
1202             glColor3fv(pp->col[o]);
1203             glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,pp->col[o]);
1204           }
1205           glVertex3fv(pp->pp[o]);
1206           polys++;
1207         }
1208       }
1209       glEnd();
1210     }
1211   }
1212   else /* appearance == APPEARANCE_DIRECTION_BANDS */
1213   {
1214     for (j=0; j<NUMU; j++)
1215     {
1216       if ((j & (NUMB-1)) >= NUMB/2)
1217         continue;
1218       if (display_mode == DISP_WIREFRAME)
1219         glBegin(GL_QUAD_STRIP);
1220       else
1221         glBegin(GL_TRIANGLE_STRIP);
1222       for (i=0; i<=NUMV; i++)
1223       {
1224         for (k=0; k<=1; k++)
1225         {
1226           l = i;
1227           m = (j+k);
1228           o = l*(NUMU+1)+m;
1229           glNormal3fv(pp->pn[o]);
1230           glTexCoord2fv(pp->tex[o]);
1231           if (colors != COLORS_TWOSIDED)
1232           {
1233             glColor3fv(pp->col[o]);
1234             glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,pp->col[o]);
1235           }
1236           glVertex3fv(pp->pp[o]);
1237           polys++;
1238         }
1239       }
1240       glEnd();
1241     }
1242   }
1243
1244   polys /= 2;
1245   return polys;
1246 }
1247
1248
1249 /* Generate a texture image that shows the orientation reversal. */
1250 static void gen_texture(ModeInfo *mi)
1251 {
1252   projectiveplanestruct *pp = &projectiveplane[MI_SCREEN(mi)];
1253
1254   glGenTextures(1,&pp->tex_name);
1255   glBindTexture(GL_TEXTURE_2D,pp->tex_name);
1256   glPixelStorei(GL_UNPACK_ALIGNMENT,1);
1257   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
1258   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
1259   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
1260   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
1261   glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
1262   glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,TEX_DIMENSION,TEX_DIMENSION,0,
1263                GL_LUMINANCE,GL_UNSIGNED_BYTE,texture);
1264 }
1265
1266
1267 static void init(ModeInfo *mi)
1268 {
1269   static const GLfloat light_ambient[]  = { 0.0, 0.0, 0.0, 1.0 };
1270   static const GLfloat light_diffuse[]  = { 1.0, 1.0, 1.0, 1.0 };
1271   static const GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
1272   static const GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
1273   static const GLfloat mat_specular[]   = { 1.0, 1.0, 1.0, 1.0 };
1274   projectiveplanestruct *pp = &projectiveplane[MI_SCREEN(mi)];
1275
1276   if (walk_speed == 0.0)
1277     walk_speed = 20.0;
1278
1279   if (view == VIEW_TURN)
1280   {
1281     pp->alpha = frand(360.0);
1282     pp->beta = frand(360.0);
1283     pp->delta = frand(360.0);
1284     pp->zeta = 0.0;
1285     pp->eta = 0.0;
1286     pp->theta = 0.0;
1287   }
1288   else
1289   {
1290     pp->alpha = 0.0;
1291     pp->beta = 0.0;
1292     pp->delta = 0.0;
1293     pp->zeta = 120.0;
1294     pp->eta = 180.0;
1295     pp->theta = 90.0;
1296   }
1297   pp->umove = frand(2.0*M_PI);
1298   pp->vmove = frand(2.0*M_PI);
1299   pp->dumove = 0.0;
1300   pp->dvmove = 0.0;
1301   pp->side = 1;
1302   if (sin(walk_direction*M_PI/180.0) >= 0.0)
1303     pp->dir = 1;
1304   else
1305     pp->dir = -1;
1306
1307   pp->offset4d[0] = 0.0;
1308   pp->offset4d[1] = 0.0;
1309   pp->offset4d[2] = 0.0;
1310   pp->offset4d[3] = 1.2;
1311   pp->offset3d[0] = 0.0;
1312   pp->offset3d[1] = 0.0;
1313   pp->offset3d[2] = -1.2;
1314   pp->offset3d[3] = 0.0;
1315
1316   gen_texture(mi);
1317   setup_projective_plane(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
1318
1319   if (marks)
1320     glEnable(GL_TEXTURE_2D);
1321   else
1322     glDisable(GL_TEXTURE_2D);
1323
1324   glMatrixMode(GL_PROJECTION);
1325   glLoadIdentity();
1326   if (projection_3d == DISP_3D_PERSPECTIVE ||
1327       view == VIEW_WALK || view == VIEW_WALKTURN)
1328   {
1329     if (view == VIEW_WALK || view == VIEW_WALKTURN)
1330       gluPerspective(60.0,1.0,0.01,10.0);
1331     else
1332       gluPerspective(60.0,1.0,0.1,10.0);
1333   }
1334   else
1335   {
1336     glOrtho(-0.6,0.6,-0.6,0.6,0.1,10.0);
1337   }
1338   glMatrixMode(GL_MODELVIEW);
1339   glLoadIdentity();
1340
1341 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
1342   if (display_mode == DISP_WIREFRAME)
1343     display_mode = DISP_SURFACE;
1344 # endif
1345
1346   if (display_mode == DISP_SURFACE)
1347   {
1348     glEnable(GL_DEPTH_TEST);
1349     glDepthFunc(GL_LESS);
1350     glShadeModel(GL_SMOOTH);
1351     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
1352     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
1353     glEnable(GL_LIGHTING);
1354     glEnable(GL_LIGHT0);
1355     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
1356     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
1357     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
1358     glLightfv(GL_LIGHT0,GL_POSITION,light_position);
1359     glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
1360     glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
1361     glDepthMask(GL_TRUE);
1362     glDisable(GL_BLEND);
1363   }
1364   else if (display_mode == DISP_TRANSPARENT)
1365   {
1366     glDisable(GL_DEPTH_TEST);
1367     glShadeModel(GL_SMOOTH);
1368     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
1369     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
1370     glEnable(GL_LIGHTING);
1371     glEnable(GL_LIGHT0);
1372     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
1373     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
1374     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
1375     glLightfv(GL_LIGHT0,GL_POSITION,light_position);
1376     glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
1377     glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
1378     glDepthMask(GL_FALSE);
1379     glEnable(GL_BLEND);
1380     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
1381   }
1382   else  /* display_mode == DISP_WIREFRAME */
1383   {
1384     glDisable(GL_DEPTH_TEST);
1385     glShadeModel(GL_FLAT);
1386     glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
1387     glDisable(GL_LIGHTING);
1388     glDisable(GL_LIGHT0);
1389     glDisable(GL_BLEND);
1390   }
1391 }
1392
1393
1394 /* Redisplay the Klein bottle. */
1395 static void display_projectiveplane(ModeInfo *mi)
1396 {
1397   projectiveplanestruct *pp = &projectiveplane[MI_SCREEN(mi)];
1398
1399   if (!pp->button_pressed)
1400   {
1401     if (view == VIEW_TURN)
1402     {
1403       pp->alpha += speed_wx * pp->speed_scale;
1404       if (pp->alpha >= 360.0)
1405         pp->alpha -= 360.0;
1406       pp->beta += speed_wy * pp->speed_scale;
1407       if (pp->beta >= 360.0)
1408         pp->beta -= 360.0;
1409       pp->delta += speed_wz * pp->speed_scale;
1410       if (pp->delta >= 360.0)
1411         pp->delta -= 360.0;
1412       pp->zeta += speed_xy * pp->speed_scale;
1413       if (pp->zeta >= 360.0)
1414         pp->zeta -= 360.0;
1415       pp->eta += speed_xz * pp->speed_scale;
1416       if (pp->eta >= 360.0)
1417         pp->eta -= 360.0;
1418       pp->theta += speed_yz * pp->speed_scale;
1419       if (pp->theta >= 360.0)
1420         pp->theta -= 360.0;
1421     }
1422     if (view == VIEW_WALKTURN)
1423     {
1424       pp->zeta += speed_xy * pp->speed_scale;
1425       if (pp->zeta >= 360.0)
1426         pp->zeta -= 360.0;
1427       pp->eta += speed_xz * pp->speed_scale;
1428       if (pp->eta >= 360.0)
1429         pp->eta -= 360.0;
1430       pp->theta += speed_yz * pp->speed_scale;
1431       if (pp->theta >= 360.0)
1432         pp->theta -= 360.0;
1433     }
1434     if (view == VIEW_WALK || view == VIEW_WALKTURN)
1435     {
1436       pp->dvmove = (pp->dir*sin(walk_direction*M_PI/180.0)*
1437                     walk_speed*M_PI/4096.0);
1438       pp->vmove += pp->dvmove;
1439       if (pp->vmove > 2.0*M_PI)
1440       {
1441         pp->vmove = 4.0*M_PI-pp->vmove;
1442         pp->umove = pp->umove-M_PI;
1443         if (pp->umove < 0.0)
1444           pp->umove += 2.0*M_PI;
1445         pp->side = -pp->side;
1446         pp->dir = -pp->dir;
1447         pp->dvmove = -pp->dvmove;
1448       }
1449       if (pp->vmove < 0.0)
1450       {
1451         pp->vmove = -pp->vmove;
1452         pp->umove = pp->umove-M_PI;
1453         if (pp->umove < 0.0)
1454           pp->umove += 2.0*M_PI;
1455         pp->dir = -pp->dir;
1456         pp->dvmove = -pp->dvmove;
1457       }
1458       pp->dumove = cos(walk_direction*M_PI/180.0)*walk_speed*M_PI/4096.0;
1459       pp->umove += pp->dumove;
1460       if (pp->umove >= 2.0*M_PI)
1461         pp->umove -= 2.0*M_PI;
1462       if (pp->umove < 0.0)
1463         pp->umove += 2.0*M_PI;
1464     }
1465   }
1466
1467   glMatrixMode(GL_PROJECTION);
1468   glLoadIdentity();
1469   if (projection_3d == DISP_3D_PERSPECTIVE ||
1470       view == VIEW_WALK || view == VIEW_WALKTURN)
1471   {
1472     if (view == VIEW_WALK || view == VIEW_WALKTURN)
1473       gluPerspective(60.0,pp->aspect,0.01,10.0);
1474     else
1475       gluPerspective(60.0,pp->aspect,0.1,10.0);
1476   }
1477   else
1478   {
1479     if (pp->aspect >= 1.0)
1480       glOrtho(-0.6*pp->aspect,0.6*pp->aspect,-0.6,0.6,0.1,10.0);
1481     else
1482       glOrtho(-0.6,0.6,-0.6/pp->aspect,0.6/pp->aspect,0.1,10.0);
1483   }
1484   glMatrixMode(GL_MODELVIEW);
1485   glLoadIdentity();
1486
1487   mi->polygon_count = projective_plane(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
1488 }
1489
1490
1491 ENTRYPOINT void reshape_projectiveplane(ModeInfo *mi, int width, int height)
1492 {
1493   projectiveplanestruct *pp = &projectiveplane[MI_SCREEN(mi)];
1494
1495   pp->WindW = (GLint)width;
1496   pp->WindH = (GLint)height;
1497   glViewport(0,0,width,height);
1498   pp->aspect = (GLfloat)width/(GLfloat)height;
1499 }
1500
1501
1502 ENTRYPOINT Bool projectiveplane_handle_event(ModeInfo *mi, XEvent *event)
1503 {
1504   projectiveplanestruct *pp = &projectiveplane[MI_SCREEN(mi)];
1505   KeySym  sym = 0;
1506   char c = 0;
1507
1508   if (event->xany.type == KeyPress || event->xany.type == KeyRelease)
1509     XLookupString (&event->xkey, &c, 1, &sym, 0);
1510
1511   if (event->xany.type == ButtonPress &&
1512       event->xbutton.button == Button1)
1513   {
1514     pp->button_pressed = True;
1515     gltrackball_start(pp->trackballs[pp->current_trackball],
1516                       event->xbutton.x, event->xbutton.y,
1517                       MI_WIDTH(mi), MI_HEIGHT(mi));
1518     return True;
1519   }
1520   else if (event->xany.type == ButtonRelease &&
1521            event->xbutton.button == Button1)
1522   {
1523     pp->button_pressed = False;
1524     return True;
1525   }
1526   else if (event->xany.type == KeyPress)
1527   {
1528     if (sym == XK_Shift_L || sym == XK_Shift_R)
1529     {
1530       pp->current_trackball = 1;
1531       if (pp->button_pressed)
1532         gltrackball_start(pp->trackballs[pp->current_trackball],
1533                           event->xbutton.x, event->xbutton.y,
1534                           MI_WIDTH(mi), MI_HEIGHT(mi));
1535       return True;
1536     }
1537   }
1538   else if (event->xany.type == KeyRelease)
1539   {
1540     if (sym == XK_Shift_L || sym == XK_Shift_R)
1541     {
1542       pp->current_trackball = 0;
1543       if (pp->button_pressed)
1544         gltrackball_start(pp->trackballs[pp->current_trackball],
1545                           event->xbutton.x, event->xbutton.y,
1546                           MI_WIDTH(mi), MI_HEIGHT(mi));
1547       return True;
1548     }
1549   }
1550   else if (event->xany.type == MotionNotify && pp->button_pressed)
1551   {
1552     gltrackball_track(pp->trackballs[pp->current_trackball],
1553                       event->xmotion.x, event->xmotion.y,
1554                       MI_WIDTH(mi), MI_HEIGHT(mi));
1555     return True;
1556   }
1557
1558   return False;
1559 }
1560
1561
1562 /*
1563  *-----------------------------------------------------------------------------
1564  *-----------------------------------------------------------------------------
1565  *    Xlock hooks.
1566  *-----------------------------------------------------------------------------
1567  *-----------------------------------------------------------------------------
1568  */
1569
1570 /*
1571  *-----------------------------------------------------------------------------
1572  *    Initialize projectiveplane.  Called each time the window changes.
1573  *-----------------------------------------------------------------------------
1574  */
1575
1576 ENTRYPOINT void init_projectiveplane(ModeInfo *mi)
1577 {
1578   projectiveplanestruct *pp;
1579
1580   if (projectiveplane == NULL)
1581   {
1582     projectiveplane =
1583       (projectiveplanestruct *)calloc(MI_NUM_SCREENS(mi),
1584                                       sizeof(projectiveplanestruct));
1585     if (projectiveplane == NULL)
1586       return;
1587   }
1588   pp = &projectiveplane[MI_SCREEN(mi)];
1589
1590   
1591   pp->trackballs[0] = gltrackball_init(True);
1592   pp->trackballs[1] = gltrackball_init(True);
1593   pp->current_trackball = 0;
1594   pp->button_pressed = False;
1595
1596   /* Set the display mode. */
1597   if (!strcasecmp(mode,"random"))
1598   {
1599     display_mode = random() % NUM_DISPLAY_MODES;
1600   }
1601   else if (!strcasecmp(mode,"wireframe"))
1602   {
1603     display_mode = DISP_WIREFRAME;
1604   }
1605   else if (!strcasecmp(mode,"surface"))
1606   {
1607     display_mode = DISP_SURFACE;
1608   }
1609   else if (!strcasecmp(mode,"transparent"))
1610   {
1611     display_mode = DISP_TRANSPARENT;
1612   }
1613   else
1614   {
1615     display_mode = random() % NUM_DISPLAY_MODES;
1616   }
1617
1618   /* Orientation marks don't make sense in wireframe mode. */
1619   if (display_mode == DISP_WIREFRAME)
1620     marks = False;
1621
1622   /* Set the appearance. */
1623   if (!strcasecmp(appear,"random"))
1624   {
1625     appearance = random() % NUM_APPEARANCES;
1626   }
1627   else if (!strcasecmp(appear,"solid"))
1628   {
1629     appearance = APPEARANCE_SOLID;
1630   }
1631   else if (!strcasecmp(appear,"distance-bands"))
1632   {
1633     appearance = APPEARANCE_DISTANCE_BANDS;
1634   }
1635   else if (!strcasecmp(appear,"direction-bands"))
1636   {
1637     appearance = APPEARANCE_DIRECTION_BANDS;
1638   }
1639   else
1640   {
1641     appearance = random() % NUM_APPEARANCES;
1642   }
1643
1644   /* Set the color mode. */
1645   if (!strcasecmp(color_mode,"random"))
1646   {
1647     colors = random() % NUM_COLORS;
1648   }
1649   else if (!strcasecmp(color_mode,"two-sided"))
1650   {
1651     colors = COLORS_TWOSIDED;
1652   }
1653   else if (!strcasecmp(color_mode,"distance"))
1654   {
1655     colors = COLORS_DISTANCE;
1656   }
1657   else if (!strcasecmp(color_mode,"direction"))
1658   {
1659     colors = COLORS_DIRECTION;
1660   }
1661   else if (!strcasecmp(color_mode,"depth"))
1662   {
1663     colors = COLORS_DEPTH;
1664   }
1665   else
1666   {
1667     colors = random() % NUM_COLORS;
1668   }
1669
1670   /* Set the view mode. */
1671   if (!strcasecmp(view_mode,"random"))
1672   {
1673     view = random() % NUM_VIEW_MODES;
1674   }
1675   else if (!strcasecmp(view_mode,"walk"))
1676   {
1677     view = VIEW_WALK;
1678   }
1679   else if (!strcasecmp(view_mode,"turn"))
1680   {
1681     view = VIEW_TURN;
1682   }
1683   else if (!strcasecmp(view_mode,"walk-turn"))
1684   {
1685     view = VIEW_WALKTURN;
1686   }
1687   else
1688   {
1689     view = random() % NUM_VIEW_MODES;
1690   }
1691
1692   /* Set the 3d projection mode. */
1693   if (!strcasecmp(proj_3d,"random"))
1694   {
1695     /* Orthographic projection only makes sense in turn mode. */
1696     if (view == VIEW_TURN)
1697       projection_3d = random() % NUM_DISP_3D_MODES;
1698     else
1699       projection_3d = DISP_3D_PERSPECTIVE;
1700   }
1701   else if (!strcasecmp(proj_3d,"perspective"))
1702   {
1703     projection_3d = DISP_3D_PERSPECTIVE;
1704   }
1705   else if (!strcasecmp(proj_3d,"orthographic"))
1706   {
1707     projection_3d = DISP_3D_ORTHOGRAPHIC;
1708   }
1709   else
1710   {
1711     /* Orthographic projection only makes sense in turn mode. */
1712     if (view == VIEW_TURN)
1713       projection_3d = random() % NUM_DISP_3D_MODES;
1714     else
1715       projection_3d = DISP_3D_PERSPECTIVE;
1716   }
1717
1718   /* Set the 4d projection mode. */
1719   if (!strcasecmp(proj_4d,"random"))
1720   {
1721     projection_4d = random() % NUM_DISP_4D_MODES;
1722   }
1723   else if (!strcasecmp(proj_4d,"perspective"))
1724   {
1725     projection_4d = DISP_4D_PERSPECTIVE;
1726   }
1727   else if (!strcasecmp(proj_4d,"orthographic"))
1728   {
1729     projection_4d = DISP_4D_ORTHOGRAPHIC;
1730   }
1731   else
1732   {
1733     projection_4d = random() % NUM_DISP_4D_MODES;
1734   }
1735
1736   /* Modify the speeds to a useful range in walk-and-turn mode. */
1737   if (view == VIEW_WALKTURN)
1738   {
1739     speed_wx *= 0.2;
1740     speed_wy *= 0.2;
1741     speed_wz *= 0.2;
1742     speed_xy *= 0.2;
1743     speed_xz *= 0.2;
1744     speed_yz *= 0.2;
1745   }
1746
1747   /* make multiple screens rotate at slightly different rates. */
1748   pp->speed_scale = 0.9 + frand(0.3);
1749
1750   if ((pp->glx_context = init_GL(mi)) != NULL)
1751   {
1752     reshape_projectiveplane(mi,MI_WIDTH(mi),MI_HEIGHT(mi));
1753     glDrawBuffer(GL_BACK);
1754     init(mi);
1755   }
1756   else
1757   {
1758     MI_CLEARWINDOW(mi);
1759   }
1760 }
1761
1762 /*
1763  *-----------------------------------------------------------------------------
1764  *    Called by the mainline code periodically to update the display.
1765  *-----------------------------------------------------------------------------
1766  */
1767 ENTRYPOINT void draw_projectiveplane(ModeInfo *mi)
1768 {
1769   Display          *display = MI_DISPLAY(mi);
1770   Window           window = MI_WINDOW(mi);
1771   projectiveplanestruct *pp;
1772
1773   if (projectiveplane == NULL)
1774     return;
1775   pp = &projectiveplane[MI_SCREEN(mi)];
1776
1777   MI_IS_DRAWN(mi) = True;
1778   if (!pp->glx_context)
1779     return;
1780
1781   glXMakeCurrent(display,window,*(pp->glx_context));
1782
1783   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
1784   glLoadIdentity();
1785
1786   display_projectiveplane(mi);
1787
1788   if (MI_IS_FPS(mi))
1789     do_fps (mi);
1790
1791   glFlush();
1792
1793   glXSwapBuffers(display,window);
1794 }
1795
1796
1797 /*
1798  *-----------------------------------------------------------------------------
1799  *    The display is being taken away from us.  Free up malloc'ed 
1800  *      memory and X resources that we've alloc'ed.  Only called
1801  *      once, we must zap everything for every screen.
1802  *-----------------------------------------------------------------------------
1803  */
1804
1805 ENTRYPOINT void release_projectiveplane(ModeInfo *mi)
1806 {
1807   if (projectiveplane != NULL)
1808   {
1809     int screen;
1810
1811     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1812     {
1813       projectiveplanestruct *pp = &projectiveplane[screen];
1814
1815       if (pp->glx_context)
1816         pp->glx_context = (GLXContext *)NULL;
1817     }
1818     (void) free((void *)projectiveplane);
1819     projectiveplane = (projectiveplanestruct *)NULL;
1820   }
1821   FreeAllGL(mi);
1822 }
1823
1824 #ifndef STANDALONE
1825 ENTRYPOINT void change_projectiveplane(ModeInfo *mi)
1826 {
1827   projectiveplanestruct *pp = &projectiveplane[MI_SCREEN(mi)];
1828
1829   if (!pp->glx_context)
1830     return;
1831
1832   glXMakeCurrent(MI_DISPLAY(mi),MI_WINDOW(mi),*(pp->glx_context));
1833   init(mi);
1834 }
1835 #endif /* !STANDALONE */
1836
1837 XSCREENSAVER_MODULE ("ProjectivePlane", projectiveplane)
1838
1839 #endif /* USE_GL */