summaryrefslogtreecommitdiffstats
path: root/hacks/glx
diff options
context:
space:
mode:
authorSimon Rettberg2020-12-02 08:23:52 +0100
committerSimon Rettberg2020-12-02 08:23:52 +0100
commit2954208d00d422b34fa3a69631b0a091f17a349d (patch)
treeb4322e320155656af133b8f47025d00b17a68f64 /hacks/glx
parentUpdate to 5.43 (diff)
downloadxscreensaver-2954208d00d422b34fa3a69631b0a091f17a349d.tar.gz
xscreensaver-2954208d00d422b34fa3a69631b0a091f17a349d.tar.xz
xscreensaver-2954208d00d422b34fa3a69631b0a091f17a349d.zip
5.44
Diffstat (limited to 'hacks/glx')
-rw-r--r--hacks/glx/Makefile.in50
-rw-r--r--hacks/glx/deepstars.c8
-rw-r--r--hacks/glx/etruscanvenus.c1825
-rw-r--r--hacks/glx/etruscanvenus.man372
-rw-r--r--hacks/glx/gibson.c1335
-rw-r--r--hacks/glx/gibson.man88
-rw-r--r--hacks/glx/glhanoi.c9
-rw-r--r--hacks/glx/glmatrix.c2
-rw-r--r--hacks/glx/gravitywell.c6
-rw-r--r--hacks/glx/hypertorus.c441
-rw-r--r--hacks/glx/hypertorus.man76
-rw-r--r--hacks/glx/klein.c815
-rw-r--r--hacks/glx/klein.man138
-rw-r--r--hacks/glx/polytopes.c10
-rw-r--r--hacks/glx/projectiveplane.c460
-rw-r--r--hacks/glx/projectiveplane.man132
-rw-r--r--hacks/glx/romanboy.c358
-rw-r--r--hacks/glx/romanboy.man110
-rw-r--r--hacks/glx/sonar-icmp.c22
-rw-r--r--hacks/glx/sonar.c8
-rw-r--r--hacks/glx/starwars.c12
21 files changed, 5445 insertions, 832 deletions
diff --git a/hacks/glx/Makefile.in b/hacks/glx/Makefile.in
index 482bed3..7ddb223 100644
--- a/hacks/glx/Makefile.in
+++ b/hacks/glx/Makefile.in
@@ -137,7 +137,8 @@ SRCS = xscreensaver-gl-helper.c normals.c erase-gl.c fps-gl.c \
raverhoop.c hydrostat.c discoball.c cubetwist.c cubestack.c \
splodesic.c hexstrut.c vigilance.c seccam.c esper.c \
razzledazzle.c ships.c peepers.c crumbler.c quickhull.c \
- maze3d.c handsy.c handsy_model.c gravitywell.c deepstars.c
+ maze3d.c handsy.c handsy_model.c gravitywell.c deepstars.c \
+ gibson.c etruscanvenus.c
OBJS = xscreensaver-gl-helper.o normals.o erase-gl.o fps-gl.o \
atlantis.o b_draw.o b_lockglue.o b_sphere.o bubble3d.o \
@@ -184,7 +185,8 @@ OBJS = xscreensaver-gl-helper.o normals.o erase-gl.o fps-gl.o \
raverhoop.o hydrostat.o discoball.o cubetwist.o cubestack.o \
splodesic.o hexstrut.o vigilance.o seccam.o esper.o \
razzledazzle.o ships.o peepers.o crumbler.o quickhull.o \
- maze3d.o handsy.o handsy_model.o gravitywell.o deepstars.o
+ maze3d.o handsy.o handsy_model.o gravitywell.o deepstars.o \
+ gibson.o etruscanvenus.o
GL_EXES = cage gears moebius pipes sproingies stairs superquadrics \
morph3d rubik atlantis lament bubble3d glplanet pulsar \
@@ -205,7 +207,7 @@ GL_EXES = cage gears moebius pipes sproingies stairs superquadrics \
splitflap dymaxionmap unicrud energystream raverhoop \
hydrostat discoball cubetwist cubestack splodesic \
hexstrut vigilance esper razzledazzle peepers crumbler \
- maze3d handsy gravitywell deepstars
+ maze3d handsy gravitywell deepstars gibson etruscanvenus
GLE_EXES = extrusion
SUID_EXES = sonar
SETCAP_EXES = sonar
@@ -273,7 +275,7 @@ GL_MEN = atlantis.man boxed.man bubble3d.man cage.man circuit.man \
discoball.man cubetwist.man cubestack.man splodesic.man \
hexstrut.man vigilance.man esper.man razzledazzle.man \
peepers.man crumbler.man maze3d.man handsy.man \
- gravitywell.man deepstars.man
+ gravitywell.man deepstars.man gibson.man etruscanvenus.man
MEN = @GL_MEN@
RETIRED_MEN = glforestfire.man
EXTRAS = README Makefile.in dxf2gl.pl vrml2gl.pl wfront2gl.pl \
@@ -791,6 +793,9 @@ projectiveplane: projectiveplane.o $(HACK_TRACK_OBJS)
romanboy: romanboy.o $(HACK_TRACK_OBJS)
$(CC_HACK) -o $@ $@.o $(HACK_TRACK_OBJS) $(HACK_LIBS)
+etruscanvenus: etruscanvenus.o $(HACK_TRACK_OBJS)
+ $(CC_HACK) -o $@ $@.o $(HACK_TRACK_OBJS) $(HACK_LIBS)
+
glmatrix: glmatrix.o $(PNG) $(HACK_OBJS)
$(CC_HACK) -o $@ $@.o $(PNG) $(HACK_OBJS) $(PNG_LIBS)
@@ -1033,6 +1038,9 @@ gravitywell: gravitywell.o $(HACK_TRACK_OBJS)
deepstars: deepstars.o $(HACK_TRACK_OBJS)
$(CC_HACK) -o $@ $@.o $(HACK_TRACK_OBJS) $(HACK_LIBS)
+gibson: gibson.o $(PNG) $(HACK_TRACK_OBJS)
+ $(CC_HACK) -o $@ $@.o $(PNG) $(HACK_TRACK_OBJS) $(PNG_LIBS)
+
##############################################################################
#
# DO NOT DELETE: updated by make distdepend
@@ -1731,6 +1739,23 @@ esper.o: $(UTILS_SRC)/xshm.h
esper.o: $(UTILS_SRC)/yarandom.h
esper.o: $(HACK_SRC)/xlockmoreI.h
esper.o: $(HACK_SRC)/xlockmore.h
+etruscanvenus.o: ../../config.h
+etruscanvenus.o: $(srcdir)/curlicue.h
+etruscanvenus.o: $(HACK_SRC)/fps.h
+etruscanvenus.o: $(srcdir)/gltrackball.h
+etruscanvenus.o: $(HACK_SRC)/recanim.h
+etruscanvenus.o: $(HACK_SRC)/screenhackI.h
+etruscanvenus.o: $(UTILS_SRC)/colors.h
+etruscanvenus.o: $(UTILS_SRC)/erase.h
+etruscanvenus.o: $(UTILS_SRC)/font-retry.h
+etruscanvenus.o: $(UTILS_SRC)/grabscreen.h
+etruscanvenus.o: $(UTILS_SRC)/hsv.h
+etruscanvenus.o: $(UTILS_SRC)/resources.h
+etruscanvenus.o: $(UTILS_SRC)/usleep.h
+etruscanvenus.o: $(UTILS_SRC)/visual.h
+etruscanvenus.o: $(UTILS_SRC)/yarandom.h
+etruscanvenus.o: $(HACK_SRC)/xlockmoreI.h
+etruscanvenus.o: $(HACK_SRC)/xlockmore.h
extrusion-helix2.o: ../../config.h
extrusion-helix2.o: $(srcdir)/extrusion.h
extrusion-helix3.o: ../../config.h
@@ -1966,6 +1991,23 @@ gflux.o: $(UTILS_SRC)/visual.h
gflux.o: $(UTILS_SRC)/yarandom.h
gflux.o: $(HACK_SRC)/xlockmoreI.h
gflux.o: $(HACK_SRC)/xlockmore.h
+gibson.o: ../../config.h
+gibson.o: $(HACK_SRC)/fps.h
+gibson.o: $(HACK_SRC)/recanim.h
+gibson.o: $(srcdir)/rotator.h
+gibson.o: $(HACK_SRC)/screenhackI.h
+gibson.o: $(srcdir)/texfont.h
+gibson.o: $(UTILS_SRC)/colors.h
+gibson.o: $(UTILS_SRC)/erase.h
+gibson.o: $(UTILS_SRC)/font-retry.h
+gibson.o: $(UTILS_SRC)/grabscreen.h
+gibson.o: $(UTILS_SRC)/hsv.h
+gibson.o: $(UTILS_SRC)/resources.h
+gibson.o: $(UTILS_SRC)/usleep.h
+gibson.o: $(UTILS_SRC)/visual.h
+gibson.o: $(UTILS_SRC)/yarandom.h
+gibson.o: $(HACK_SRC)/xlockmoreI.h
+gibson.o: $(HACK_SRC)/xlockmore.h
glblur.o: ../../config.h
glblur.o: $(HACK_SRC)/fps.h
glblur.o: $(srcdir)/gltrackball.h
diff --git a/hacks/glx/deepstars.c b/hacks/glx/deepstars.c
index b477e00..f415f18 100644
--- a/hacks/glx/deepstars.c
+++ b/hacks/glx/deepstars.c
@@ -11,13 +11,13 @@
#ifdef STANDALONE
#define DEFAULTS "*delay: 30000 \n" \
- "*showFPS: False \n" \
- "*suppressRotationAnimation: True\n" \
+ "*showFPS: False \n" \
+ "*suppressRotationAnimation: True\n" \
# define release_deepstars 0
-# include "xlockmore.h" /* from the xscreensaver distribution */
+# include "xlockmore.h" /* from the xscreensaver distribution */
#else /* !STANDALONE */
-# include "xlock.h" /* from the xlockmore distribution */
+# include "xlock.h" /* from the xlockmore distribution */
#endif /* !STANDALONE */
#ifdef USE_GL /* whole file */
diff --git a/hacks/glx/etruscanvenus.c b/hacks/glx/etruscanvenus.c
new file mode 100644
index 0000000..71af80a
--- /dev/null
+++ b/hacks/glx/etruscanvenus.c
@@ -0,0 +1,1825 @@
+/* etruscanvenus --- Shows a 3d immersion of a Klein bottle that
+ rotates in 3d or on which you can walk and that can deform smoothly
+ between the Etruscan Venus surface, the Roman surface, the Boy
+ surface surface, and the Ida surface. */
+
+#if 0
+static const char sccsid[] = "@(#)etruscanvenus.c 1.1 05/01/20 xlockmore";
+#endif
+
+/* Copyright (c) 2019-2020 Carsten Steger <carsten@mirsanmir.org>. */
+
+/*
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ *
+ * REVISION HISTORY:
+ * C. Steger - 05/01/20: Initial version
+ */
+
+/*
+ * This program shows a 3d immersion of a Klein bottle that smoothly
+ * deforms between the Etruscan Venus surface, the Roman surface, the
+ * Boy surface, and the Ida surface. You can walk on the Klein bottle
+ * or turn it in 3d. Topologically, all surfaces are Klein bottles,
+ * even the Roman and Boy surfaces, which are doubly covered and
+ * therefore appear to be an immersed real projective plane. The
+ * smooth deformation between these surfaces was constructed by George
+ * K. Francis.
+ *
+ * The Klein bottle is a non-orientable surface. To make this
+ * apparent, the two-sided color mode can be used. Alternatively,
+ * orientation markers (curling arrows) can be drawn as a texture map
+ * on the surface of the Klein bottle. While walking on the Klein
+ * bottle, you will notice that the orientation of the curling arrows
+ * changes (which it must because the Klein bottle is non-orientable).
+ * Since all the surfaces except the Ida surface have points where the
+ * surface normal is not well defined for some points, walking is only
+ * performed on the Ida surface.
+ *
+ * As mentioned above, the Roman and Boy surfaces are doubly covered
+ * and therefore appear to be an immersed real projective plane.
+ * Since some of the parameter names are based on this interpretation
+ * of the surface, the geometry of the real projective plane will be
+ * briefly disussed. The real projective plane is a model for the
+ * projective geometry in 2d space. One point can be singled out as
+ * the origin. A line can be singled out as the line at infinity,
+ * i.e., a line that lies at an infinite distance to the origin. The
+ * line at infinity is topologically a circle. Points on the line at
+ * infinity are also used to model directions in projective geometry.
+ * Direction and distance bands refer to this interpretation of the
+ * surface. If direction bands are used, the bands extend from the
+ * origin of the projective plane in different directions to the line
+ * at infinity and back to the origin. If distance bands are used,
+ * the bands lie at constant distances to the origin. The same
+ * interpretation is used for distance and direction colors. Although
+ * there is no conceptually equivalent geometric interpretation for
+ * the two Klein bottle surfaces (the Etruscan Venus and Ida
+ * surfaces), the smooth deformation between the surfaces results in a
+ * natural extension of these concepts to the Klein bottle surfaces.
+ *
+ * The immersed surfaces can be projected to the screen either
+ * perspectively or orthographically. When using the walking mode,
+ * perspective projection to the screen will be used.
+ *
+ * There are three display modes for the Klein bottle: mesh
+ * (wireframe), solid, or transparent. Furthermore, the appearance of
+ * the surface can be as a solid object or as a set of see-through
+ * bands. The bands can be distance bands or direction bands, as
+ * explained above.
+ *
+ * The colors with with the Klein bottle is drawn can be set to
+ * one-sided, two-sided, distance, or direction. In one-sided mode,
+ * the surface is drawn with the same color on both sides of the
+ * underlying triangles. In two-sided mode, the surface is drawn with
+ * red on one side of the underlying triangles and green on the other
+ * side. Since the surface actually only has one side, the color
+ * jumps from red to green along a line on the surface. This mode
+ * enables you to see that the surface is non-orientable. In distance
+ * mode, the surface is displayed with fully saturated colors that
+ * depend on the distance of the points on the projective plane to the
+ * origin, as described above. If the surface is displayed as
+ * distance bands, each band will be displayed with a different color.
+ * In direction mode, the surface is displayed with fully saturated
+ * colors that depend on the angle of the points on the projective
+ * plane with respect to the origin (see above for an explanation).
+ * If the surface is displayed as direction bands, each band will be
+ * displayed with a different color. The colors used to color the
+ * surface can either be static or can be changed dynamically.
+ *
+ * The rotation speed for each of the three coordinate axes around
+ * which the Klein bottle rotates can be chosen.
+ *
+ * Furthermore, in the walking mode the walking direction in the 2d
+ * base square of the surface and the walking speed can be chosen.
+ * The walking direction is measured as an angle in degrees in the 2d
+ * square that forms the coordinate system of the surface. A value of
+ * 0 or 180 means that the walk is along a circle at a randomly chosen
+ * distance from the origin (parallel to a distance band). A value of
+ * 90 or 270 means that the walk is directly along a direction band.
+ * Any other value results in a curved path along the surface. As
+ * noted above, walking is performed only on the Ida surface.
+ *
+ * By default, the immersion of the Klein bottle smoothly deforms
+ * between the Etruscan Venus surface, the Roman surface, the Boy
+ * surface, and the Ida surface. It is possible to choose the speed
+ * of the deformation. Furthermore, it is possible to switch the
+ * deformation off. It is also possible to determine the initial
+ * deformation of the immersion. This is mostly useful if the
+ * deformation is switched off, in which case it will determine the
+ * appearance of the surface. A value of 0 corresponds to the
+ * Etruscan Venus surface, a value of 1000 to the Roman surface, a
+ * value of 2000 to the Boy surface, and a value of 3000 to the Ida
+ * surface.
+ *
+ * This program is inspired by George K. Francis's book "A Topological
+ * Picturebook", Springer, 1987, by George K. Francis's paper "The
+ * Etruscan Venus" in P. Concus, R. Finn, and D. A. Hoffman:
+ * "Geometric Analysis and Computer Graphics", Springer, 1991, and by
+ * a video entitled "The Etruscan Venus" by Donna J. Cox, George
+ * K. Francis, and Raymond L. Idaszak, presented at SIGGRAPH 1989.
+ */
+
+#include "curlicue.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#define DISP_WIREFRAME 0
+#define DISP_SURFACE 1
+#define DISP_TRANSPARENT 2
+#define NUM_DISPLAY_MODES 3
+
+#define APPEARANCE_SOLID 0
+#define APPEARANCE_DISTANCE_BANDS 1
+#define APPEARANCE_DIRECTION_BANDS 2
+#define NUM_APPEARANCES 3
+
+#define COLORS_ONESIDED 0
+#define COLORS_TWOSIDED 1
+#define COLORS_DISTANCE 2
+#define COLORS_DIRECTION 3
+#define NUM_COLORS 4
+
+#define VIEW_WALK 0
+#define VIEW_TURN 1
+#define NUM_VIEW_MODES 2
+
+#define DISP_PERSPECTIVE 0
+#define DISP_ORTHOGRAPHIC 1
+#define NUM_DISP_MODES 2
+
+#define DEF_DISPLAY_MODE "random"
+#define DEF_APPEARANCE "random"
+#define DEF_COLORS "random"
+#define DEF_VIEW_MODE "random"
+#define DEF_MARKS "False"
+#define DEF_CHANGE_COLORS "True"
+#define DEF_DEFORM "True"
+#define DEF_PROJECTION "random"
+#define DEF_SPEEDX "1.1"
+#define DEF_SPEEDY "1.3"
+#define DEF_SPEEDZ "1.5"
+#define DEF_WALK_DIRECTION "83.0"
+#define DEF_WALK_SPEED "20.0"
+#define DEF_DEFORM_SPEED "10.0"
+#define DEF_INIT_DEFORM "0.0"
+
+#ifdef STANDALONE
+# define DEFAULTS "*delay: 10000 \n" \
+ "*showFPS: False \n" \
+
+# define release_etruscanvenus 0
+# include "xlockmore.h" /* from the xscreensaver distribution */
+#else /* !STANDALONE */
+# include "xlock.h" /* from the xlockmore distribution */
+#endif /* !STANDALONE */
+
+#ifdef USE_GL
+
+#ifndef HAVE_JWXYZ
+# include <X11/keysym.h>
+#endif
+
+#include "gltrackball.h"
+
+#include <float.h>
+
+
+#ifdef USE_MODULES
+ModStruct etruscanvenus_description =
+{"etruscanvenus", "init_etruscanvenus", "draw_etruscanvenus",
+ NULL, "draw_etruscanvenus", "change_etruscanvenus",
+ "free_etruscanvenus", &etruscanvenus_opts, 25000, 1, 1, 1, 1.0, 4, "",
+ "Rotate a 3d immersion of a Klein bottle in 3d or walk on it",
+ 0, NULL};
+
+#endif
+
+
+static char *mode;
+static char *appear;
+static char *color_mode;
+static char *view_mode;
+static Bool marks;
+static Bool deform;
+static Bool change_colors;
+static char *proj;
+static float speed_x;
+static float speed_y;
+static float speed_z;
+static float walk_direction;
+static float walk_speed;
+static float deform_speed;
+static float init_deform;
+
+
+static XrmOptionDescRec opts[] =
+{
+ {"-mode", ".displayMode", XrmoptionSepArg, 0 },
+ {"-wireframe", ".displayMode", XrmoptionNoArg, "wireframe" },
+ {"-surface", ".displayMode", XrmoptionNoArg, "surface" },
+ {"-transparent", ".displayMode", XrmoptionNoArg, "transparent" },
+ {"-appearance", ".appearance", XrmoptionSepArg, 0 },
+ {"-solid", ".appearance", XrmoptionNoArg, "solid" },
+ {"-distance-bands", ".appearance", XrmoptionNoArg, "distance-bands" },
+ {"-direction-bands", ".appearance", XrmoptionNoArg, "direction-bands" },
+ {"-colors", ".colors", XrmoptionSepArg, 0 },
+ {"-onesided-colors", ".colors", XrmoptionNoArg, "one-sided" },
+ {"-twosided-colors", ".colors", XrmoptionNoArg, "two-sided" },
+ {"-distance-colors", ".colors", XrmoptionNoArg, "distance" },
+ {"-direction-colors", ".colors", XrmoptionNoArg, "direction" },
+ {"-change-colors", ".changeColors", XrmoptionNoArg, "on"},
+ {"+change-colors", ".changeColors", XrmoptionNoArg, "off"},
+ {"-view-mode", ".viewMode", XrmoptionSepArg, 0 },
+ {"-walk", ".viewMode", XrmoptionNoArg, "walk" },
+ {"-turn", ".viewMode", XrmoptionNoArg, "turn" },
+ {"-deform", ".deform", XrmoptionNoArg, "on"},
+ {"+deform", ".deform", XrmoptionNoArg, "off"},
+ {"-orientation-marks", ".marks", XrmoptionNoArg, "on"},
+ {"+orientation-marks", ".marks", XrmoptionNoArg, "off"},
+ {"-projection", ".projection", XrmoptionSepArg, 0 },
+ {"-perspective", ".projection", XrmoptionNoArg, "perspective" },
+ {"-orthographic", ".projection", XrmoptionNoArg, "orthographic" },
+ {"-speed-x", ".speedx", XrmoptionSepArg, 0 },
+ {"-speed-y", ".speedy", XrmoptionSepArg, 0 },
+ {"-speed-z", ".speedz", XrmoptionSepArg, 0 },
+ {"-walk-direction", ".walkDirection", XrmoptionSepArg, 0 },
+ {"-walk-speed", ".walkSpeed", XrmoptionSepArg, 0 },
+ {"-deformation-speed", ".deformSpeed", XrmoptionSepArg, 0 },
+ {"-initial-deformation", ".initDeform", XrmoptionSepArg, 0 },
+ {"-etruscan-venus", ".initDeform", XrmoptionNoArg, "0.0" },
+ {"-roman", ".initDeform", XrmoptionNoArg, "1000.0" },
+ {"-boy", ".initDeform", XrmoptionNoArg, "2000.0" },
+ {"-ida", ".initDeform", XrmoptionNoArg, "3000.0" },
+};
+
+static argtype vars[] =
+{
+ { &mode, "displayMode", "DisplayMode", DEF_DISPLAY_MODE, t_String },
+ { &appear, "appearance", "Appearance", DEF_APPEARANCE, t_String },
+ { &color_mode, "colors", "Colors", DEF_COLORS, t_String },
+ { &change_colors, "changeColors", "ChangeColors", DEF_CHANGE_COLORS, t_Bool },
+ { &view_mode, "viewMode", "ViewMode", DEF_VIEW_MODE, t_String },
+ { &deform, "deform", "Deform", DEF_DEFORM, t_Bool },
+ { &marks, "marks", "Marks", DEF_MARKS, t_Bool },
+ { &proj, "projection", "Projection", DEF_PROJECTION, t_String },
+ { &speed_x, "speedx", "Speedx", DEF_SPEEDX, t_Float},
+ { &speed_y, "speedy", "Speedy", DEF_SPEEDY, t_Float},
+ { &speed_z, "speedz", "Speedz", DEF_SPEEDZ, t_Float},
+ { &walk_direction, "walkDirection", "WalkDirection", DEF_WALK_DIRECTION, t_Float},
+ { &walk_speed, "walkSpeed", "WalkSpeed", DEF_WALK_SPEED, t_Float},
+ { &deform_speed, "deformSpeed", "DeformSpeed", DEF_DEFORM_SPEED, t_Float},
+ { &init_deform, "initDeform", "InitDeform", DEF_INIT_DEFORM, t_Float },
+};
+
+ENTRYPOINT ModeSpecOpt etruscanvenus_opts =
+{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, NULL};
+
+
+/* Offset by which we walk above the surface */
+#define DELTAY 0.01
+
+/* Color change speeds */
+#define DRHO 0.7
+#define DSIGMA 1.1
+#define DTAU 1.7
+
+/* Number of subdivisions of the surface */
+#define NUMU 192
+#define NUMV 128
+
+/* Number of subdivisions per band */
+#define NUMBDIR 8
+#define NUMBDIST 4
+
+/* Constants that are used to calculate the approximate center of the
+ surface in the z direction. */
+#define Z1 (0.8141179221194051)
+#define Z2 (0.1359276851926206)
+#define Z3 (1.1581097545867050)
+#define Z4 (0.7186549129158579)
+#define Z5 (2.5393401559381240)
+
+/* Constants that are used to calculate the approximate radius of the
+ surface. */
+#define R1 (1.308007044714129)
+#define R2 (4.005205981405042)
+#define R3 (-2.893994600199527)
+#define R4 (-1.266709537162707)
+
+
+typedef struct {
+ GLint WindH, WindW;
+ GLXContext *glx_context;
+ /* Options */
+ int display_mode;
+ int appearance;
+ int colors;
+ Bool change_colors;
+ int view;
+ int projection;
+ Bool marks;
+ /* 3D rotation angles */
+ float alpha, beta, delta;
+ /* Color rotation angles */
+ float rho, sigma, tau;
+ /* Movement parameters */
+ float umove, vmove, dumove, dvmove;
+ int side;
+ /* Deformation parameters */
+ float dd;
+ int defdir;
+ /* The viewing offset in 3d */
+ float offset3d[3];
+ /* The 3d coordinates of the surface and their derivatives */
+ float *ev;
+ float *evn;
+ /* The precomputed colors of the surface */
+ float *col;
+ /* The precomputed texture coordinates of the surface */
+ float *tex;
+ /* The "curlicue" texture */
+ GLuint tex_name;
+ /* Aspect ratio of the current window */
+ float aspect;
+ /* Trackball states */
+ trackball_state *trackball;
+ Bool button_pressed;
+ /* A random factor to modify the rotation speeds */
+ float speed_scale;
+} etruscanvenusstruct;
+
+static etruscanvenusstruct *etruscanvenus = (etruscanvenusstruct *) NULL;
+
+
+/* Add a rotation around the x-axis to the matrix m. */
+static void rotatex(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI/180.0;
+ c = cos(phi);
+ s = sin(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][1];
+ v = m[i][2];
+ m[i][1] = c*u+s*v;
+ m[i][2] = -s*u+c*v;
+ }
+}
+
+
+/* Add a rotation around the y-axis to the matrix m. */
+static void rotatey(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI/180.0;
+ c = cos(phi);
+ s = sin(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][0];
+ v = m[i][2];
+ m[i][0] = c*u-s*v;
+ m[i][2] = s*u+c*v;
+ }
+}
+
+
+/* Add a rotation around the z-axis to the matrix m. */
+static void rotatez(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI/180.0;
+ c = cos(phi);
+ s = sin(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][0];
+ v = m[i][1];
+ m[i][0] = c*u+s*v;
+ m[i][1] = -s*u+c*v;
+ }
+}
+
+
+/* Compute the rotation matrix m from the rotation angles. */
+static void rotateall(float al, float be, float de, float m[3][3])
+{
+ int i, j;
+
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ m[i][j] = (i==j);
+ rotatex(m,al);
+ rotatey(m,be);
+ rotatez(m,de);
+}
+
+
+/* Multiply two rotation matrices: o=m*n. */
+static void mult_rotmat(float m[3][3], float n[3][3], float o[3][3])
+{
+ int i, j, k;
+
+ for (i=0; i<3; i++)
+ {
+ for (j=0; j<3; j++)
+ {
+ o[i][j] = 0.0;
+ for (k=0; k<3; k++)
+ o[i][j] += m[i][k]*n[k][j];
+ }
+ }
+}
+
+
+/* Compute a 3D rotation matrix from a unit quaternion. */
+static void quat_to_rotmat(float p[4], float m[3][3])
+{
+ double al, be, de;
+ double r00, r01, r02, r12, r22;
+
+ r00 = 1.0-2.0*(p[1]*p[1]+p[2]*p[2]);
+ r01 = 2.0*(p[0]*p[1]+p[2]*p[3]);
+ r02 = 2.0*(p[2]*p[0]-p[1]*p[3]);
+ r12 = 2.0*(p[1]*p[2]+p[0]*p[3]);
+ r22 = 1.0-2.0*(p[1]*p[1]+p[0]*p[0]);
+
+ al = atan2(-r12,r22)*180.0/M_PI;
+ be = atan2(r02,sqrt(r00*r00+r01*r01))*180.0/M_PI;
+ de = atan2(-r01,r00)*180.0/M_PI;
+
+ rotateall(al,be,de,m);
+}
+
+
+/* Compute a fully saturated and bright color based on an angle and,
+ optionally, a color rotation matrix. */
+static void color(etruscanvenusstruct *ev, double angle, float mat[3][3],
+ float col[4])
+{
+ int s;
+ double t, ca, sa;
+ float m;
+
+ if (!ev->change_colors)
+ {
+ if (ev->colors == COLORS_ONESIDED || ev->colors == COLORS_TWOSIDED)
+ return;
+
+ if (angle >= 0.0)
+ angle = fmod(angle,2.0*M_PI);
+ else
+ angle = fmod(angle,-2.0*M_PI);
+ s = floor(angle/(M_PI/3));
+ t = angle/(M_PI/3)-s;
+ if (s >= 6)
+ s = 0;
+ switch (s)
+ {
+ case 0:
+ col[0] = 1.0;
+ col[1] = t;
+ col[2] = 0.0;
+ break;
+ case 1:
+ col[0] = 1.0-t;
+ col[1] = 1.0;
+ col[2] = 0.0;
+ break;
+ case 2:
+ col[0] = 0.0;
+ col[1] = 1.0;
+ col[2] = t;
+ break;
+ case 3:
+ col[0] = 0.0;
+ col[1] = 1.0-t;
+ col[2] = 1.0;
+ break;
+ case 4:
+ col[0] = t;
+ col[1] = 0.0;
+ col[2] = 1.0;
+ break;
+ case 5:
+ col[0] = 1.0;
+ col[1] = 0.0;
+ col[2] = 1.0-t;
+ break;
+ }
+ }
+ else /* ev->change_colors */
+ {
+ if (ev->colors == COLORS_ONESIDED || ev->colors == COLORS_TWOSIDED)
+ {
+ col[0] = mat[0][2];
+ col[1] = mat[1][2];
+ col[2] = mat[2][2];
+ }
+ else
+ {
+ ca = cos(angle);
+ sa = sin(angle);
+ col[0] = ca*mat[0][0]+sa*mat[0][1];
+ col[1] = ca*mat[1][0]+sa*mat[1][1];
+ col[2] = ca*mat[2][0]+sa*mat[2][1];
+ }
+ m = 0.5f/fmaxf(fmaxf(fabsf(col[0]),fabsf(col[1])),fabsf(col[2]));
+ col[0] = m*col[0]+0.5f;
+ col[1] = m*col[1]+0.5f;
+ col[2] = m*col[2]+0.5f;
+ }
+ if (ev->display_mode == DISP_TRANSPARENT)
+ col[3] = 0.7;
+ else
+ col[3] = 1.0;
+}
+
+
+/* Set up the surface colors and texture. */
+static void setup_etruscan_venus_color_texture(ModeInfo *mi, double umin,
+ double umax, double vmin,
+ double vmax, int numu, int numv)
+{
+ int i, j, k;
+ double u, v, ur, vr, vc;
+ etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)];
+
+ ur = umax-umin;
+ vr = vmax-vmin;
+ for (i=0; i<=numv; i++)
+ {
+ for (j=0; j<=numu; j++)
+ {
+ k = i*(numu+1)+j;
+ u = ur*j/numu+umin;
+ if (ev->appearance == APPEARANCE_DISTANCE_BANDS)
+ v = -vr*i/numv+vmin;
+ else
+ v = vr*i/numv+vmin;
+ if (!ev->change_colors)
+ {
+ if (ev->colors == COLORS_DISTANCE)
+ {
+ if (ev->appearance == APPEARANCE_DISTANCE_BANDS)
+ vc = -4.0*v;
+ else
+ vc = 4.0*v;
+ if (vc >= 4.0*M_PI)
+ vc -= 4.0*M_PI;
+ if (vc >= 2.0*M_PI)
+ vc = 4.0*M_PI-vc;
+ color(ev,vc,NULL,&ev->col[4*k]);
+ }
+ else /* ev->colors == COLORS_DIRECTION */
+ {
+ color(ev,u,NULL,&ev->col[4*k]);
+ }
+ }
+ ev->tex[2*k+0] = 48*u/(2.0*M_PI);
+ if (ev->appearance == APPEARANCE_DISTANCE_BANDS)
+ ev->tex[2*k+1] = 64*v/(2.0*M_PI)-0.5;
+ else
+ ev->tex[2*k+1] = 64*v/(2.0*M_PI);
+ }
+ }
+}
+
+
+/* Draw a 3d immersion of the surface. */
+static int etruscan_venus(ModeInfo *mi, double umin, double umax,
+ double vmin, double vmax, int numu, int numv)
+{
+ int polys = 0;
+ static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_oneside[] = { 0.9, 0.4, 0.3, 1.0 };
+ static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_oneside[] = { 0.9, 0.4, 0.3, 0.7 };
+ float mat_diff_dyn[4], mat_diff_dyn_compl[4];
+ float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[3][3], matc[3][3];
+ int i, j, k, l, m, o;
+ double u, v, ur, vr, oz, vc;
+ double xx[3], xxu[3], xxv[3];
+ double r, s, t;
+ double dd, bb, ll, db, dl, radius;
+ double cv, sv, c2v, s2v, cu, su, c2u, s2u, c3u, s3u;
+ double bosqrt2, b2osqrt2, b3osqrt2, nom, den, nomv, denu, denv, den2;
+ double f, fx, fy, fz, x, y, z;
+ double fu, fv, fxu, fxv, fyu, fyv, fzv, xu, xv, yu, yv, zu, zv;
+ float qu[4], r1[3][3], r2[3][3];
+ etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)];
+
+ dd = ev->dd;
+ if (dd < 1.0)
+ {
+ bb = 0.0;
+ ll = dd;
+ }
+ else if (dd < 2.0)
+ {
+ bb = dd-1.0;
+ ll = 1.0;
+ }
+ else if (dd < 3.0)
+ {
+ bb = 1.0;
+ ll = 3.0-dd;
+ }
+ else /* dd < 4.0 */
+ {
+ bb = 4.0-dd;
+ ll = 0.0;
+ }
+ db = ((6.0*bb-15.0)*bb+10.0)*bb*bb*bb;
+ dl = ((6.0*ll-15.0)*ll+10.0)*ll*ll*ll;
+ /* Calculate the approximate center of the surface in the z direction. */
+ oz = (Z1*(sin(0.5*M_PI*pow(dl,Z3))+Z2*sin(1.5*M_PI*pow(dl,Z3)))*
+ exp(Z4*pow(db,Z5)));
+ /* Calculate the approximate radius of the surface. */
+ r = R1+(db-0.5)*(dl-0.5)+R2*exp(R3*(1.0-db))*exp(R4*dl);
+ radius = 0.8/r;
+
+ if (ev->change_colors)
+ rotateall(ev->rho,ev->sigma,ev->tau,matc);
+
+ if (ev->view == VIEW_WALK)
+ {
+ u = ev->umove;
+ v = ev->vmove;
+ u = 0.5*u;
+ bosqrt2 = db/M_SQRT2;
+ b2osqrt2 = 2.0*bosqrt2;
+ b3osqrt2 = 3.0*bosqrt2;
+ cu = cos(u);
+ su = sin(u);
+ c2u = cos(2.0*u);
+ s2u = sin(2.0*u);
+ c3u = cos(3.0*u);
+ s3u = sin(3.0*u);
+ cv = cos(v);
+ sv = sin(v);
+ c2v = cos(2.0*v);
+ s2v = sin(2.0*v);
+ nom = (1.0-dl+dl*cv);
+ den = (1.0-bosqrt2*s3u*s2v);
+ f = nom/den;
+ fx = c2u*cv+cu*sv;
+ fy = s2u*cv-su*sv;
+ fz = M_SQRT2*cv;
+ x = f*fx;
+ y = f*fy;
+ z = f*fz;
+ nomv = -dl*sv;
+ denu = -b3osqrt2*c3u*s2v;
+ denv = -b2osqrt2*s3u*c2v;
+ den2 = 1.0/(den*den);
+ fu = -nom*denu*den2;
+ fv = (den*nomv-nom*denv)*den2;
+ fxu = -su*sv-2.0*s2u*cv;
+ fxv = cu*cv-c2u*sv;
+ fyu = 2.0*c2u*cv-cu*sv;
+ fyv = -s2u*sv-su*cv;
+ fzv = -M_SQRT2*sv;
+ xu = fu*fx+f*fxu;
+ xv = fv*fx+f*fxv;
+ yu = fu*fy+f*fyu;
+ yv = fv*fy+f*fyv;
+ zu = fu*fz;
+ zv = fv*fz+f*fzv;
+ xx[0] = x;
+ xx[1] = y;
+ xx[2] = z-oz;
+ n[0] = yu*zv-zu*yv;
+ n[1] = zu*xv-xu*zv;
+ n[2] = xu*yv-yu*xv;
+ t = n[0]*n[0]+n[1]*n[1]+n[2]*n[2];
+ /* Avoid degenerate tangential plane basis vectors as much as
+ possible. */
+ if (t < FLT_EPSILON)
+ {
+ u += 0.01;
+ v += 0.01;
+ cu = cos(u);
+ su = sin(u);
+ c2u = cos(2.0*u);
+ s2u = sin(2.0*u);
+ c3u = cos(3.0*u);
+ s3u = sin(3.0*u);
+ cv = cos(v);
+ sv = sin(v);
+ c2v = cos(2.0*v);
+ s2v = sin(2.0*v);
+ nom = (1.0-dl+dl*cv);
+ den = (1.0-bosqrt2*s3u*s2v);
+ f = nom/den;
+ fx = c2u*cv+cu*sv;
+ fy = s2u*cv-su*sv;
+ fz = M_SQRT2*cv;
+ nomv = -dl*sv;
+ denu = -b3osqrt2*c3u*s2v;
+ denv = -b2osqrt2*s3u*c2v;
+ den2 = 1.0/(den*den);
+ fu = -nom*denu*den2;
+ fv = (den*nomv-nom*denv)*den2;
+ fxu = -su*sv-2.0*s2u*cv;
+ fxv = cu*cv-c2u*sv;
+ fyu = 2.0*c2u*cv-cu*sv;
+ fyv = -s2u*sv-su*cv;
+ fzv = -M_SQRT2*sv;
+ xu = fu*fx+f*fxu;
+ xv = fv*fx+f*fxv;
+ yu = fu*fy+f*fyu;
+ yv = fv*fy+f*fyv;
+ zu = fu*fz;
+ zv = fv*fz+f*fzv;
+ }
+ xxu[0] = xu;
+ xxu[1] = yu;
+ xxu[2] = zu;
+ xxv[0] = xv;
+ xxv[1] = yv;
+ xxv[2] = zv;
+ for (l=0; l<3; l++)
+ {
+ p[l] = xx[l]*radius;
+ pu[l] = xxu[l]*radius;
+ pv[l] = xxv[l]*radius;
+ }
+ n[0] = pu[1]*pv[2]-pu[2]*pv[1];
+ n[1] = pu[2]*pv[0]-pu[0]*pv[2];
+ n[2] = pu[0]*pv[1]-pu[1]*pv[0];
+ t = 1.0/(ev->side*4.0*sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]));
+ n[0] *= t;
+ n[1] *= t;
+ n[2] *= t;
+ pm[0] = 0.5*pu[0]*ev->dumove+pv[0]*ev->dvmove;
+ pm[1] = 0.5*pu[1]*ev->dumove+pv[1]*ev->dvmove;
+ pm[2] = 0.5*pu[2]*ev->dumove+pv[2]*ev->dvmove;
+ t = 1.0/(4.0*sqrt(pm[0]*pm[0]+pm[1]*pm[1]+pm[2]*pm[2]));
+ pm[0] *= t;
+ pm[1] *= t;
+ pm[2] *= t;
+ b[0] = n[1]*pm[2]-n[2]*pm[1];
+ b[1] = n[2]*pm[0]-n[0]*pm[2];
+ b[2] = n[0]*pm[1]-n[1]*pm[0];
+ t = 1.0/(4.0*sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]));
+ b[0] *= t;
+ b[1] *= t;
+ b[2] *= t;
+
+ /* Compute alpha, beta, gamma from the three basis vectors.
+ | -b[0] -b[1] -b[2] |
+ m = | n[0] n[1] n[2] |
+ | -pm[0] -pm[1] -pm[2] |
+ */
+ ev->alpha = atan2(-n[2],-pm[2])*180/M_PI;
+ ev->beta = atan2(-b[2],sqrt(b[0]*b[0]+b[1]*b[1]))*180/M_PI;
+ ev->delta = atan2(b[1],-b[0])*180/M_PI;
+
+ /* Compute the rotation that rotates the surface in 3D. */
+ rotateall(ev->alpha,ev->beta,ev->delta,mat);
+
+ u = ev->umove;
+ v = ev->vmove;
+ u = 0.5*u;
+ bosqrt2 = db/M_SQRT2;
+ b2osqrt2 = 2.0*bosqrt2;
+ b3osqrt2 = 3.0*bosqrt2;
+ cu = cos(u);
+ su = sin(u);
+ c2u = cos(2.0*u);
+ s2u = sin(2.0*u);
+ s3u = sin(3.0*u);
+ cv = cos(v);
+ sv = sin(v);
+ s2v = sin(2.0*v);
+ nom = (1.0-dl+dl*cv);
+ den = (1.0-bosqrt2*s3u*s2v);
+ f = nom/den;
+ fx = c2u*cv+cu*sv;
+ fy = s2u*cv-su*sv;
+ fz = M_SQRT2*cv;
+ x = f*fx;
+ y = f*fy;
+ z = f*fz;
+ xx[0] = x;
+ xx[1] = y;
+ xx[2] = z-oz;
+ for (l=0; l<3; l++)
+ {
+ r = 0.0;
+ for (m=0; m<3; m++)
+ r += mat[l][m]*xx[m];
+ p[l] = r*radius;
+ }
+
+ ev->offset3d[0] = -p[0];
+ ev->offset3d[1] = -p[1]-DELTAY;
+ ev->offset3d[2] = -p[2];
+ }
+ else
+ {
+ /* Compute the rotation that rotates the surface in 3D, including the
+ trackball rotations. */
+ rotateall(ev->alpha,ev->beta,ev->delta,r1);
+
+ gltrackball_get_quaternion(ev->trackball,qu);
+ quat_to_rotmat(qu,r2);
+
+ mult_rotmat(r2,r1,mat);
+ }
+
+ if (!ev->change_colors)
+ {
+ if (ev->colors == COLORS_ONESIDED)
+ {
+ glColor3fv(mat_diff_oneside);
+ if (ev->display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_trans_oneside);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_oneside);
+ }
+ }
+ else if (ev->colors == COLORS_TWOSIDED)
+ {
+ glColor3fv(mat_diff_red);
+ if (ev->display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ }
+ }
+ }
+ else /* ev->change_colors */
+ {
+ color(ev,0.0,matc,mat_diff_dyn);
+ if (ev->colors == COLORS_ONESIDED)
+ {
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ }
+ else if (ev->colors == COLORS_TWOSIDED)
+ {
+ mat_diff_dyn_compl[0] = 1.0f-mat_diff_dyn[0];
+ mat_diff_dyn_compl[1] = 1.0f-mat_diff_dyn[1];
+ mat_diff_dyn_compl[2] = 1.0f-mat_diff_dyn[2];
+ mat_diff_dyn_compl[3] = mat_diff_dyn[3];
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn_compl);
+ }
+ }
+ glBindTexture(GL_TEXTURE_2D,ev->tex_name);
+
+ ur = umax-umin;
+ vr = vmax-vmin;
+
+ /* Set up the surface coordinates and normals. */
+ if (ev->appearance == APPEARANCE_DISTANCE_BANDS)
+ {
+ for (i=0; i<=numv; i++)
+ {
+ if ((i & (NUMBDIST-1)) >= NUMBDIST/4+1 &&
+ (i & (NUMBDIST-1)) < 3*NUMBDIST/4)
+ continue;
+ for (j=0; j<=numu; j++)
+ {
+ o = i*(numu+1)+j;
+ u = ur*j/numu+umin;
+ v = -vr*i/numv+vmin;
+ if (ev->change_colors)
+ {
+ /* Compute the colors dynamically. */
+ if (ev->colors == COLORS_DISTANCE)
+ {
+ vc = -4.0*v;
+ if (vc >= 4.0*M_PI)
+ vc -= 4.0*M_PI;
+ if (vc >= 2.0*M_PI)
+ vc = 4.0*M_PI-vc;
+ color(ev,vc,matc,&ev->col[4*o]);
+ }
+ else if (ev->colors == COLORS_DIRECTION)
+ {
+ color(ev,u,matc,&ev->col[4*o]);
+ }
+ }
+ u = 0.5*u;
+ bosqrt2 = db/M_SQRT2;
+ b2osqrt2 = 2.0*bosqrt2;
+ b3osqrt2 = 3.0*bosqrt2;
+ cu = cos(u);
+ su = sin(u);
+ c2u = cos(2.0*u);
+ s2u = sin(2.0*u);
+ c3u = cos(3.0*u);
+ s3u = sin(3.0*u);
+ cv = cos(v);
+ sv = sin(v);
+ c2v = cos(2.0*v);
+ s2v = sin(2.0*v);
+ nom = (1.0-dl+dl*cv);
+ den = (1.0-bosqrt2*s3u*s2v);
+ f = nom/den;
+ fx = c2u*cv+cu*sv;
+ fy = s2u*cv-su*sv;
+ fz = M_SQRT2*cv;
+ x = f*fx;
+ y = f*fy;
+ z = f*fz;
+ nomv = -dl*sv;
+ denu = -b3osqrt2*c3u*s2v;
+ denv = -b2osqrt2*s3u*c2v;
+ den2 = 1.0/(den*den);
+ fu = -nom*denu*den2;
+ fv = (den*nomv-nom*denv)*den2;
+ fxu = -su*sv-2.0*s2u*cv;
+ fxv = cu*cv-c2u*sv;
+ fyu = 2.0*c2u*cv-cu*sv;
+ fyv = -s2u*sv-su*cv;
+ fzv = -M_SQRT2*sv;
+ xu = fu*fx+f*fxu;
+ xv = fv*fx+f*fxv;
+ yu = fu*fy+f*fyu;
+ yv = fv*fy+f*fyv;
+ zu = fu*fz;
+ zv = fv*fz+f*fzv;
+ xx[0] = x;
+ xx[1] = y;
+ xx[2] = z-oz;
+ n[0] = yu*zv-zu*yv;
+ n[1] = zu*xv-xu*zv;
+ n[2] = xu*yv-yu*xv;
+ t = n[0]*n[0]+n[1]*n[1]+n[2]*n[2];
+ /* Avoid degenerate tangential plane basis vectors as much as
+ possible. */
+ if (t < FLT_EPSILON)
+ {
+ u += 0.01;
+ v += 0.01;
+ cu = cos(u);
+ su = sin(u);
+ c2u = cos(2.0*u);
+ s2u = sin(2.0*u);
+ c3u = cos(3.0*u);
+ s3u = sin(3.0*u);
+ cv = cos(v);
+ sv = sin(v);
+ c2v = cos(2.0*v);
+ s2v = sin(2.0*v);
+ nom = (1.0-dl+dl*cv);
+ den = (1.0-bosqrt2*s3u*s2v);
+ f = nom/den;
+ fx = c2u*cv+cu*sv;
+ fy = s2u*cv-su*sv;
+ fz = M_SQRT2*cv;
+ nomv = -dl*sv;
+ denu = -b3osqrt2*c3u*s2v;
+ denv = -b2osqrt2*s3u*c2v;
+ den2 = 1.0/(den*den);
+ fu = -nom*denu*den2;
+ fv = (den*nomv-nom*denv)*den2;
+ fxu = -su*sv-2.0*s2u*cv;
+ fxv = cu*cv-c2u*sv;
+ fyu = 2.0*c2u*cv-cu*sv;
+ fyv = -s2u*sv-su*cv;
+ fzv = -M_SQRT2*sv;
+ xu = fu*fx+f*fxu;
+ xv = fv*fx+f*fxv;
+ yu = fu*fy+f*fyu;
+ yv = fv*fy+f*fyv;
+ zu = fu*fz;
+ zv = fv*fz+f*fzv;
+ }
+ xxu[0] = xu;
+ xxu[1] = yu;
+ xxu[2] = zu;
+ xxv[0] = xv;
+ xxv[1] = yv;
+ xxv[2] = zv;
+ for (l=0; l<3; l++)
+ {
+ r = 0.0;
+ s = 0.0;
+ t = 0.0;
+ for (m=0; m<3; m++)
+ {
+ r += mat[l][m]*xx[m];
+ s += mat[l][m]*xxu[m];
+ t += mat[l][m]*xxv[m];
+ }
+ p[l] = r*radius+ev->offset3d[l];
+ pu[l] = s*radius;
+ pv[l] = t*radius;
+ }
+ n[0] = pu[1]*pv[2]-pu[2]*pv[1];
+ n[1] = pu[2]*pv[0]-pu[0]*pv[2];
+ n[2] = pu[0]*pv[1]-pu[1]*pv[0];
+ t = 1.0/sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);
+ n[0] *= t;
+ n[1] *= t;
+ n[2] *= t;
+ ev->ev[3*o+0] = p[0];
+ ev->ev[3*o+1] = p[1];
+ ev->ev[3*o+2] = p[2];
+ ev->evn[3*o+0] = n[0];
+ ev->evn[3*o+1] = n[1];
+ ev->evn[3*o+2] = n[2];
+ }
+ }
+ }
+ else /* ev->appearance != APPEARANCE_DISTANCE_BANDS */
+ {
+ for (j=0; j<=numu; j++)
+ {
+ if (ev->appearance == APPEARANCE_DIRECTION_BANDS &&
+ ((j & (NUMBDIR-1)) >= NUMBDIR/2+1))
+ continue;
+ for (i=0; i<=numv; i++)
+ {
+ o = i*(numu+1)+j;
+ u = ur*j/numu+umin;
+ v = vr*i/numv+vmin;
+ if (ev->change_colors)
+ {
+ /* Compute the colors dynamically. */
+ if (ev->colors == COLORS_DISTANCE)
+ {
+ vc = 4.0*v;
+ if (vc >= 4.0*M_PI)
+ vc -= 4.0*M_PI;
+ if (vc >= 2.0*M_PI)
+ vc = 4.0*M_PI-vc;
+ color(ev,vc,matc,&ev->col[4*o]);
+ }
+ else if (ev->colors == COLORS_DIRECTION)
+ {
+ color(ev,u,matc,&ev->col[4*o]);
+ }
+ }
+ u = 0.5*u;
+ bosqrt2 = db/M_SQRT2;
+ b2osqrt2 = 2.0*bosqrt2;
+ b3osqrt2 = 3.0*bosqrt2;
+ cu = cos(u);
+ su = sin(u);
+ c2u = cos(2.0*u);
+ s2u = sin(2.0*u);
+ c3u = cos(3.0*u);
+ s3u = sin(3.0*u);
+ cv = cos(v);
+ sv = sin(v);
+ c2v = cos(2.0*v);
+ s2v = sin(2.0*v);
+ nom = (1.0-dl+dl*cv);
+ den = (1.0-bosqrt2*s3u*s2v);
+ f = nom/den;
+ fx = c2u*cv+cu*sv;
+ fy = s2u*cv-su*sv;
+ fz = M_SQRT2*cv;
+ x = f*fx;
+ y = f*fy;
+ z = f*fz;
+ nomv = -dl*sv;
+ denu = -b3osqrt2*c3u*s2v;
+ denv = -b2osqrt2*s3u*c2v;
+ den2 = 1.0/(den*den);
+ fu = -nom*denu*den2;
+ fv = (den*nomv-nom*denv)*den2;
+ fxu = -su*sv-2.0*s2u*cv;
+ fxv = cu*cv-c2u*sv;
+ fyu = 2.0*c2u*cv-cu*sv;
+ fyv = -s2u*sv-su*cv;
+ fzv = -M_SQRT2*sv;
+ xu = fu*fx+f*fxu;
+ xv = fv*fx+f*fxv;
+ yu = fu*fy+f*fyu;
+ yv = fv*fy+f*fyv;
+ zu = fu*fz;
+ zv = fv*fz+f*fzv;
+ xx[0] = x;
+ xx[1] = y;
+ xx[2] = z-oz;
+ n[0] = yu*zv-zu*yv;
+ n[1] = zu*xv-xu*zv;
+ n[2] = xu*yv-yu*xv;
+ t = n[0]*n[0]+n[1]*n[1]+n[2]*n[2];
+ /* Avoid degenerate tangential plane basis vectors as much as
+ possible. */
+ if (t < FLT_EPSILON)
+ {
+ u += 0.01;
+ v += 0.01;
+ cu = cos(u);
+ su = sin(u);
+ c2u = cos(2.0*u);
+ s2u = sin(2.0*u);
+ c3u = cos(3.0*u);
+ s3u = sin(3.0*u);
+ cv = cos(v);
+ sv = sin(v);
+ c2v = cos(2.0*v);
+ s2v = sin(2.0*v);
+ nom = (1.0-dl+dl*cv);
+ den = (1.0-bosqrt2*s3u*s2v);
+ f = nom/den;
+ fx = c2u*cv+cu*sv;
+ fy = s2u*cv-su*sv;
+ fz = M_SQRT2*cv;
+ nomv = -dl*sv;
+ denu = -b3osqrt2*c3u*s2v;
+ denv = -b2osqrt2*s3u*c2v;
+ den2 = 1.0/(den*den);
+ fu = -nom*denu*den2;
+ fv = (den*nomv-nom*denv)*den2;
+ fxu = -su*sv-2.0*s2u*cv;
+ fxv = cu*cv-c2u*sv;
+ fyu = 2.0*c2u*cv-cu*sv;
+ fyv = -s2u*sv-su*cv;
+ fzv = -M_SQRT2*sv;
+ xu = fu*fx+f*fxu;
+ xv = fv*fx+f*fxv;
+ yu = fu*fy+f*fyu;
+ yv = fv*fy+f*fyv;
+ zu = fu*fz;
+ zv = fv*fz+f*fzv;
+ }
+ xxu[0] = xu;
+ xxu[1] = yu;
+ xxu[2] = zu;
+ xxv[0] = xv;
+ xxv[1] = yv;
+ xxv[2] = zv;
+ for (l=0; l<3; l++)
+ {
+ r = 0.0;
+ s = 0.0;
+ t = 0.0;
+ for (m=0; m<3; m++)
+ {
+ r += mat[l][m]*xx[m];
+ s += mat[l][m]*xxu[m];
+ t += mat[l][m]*xxv[m];
+ }
+ p[l] = r*radius+ev->offset3d[l];
+ pu[l] = s*radius;
+ pv[l] = t*radius;
+ }
+ n[0] = pu[1]*pv[2]-pu[2]*pv[1];
+ n[1] = pu[2]*pv[0]-pu[0]*pv[2];
+ n[2] = pu[0]*pv[1]-pu[1]*pv[0];
+ t = 1.0/sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);
+ n[0] *= t;
+ n[1] *= t;
+ n[2] *= t;
+ ev->ev[3*o+0] = p[0];
+ ev->ev[3*o+1] = p[1];
+ ev->ev[3*o+2] = p[2];
+ ev->evn[3*o+0] = n[0];
+ ev->evn[3*o+1] = n[1];
+ ev->evn[3*o+2] = n[2];
+ }
+ }
+ }
+
+ if (ev->appearance == APPEARANCE_DISTANCE_BANDS)
+ {
+ for (i=0; i<numv; i++)
+ {
+ if ((i & (NUMBDIST-1)) >= NUMBDIST/4 &&
+ (i & (NUMBDIST-1)) < 3*NUMBDIST/4)
+ continue;
+ if (ev->display_mode == DISP_WIREFRAME)
+ glBegin(GL_QUAD_STRIP);
+ else
+ glBegin(GL_TRIANGLE_STRIP);
+ for (j=0; j<=numu; j++)
+ {
+ for (k=0; k<=1; k++)
+ {
+ l = (i+k);
+ m = j;
+ o = l*(numu+1)+m;
+ glTexCoord2fv(&ev->tex[2*o]);
+ if (ev->colors != COLORS_ONESIDED && ev->colors != COLORS_TWOSIDED)
+ {
+ glColor3fv(&ev->col[4*o]);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ &ev->col[4*o]);
+ }
+ glNormal3fv(&ev->evn[3*o]);
+ glVertex3fv(&ev->ev[3*o]);
+ polys++;
+ }
+ }
+ glEnd();
+ }
+ }
+ else /* ev->appearance != APPEARANCE_DISTANCE_BANDS */
+ {
+ for (j=0; j<numu; j++)
+ {
+ if (ev->appearance == APPEARANCE_DIRECTION_BANDS &&
+ ((j & (NUMBDIR-1)) >= NUMBDIR/2))
+ continue;
+ if (ev->display_mode == DISP_WIREFRAME)
+ glBegin(GL_QUAD_STRIP);
+ else
+ glBegin(GL_TRIANGLE_STRIP);
+ for (i=0; i<=numv; i++)
+ {
+ for (k=0; k<=1; k++)
+ {
+ l = i;
+ m = (j+k);
+ o = l*(numu+1)+m;
+ glTexCoord2fv(&ev->tex[2*o]);
+ if (ev->colors != COLORS_ONESIDED && ev->colors != COLORS_TWOSIDED)
+ {
+ glColor3fv(&ev->col[4*o]);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ &ev->col[4*o]);
+ }
+ glNormal3fv(&ev->evn[3*o]);
+ glVertex3fv(&ev->ev[3*o]);
+ polys++;
+ }
+ }
+ glEnd();
+ }
+ }
+
+ polys /= 2;
+ return polys;
+}
+
+
+/* Generate a texture image that shows the orientation reversal. */
+static void gen_texture(ModeInfo *mi)
+{
+ etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)];
+
+ glGenTextures(1,&ev->tex_name);
+ glBindTexture(GL_TEXTURE_2D,ev->tex_name);
+ glPixelStorei(GL_UNPACK_ALIGNMENT,1);
+ glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+ glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
+ glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,TEX_DIMENSION,TEX_DIMENSION,0,
+ GL_LUMINANCE,GL_UNSIGNED_BYTE,texture);
+}
+
+
+static void init(ModeInfo *mi)
+{
+ static const GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
+ static const GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
+ static const GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
+ static const GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
+ static const GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
+ etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)];
+
+ if (deform_speed == 0.0)
+ deform_speed = 10.0;
+
+ if (init_deform < 0.0)
+ init_deform = 0.0;
+ if (init_deform >= 4000.0)
+ init_deform = 0.0;
+
+ if (walk_speed == 0.0)
+ walk_speed = 20.0;
+
+ if (ev->view == VIEW_TURN)
+ {
+ ev->alpha = frand(360.0);
+ ev->beta = frand(360.0);
+ ev->delta = frand(360.0);
+ }
+ else
+ {
+ ev->alpha = 0.0;
+ ev->beta = 0.0;
+ ev->delta = 0.0;
+ }
+ ev->umove = frand(2.0*M_PI);
+ ev->vmove = frand(2.0*M_PI);
+ ev->dumove = 0.0;
+ ev->dvmove = 0.0;
+ ev->side = 1;
+
+ ev->dd = init_deform*0.001;
+ ev->defdir = 1;
+
+ ev->rho = frand(360.0);
+ ev->sigma = frand(360.0);
+ ev->tau = frand(360.0);
+
+ ev->offset3d[0] = 0.0;
+ ev->offset3d[1] = 0.0;
+ ev->offset3d[2] = -2.0;
+
+ gen_texture(mi);
+ setup_etruscan_venus_color_texture(mi,0.0,2.0*M_PI,0.0,2.0*M_PI,NUMU,NUMV);
+
+ if (ev->marks)
+ glEnable(GL_TEXTURE_2D);
+ else
+ glDisable(GL_TEXTURE_2D);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ if (ev->projection == DISP_PERSPECTIVE || ev->view == VIEW_WALK)
+ {
+ if (ev->view == VIEW_WALK)
+ gluPerspective(60.0,1.0,0.01,10.0);
+ else
+ gluPerspective(60.0,1.0,0.1,10.0);
+ }
+ else
+ {
+ glOrtho(-1.0,1.0,-1.0,1.0,0.1,10.0);
+ }
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+# ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
+ if (ev->display_mode == DISP_WIREFRAME)
+ ev->display_mode = DISP_SURFACE;
+# endif
+
+ if (ev->display_mode == DISP_SURFACE)
+ {
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glShadeModel(GL_SMOOTH);
+ glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
+ glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
+ glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
+ glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
+ glLightfv(GL_LIGHT0,GL_POSITION,light_position);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
+ glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+ }
+ else if (ev->display_mode == DISP_TRANSPARENT)
+ {
+ glDisable(GL_DEPTH_TEST);
+ glShadeModel(GL_SMOOTH);
+ glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
+ glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
+ glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
+ glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
+ glLightfv(GL_LIGHT0,GL_POSITION,light_position);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
+ glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
+ glDepthMask(GL_FALSE);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA,GL_ONE);
+ }
+ else /* ev->display_mode == DISP_WIREFRAME */
+ {
+ glDisable(GL_DEPTH_TEST);
+ glShadeModel(GL_FLAT);
+ glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
+ glDisable(GL_LIGHTING);
+ glDisable(GL_LIGHT0);
+ glDisable(GL_BLEND);
+ }
+}
+
+
+/* Redisplay the Klein bottle. */
+static void display_etruscanvenus(ModeInfo *mi)
+{
+ etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)];
+
+ if (!ev->button_pressed)
+ {
+ if (deform)
+ {
+ ev->dd += ev->defdir*deform_speed*0.001;
+ if (ev->dd < 0.0)
+ ev->dd += 4.0;
+ if (ev->dd >= 4.0)
+ ev->dd -= 4.0;
+ /* Randomly change the deformation direction at one of the four
+ surface types in 10% of the cases. */
+ if (fabs(round(ev->dd)-ev->dd) <= deform_speed*0.0005)
+ {
+ if (LRAND() % 10 == 0)
+ ev->defdir = -ev->defdir;
+ }
+ }
+ if (ev->view == VIEW_TURN)
+ {
+ ev->alpha += speed_x*ev->speed_scale;
+ if (ev->alpha >= 360.0)
+ ev->alpha -= 360.0;
+ ev->beta += speed_y*ev->speed_scale;
+ if (ev->beta >= 360.0)
+ ev->beta -= 360.0;
+ ev->delta += speed_z*ev->speed_scale;
+ if (ev->delta >= 360.0)
+ ev->delta -= 360.0;
+ }
+ if (ev->view == VIEW_WALK)
+ {
+ ev->dumove = cos(walk_direction*M_PI/180.0)*walk_speed*M_PI/4096.0;
+ ev->dvmove = sin(walk_direction*M_PI/180.0)*walk_speed*M_PI/4096.0;
+ ev->umove += ev->dumove;
+ if (ev->umove >= 2.0*M_PI)
+ {
+ ev->umove -= 2.0*M_PI;
+ ev->vmove = 2.0*M_PI-ev->vmove;
+ ev->side = -ev->side;
+ }
+ if (ev->umove < 0.0)
+ {
+ ev->umove += 2.0*M_PI;
+ ev->vmove = 2.0*M_PI-ev->vmove;
+ ev->side = -ev->side;
+ }
+ ev->vmove += ev->dvmove;
+ if (ev->vmove >= 2.0*M_PI)
+ ev->vmove -= 2.0*M_PI;
+ if (ev->vmove < 0.0)
+ ev->vmove += 2.0*M_PI;
+ }
+ if (ev->change_colors)
+ {
+ ev->rho += DRHO;
+ if (ev->rho >= 360.0)
+ ev->rho -= 360.0;
+ ev->sigma += DSIGMA;
+ if (ev->sigma >= 360.0)
+ ev->sigma -= 360.0;
+ ev->tau += DTAU;
+ if (ev->tau >= 360.0)
+ ev->tau -= 360.0;
+ }
+ }
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ if (ev->projection == DISP_PERSPECTIVE || ev->view == VIEW_WALK)
+ {
+ if (ev->view == VIEW_WALK)
+ gluPerspective(60.0,ev->aspect,0.01,10.0);
+ else
+ gluPerspective(60.0,ev->aspect,0.1,10.0);
+ }
+ else
+ {
+ if (ev->aspect >= 1.0)
+ glOrtho(-ev->aspect,ev->aspect,-1.0,1.0,0.1,10.0);
+ else
+ glOrtho(-1.0,1.0,-1.0/ev->aspect,1.0/ev->aspect,0.1,10.0);
+ }
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ mi->polygon_count = etruscan_venus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI,NUMU,NUMV);
+}
+
+
+ENTRYPOINT void reshape_etruscanvenus(ModeInfo *mi, int width, int height)
+{
+ etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)];
+ int y = 0;
+
+ if (width > height * 5) { /* tiny window: show middle */
+ height = width;
+ y = -height/2;
+ }
+
+ ev->WindW = (GLint)width;
+ ev->WindH = (GLint)height;
+ glViewport(0,y,width,height);
+ ev->aspect = (GLfloat)width/(GLfloat)height;
+}
+
+
+ENTRYPOINT Bool etruscanvenus_handle_event(ModeInfo *mi, XEvent *event)
+{
+ etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)];
+
+ if (event->xany.type == ButtonPress && event->xbutton.button == Button1)
+ {
+ ev->button_pressed = True;
+ gltrackball_start(ev->trackball, event->xbutton.x, event->xbutton.y,
+ MI_WIDTH(mi), MI_HEIGHT(mi));
+ return True;
+ }
+ else if (event->xany.type == ButtonRelease &&
+ event->xbutton.button == Button1)
+ {
+ ev->button_pressed = False;
+ return True;
+ }
+ else if (event->xany.type == MotionNotify && ev->button_pressed)
+ {
+ gltrackball_track(ev->trackball, event->xmotion.x, event->xmotion.y,
+ MI_WIDTH(mi), MI_HEIGHT(mi));
+ return True;
+ }
+
+ return False;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *-----------------------------------------------------------------------------
+ * Xlock hooks.
+ *-----------------------------------------------------------------------------
+ *-----------------------------------------------------------------------------
+ */
+
+/*
+ *-----------------------------------------------------------------------------
+ * Initialize etruscanvenus. Called each time the window changes.
+ *-----------------------------------------------------------------------------
+ */
+
+ENTRYPOINT void init_etruscanvenus(ModeInfo *mi)
+{
+ etruscanvenusstruct *ev;
+
+ MI_INIT (mi, etruscanvenus);
+ ev = &etruscanvenus[MI_SCREEN(mi)];
+
+ ev->ev = calloc(3*(NUMU+1)*(NUMV+1),sizeof(float));
+ ev->evn = calloc(3*(NUMU+1)*(NUMV+1),sizeof(float));
+ ev->col = calloc(4*(NUMU+1)*(NUMV+1),sizeof(float));
+ ev->tex = calloc(2*(NUMU+1)*(NUMV+1),sizeof(float));
+
+ ev->trackball = gltrackball_init(True);
+ ev->button_pressed = False;
+
+ /* Set the display mode. */
+ if (!strcasecmp(mode,"random"))
+ {
+ ev->display_mode = random() % NUM_DISPLAY_MODES;
+ }
+ else if (!strcasecmp(mode,"wireframe"))
+ {
+ ev->display_mode = DISP_WIREFRAME;
+ }
+ else if (!strcasecmp(mode,"surface"))
+ {
+ ev->display_mode = DISP_SURFACE;
+ }
+ else if (!strcasecmp(mode,"transparent"))
+ {
+ ev->display_mode = DISP_TRANSPARENT;
+ }
+ else
+ {
+ ev->display_mode = random() % NUM_DISPLAY_MODES;
+ }
+
+ ev->marks = marks;
+
+ /* Orientation marks don't make sense in wireframe mode. */
+ if (ev->display_mode == DISP_WIREFRAME)
+ ev->marks = False;
+
+ /* Set the appearance. */
+ if (!strcasecmp(appear,"random"))
+ {
+ ev->appearance = random() % NUM_APPEARANCES;
+ }
+ else if (!strcasecmp(appear,"solid"))
+ {
+ ev->appearance = APPEARANCE_SOLID;
+ }
+ else if (!strcasecmp(appear,"distance-bands"))
+ {
+ ev->appearance = APPEARANCE_DISTANCE_BANDS;
+ }
+ else if (!strcasecmp(appear,"direction-bands"))
+ {
+ ev->appearance = APPEARANCE_DIRECTION_BANDS;
+ }
+ else
+ {
+ ev->appearance = random() % NUM_APPEARANCES;
+ }
+
+ /* Set the color mode. */
+ if (!strcasecmp(color_mode,"random"))
+ {
+ ev->colors = random() % NUM_COLORS;
+ }
+ else if (!strcasecmp(color_mode,"one-sided"))
+ {
+ ev->colors = COLORS_ONESIDED;
+ }
+ else if (!strcasecmp(color_mode,"two-sided"))
+ {
+ ev->colors = COLORS_TWOSIDED;
+ }
+ else if (!strcasecmp(color_mode,"distance"))
+ {
+ ev->colors = COLORS_DISTANCE;
+ }
+ else if (!strcasecmp(color_mode,"direction"))
+ {
+ ev->colors = COLORS_DIRECTION;
+ }
+ else
+ {
+ ev->colors = random() % NUM_COLORS;
+ }
+
+ ev->change_colors = change_colors;
+
+ /* Set the view mode. */
+ if (!strcasecmp(view_mode,"random"))
+ {
+ /* Select the walking mode only in 10% of the cases. */
+ if (LRAND() % 10 == 0)
+ ev->view = VIEW_WALK;
+ else
+ ev->view = VIEW_TURN;
+ }
+ else if (!strcasecmp(view_mode,"walk"))
+ {
+ ev->view = VIEW_WALK;
+ }
+ else if (!strcasecmp(view_mode,"turn"))
+ {
+ ev->view = VIEW_TURN;
+ }
+ else
+ {
+ /* Select the walking mode only in 10% of the cases. */
+ if (LRAND() % 10 == 0)
+ ev->view = VIEW_WALK;
+ else
+ ev->view = VIEW_TURN;
+ }
+
+ if (ev->view == VIEW_WALK)
+ {
+ /* Walking only works on the Ida surface. Therefore, set the initial
+ deformation to the Ida surface and switch off the deformation. */
+ init_deform = 3000.0;
+ deform = False;
+ }
+
+ /* Set the 3d projection mode. */
+ if (!strcasecmp(proj,"random"))
+ {
+ /* Orthographic projection only makes sense in turn mode. */
+ if (ev->view == VIEW_TURN)
+ ev->projection = random() % NUM_DISP_MODES;
+ else
+ ev->projection = DISP_PERSPECTIVE;
+ }
+ else if (!strcasecmp(proj,"perspective"))
+ {
+ ev->projection = DISP_PERSPECTIVE;
+ }
+ else if (!strcasecmp(proj,"orthographic"))
+ {
+ ev->projection = DISP_ORTHOGRAPHIC;
+ }
+ else
+ {
+ /* Orthographic projection only makes sense in turn mode. */
+ if (ev->view == VIEW_TURN)
+ ev->projection = random() % NUM_DISP_MODES;
+ else
+ ev->projection = DISP_PERSPECTIVE;
+ }
+
+ /* Make multiple screens rotate at slightly different rates. */
+ ev->speed_scale = 0.9+frand(0.3);
+
+ if ((ev->glx_context = init_GL(mi)) != NULL)
+ {
+ reshape_etruscanvenus(mi,MI_WIDTH(mi),MI_HEIGHT(mi));
+ glDrawBuffer(GL_BACK);
+ init(mi);
+ }
+ else
+ {
+ MI_CLEARWINDOW(mi);
+ }
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Called by the mainline code periodically to update the display.
+ *-----------------------------------------------------------------------------
+ */
+ENTRYPOINT void draw_etruscanvenus(ModeInfo *mi)
+{
+ Display *display = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+ etruscanvenusstruct *ev;
+
+ if (etruscanvenus == NULL)
+ return;
+ ev = &etruscanvenus[MI_SCREEN(mi)];
+
+ MI_IS_DRAWN(mi) = True;
+ if (!ev->glx_context)
+ return;
+
+ glXMakeCurrent(display, window, *ev->glx_context);
+
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ glLoadIdentity();
+
+ display_etruscanvenus(mi);
+
+ if (MI_IS_FPS(mi))
+ do_fps (mi);
+
+ glFlush();
+
+ glXSwapBuffers(display,window);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * The display is being taken away from us. Free up malloc'ed
+ * memory and X resources that we've alloc'ed.
+ *-----------------------------------------------------------------------------
+ */
+
+ENTRYPOINT void free_etruscanvenus(ModeInfo *mi)
+{
+ etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)];
+
+ if (!ev->glx_context) return;
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *ev->glx_context);
+
+ if (ev->ev) free(ev->ev);
+ if (ev->evn) free(ev->evn);
+ if (ev->col) free(ev->col);
+ if (ev->tex) free(ev->tex);
+ gltrackball_free(ev->trackball);
+ if (ev->tex_name) glDeleteTextures(1, &ev->tex_name);
+}
+
+#ifndef STANDALONE
+ENTRYPOINT void change_etruscanvenus(ModeInfo *mi)
+{
+ etruscanvenusstruct *ev = &etruscanvenus[MI_SCREEN(mi)];
+
+ if (!ev->glx_context)
+ return;
+
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *ev->glx_context);
+ init(mi);
+}
+#endif /* !STANDALONE */
+
+XSCREENSAVER_MODULE ("EtruscanVenus", etruscanvenus)
+
+#endif /* USE_GL */
diff --git a/hacks/glx/etruscanvenus.man b/hacks/glx/etruscanvenus.man
new file mode 100644
index 0000000..a96bd23
--- /dev/null
+++ b/hacks/glx/etruscanvenus.man
@@ -0,0 +1,372 @@
+.TH XScreenSaver 1 "" "X Version 11"
+.SH NAME
+etruscanvenus - Draws a 3d immersion of a Klein bottle that smoothly
+deforms between the Etruscan Venus surface, the Roman surface, the Boy
+surface surface, and the Ida surface.
+.SH SYNOPSIS
+.B etruscanvenus
+[\-display \fIhost:display.screen\fP]
+[\-install]
+[\-visual \fIvisual\fP]
+[\-window]
+[\-root]
+[\-delay \fIusecs\fP]
+[\-fps]
+[\-mode \fIdisplay-mode\fP]
+[\-wireframe]
+[\-surface]
+[\-transparent]
+[\-appearance \fIappearance\fP]
+[\-solid]
+[\-distance-bands]
+[\-direction-bands]
+[\-colors \fIcolor-scheme\fP]
+[\-onesided-colors]
+[\-twosided-colors]
+[\-distance-colors]
+[\-direction-colors]
+[\-no-change-colors]
+[\-view-mode \fIview-mode\fP]
+[\-walk]
+[\-turn]
+[\-no-deform]
+[\-deformation-speed \fIfloat\fP]
+[\-initial-deformation \fIfloat\fP]
+[\-etruscan-venus]
+[\-roman]
+[\-boy]
+[\-ida]
+[\-orientation-marks]
+[\-projection \fImode\fP]
+[\-perspective]
+[\-orthographic]
+[\-speed-x \fIfloat\fP]
+[\-speed-y \fIfloat\fP]
+[\-speed-z \fIfloat\fP]
+[\-walk-direction \fIfloat\fP]
+[\-walk-speed \fIfloat\fP]
+.SH DESCRIPTION
+The \fIetruscanvenus\fP program shows a 3d immersion of a Klein bottle
+that smoothly deforms between the Etruscan Venus surface, the Roman
+surface, the Boy surface, and the Ida surface. You can walk on the
+Klein bottle or turn it in 3d. Topologically, all surfaces are Klein
+bottles, even the Roman and Boy surfaces, which are doubly covered and
+therefore appear to be an immersed real projective plane. The smooth
+deformation between these surfaces was constructed by George
+K. Francis.
+.PP
+The Klein bottle is a non-orientable surface. To make this apparent,
+the two-sided color mode can be used. Alternatively, orientation
+markers (curling arrows) can be drawn as a texture map on the surface
+of the Klein bottle. While walking on the Klein bottle, you will
+notice that the orientation of the curling arrows changes (which it
+must because the Klein bottle is non-orientable). Since all the
+surfaces except the Ida surface have points where the surface normal
+is not well defined for some points, walking is only performed on the
+Ida surface.
+.PP
+As mentioned above, the Roman and Boy surfaces are doubly covered and
+therefore appear to be an immersed real projective plane. Since some
+of the parameter names are based on this interpretation of the
+surface, the geometry of the real projective plane will be briefly
+disussed. The real projective plane is a model for the projective
+geometry in 2d space. One point can be singled out as the origin. A
+line can be singled out as the line at infinity, i.e., a line that
+lies at an infinite distance to the origin. The line at infinity is
+topologically a circle. Points on the line at infinity are also used
+to model directions in projective geometry. Direction and distance
+bands refer to this interpretation of the surface. If direction bands
+are used, the bands extend from the origin of the projective plane in
+different directions to the line at infinity and back to the origin.
+If distance bands are used, the bands lie at constant distances to the
+origin. The same interpretation is used for distance and direction
+colors. Although there is no conceptually equivalent geometric
+interpretation for the two Klein bottle surfaces (the Etruscan Venus
+and Ida surfaces), the smooth deformation between the surfaces results
+in a natural extension of these concepts to the Klein bottle surfaces.
+.PP
+The immersed surfaces can be projected to the screen either
+perspectively or orthographically. When using the walking mode,
+perspective projection to the screen will be used.
+.PP
+There are three display modes for the Klein bottle: mesh (wireframe),
+solid, or transparent. Furthermore, the appearance of the surface can
+be as a solid object or as a set of see-through bands. The bands can
+be distance bands or direction bands, as explained above.
+.PP
+The colors with with the surface is drawn can be set to one-sided,
+two-sided, distance, or direction. In one-sided mode, the surface is
+drawn with the same color on both sides of the underlying triangles.
+In two-sided mode, the surface is drawn with one color on one side of
+the underlying triangles and the complementary color on the other
+side. Since the surface actually only has one side, the color jumps
+from red to green along a line on the surface. This mode enables you
+to see that the surface is non-orientable. In distance mode, the
+surface is displayed with fully saturated colors that depend on the
+distance of the points on the projective plane to the origin, as
+described above. If the surface is displayed as distance bands, each
+band will be displayed with a different color. In direction mode, the
+surface is displayed with fully saturated colors that depend on the
+angle of the points on the projective plane with respect to the origin
+(see above for an explanation). If the surface is displayed as
+direction bands, each band will be displayed with a different color.
+The colors used to color the surface can either be static or can be
+changed dynamically.
+.PP
+The rotation speed for each of the three coordinate axes around which
+the Klein bottle rotates can be chosen.
+.PP
+Furthermore, in the walking mode the walking direction in the 2d base
+square of the surface and the walking speed can be chosen. The
+walking direction is measured as an angle in degrees in the 2d square
+that forms the coordinate system of the surface. A value of 0 or 180
+means that the walk is along a circle at a randomly chosen distance
+from the origin (parallel to a distance band). A value of 90 or 270
+means that the walk is directly along a direction band. Any other
+value results in a curved path along the surface. As noted above,
+walking is performed only on the Ida surface.
+.PP
+By default, the immersion of the Klein bottle smoothly deforms between
+the Etruscan Venus surface, the Roman surface, the Boy surface, and
+the Ida surface. It is possible to choose the speed of the
+deformation. Furthermore, it is possible to switch the deformation
+off. It is also possible to determine the initial deformation of the
+immersion. This is mostly useful if the deformation is switched off,
+in which case it will determine the appearance of the surface. A
+value of 0 corresponds to the Etruscan Venus surface, a value of 1000
+to the Roman surface, a value of 2000 to the Boy surface, and a value
+of 3000 to the Ida surface.
+.PP
+This program is inspired by George K. Francis's book "A Topological
+Picturebook", Springer, 1987, by George K. Francis's paper "The
+Etruscan Venus" in P. Concus, R. Finn, and D. A. Hoffman: "Geometric
+Analysis and Computer Graphics", Springer, 1991, and by a video
+entitled "The Etruscan Venus" by Donna J. Cox, George K. Francis, and
+Raymond L. Idaszak, presented at SIGGRAPH 1989.
+.SH OPTIONS
+.I etruscanvenus
+accepts the following options:
+.TP 8
+.B \-window
+Draw on a newly-created window. This is the default.
+.TP 8
+.B \-root
+Draw on the root window.
+.TP 8
+.B \-install
+Install a private colormap for the window.
+.TP 8
+.B \-visual \fIvisual\fP
+Specify which visual to use. Legal values are the name of a visual
+class, or the id number (decimal or hex) of a specific visual.
+.TP 8
+.B \-delay \fImicroseconds\fP
+How much of a delay should be introduced between steps of the
+animation. Default 10000, or 1/100th second.
+.TP 8
+.B \-fps
+Display the current frame rate, CPU load, and polygon count.
+.PP
+The following four options are mutually exclusive. They determine how
+the Klein bottle is displayed.
+.TP 8
+.B \-mode random
+Display the Klein bottle in a random display mode (default).
+.TP 8
+.B \-mode wireframe \fP(Shortcut: \fB\-wireframe\fP)
+Display the Klein bottle as a wireframe mesh.
+.TP 8
+.B \-mode surface \fP(Shortcut: \fB\-surface\fP)
+Display the Klein bottle as a solid surface.
+.TP 8
+.B \-mode transparent \fP(Shortcut: \fB\-transparent\fP)
+Display the Klein bottle as a transparent surface.
+.PP
+The following four options are mutually exclusive. They determine the
+appearance of the Klein bottle.
+.TP 8
+.B \-appearance random
+Display the Klein bottle with a random appearance (default).
+.TP 8
+.B \-appearance solid \fP(Shortcut: \fB\-solid\fP)
+Display the Klein bottle as a solid object.
+.TP 8
+.B \-appearance distance-bands \fP(Shortcut: \fB\-distance-bands\fP)
+Display the Klein bottle as see-through bands that lie at increasing
+distances from the origin (see above for an explanation).
+.PP
+.TP 8
+.B \-appearance direction-bands \fP(Shortcut: \fB\-direction-bands\fP)
+Display the Klein bottle as see-through bands that lie at increasing
+angles with respect to the origin (see above for an explanation).
+.PP
+The following five options are mutually exclusive. They determine how
+to color the Klein bottle.
+.TP 8
+.B \-colors random
+Display the Klein bottle with a random color scheme (default).
+.TP 8
+.B \-colors onesided \fP(Shortcut: \fB\-onesided-colors\fP)
+Display the Klein bottle with a single color.
+.TP 8
+.B \-colors twosided \fP(Shortcut: \fB\-twosided-colors\fP)
+Display the Klein bottle with two colors: one color on one "side" and
+the complementary color on the "other side."
+.TP 8
+.B \-colors distance \fP(Shortcut: \fB\-distance-colors\fP)
+Display the Klein bottle with fully saturated colors that depend on
+the distance of the points on the projective plane to the origin (see
+above for an explanation). If the Klein bottle is displayed as
+distance bands, each band will be displayed with a different color.
+.TP 8
+.B \-colors direction \fP(Shortcut: \fB\-direction-colors\fP)
+Display the Klein bottle with fully saturated colors that depend on
+the angle of the points on the projective plane with respect to the
+origin (see above for an explanation). If the Klein bottle is
+displayed as direction bands, each band will be displayed with a
+different color.
+.PP
+The following options determine whether the colors with which the
+Klein bottle are displayed are static or are changing dynamically.
+.TP 8
+.B \-change-colors
+Change the colors with which the Klein bottle is displayed
+dynamically (default).
+.TP 8
+.B \-no-change-colors
+Use static colors to display the Klein bottle.
+.PP
+The following three options are mutually exclusive. They determine
+how to view the Klein bottle.
+.TP 8
+.B \-view-mode random
+View the Klein bottle in a random view mode (default). The walking
+mode will be randomly selected in approximately 10% of the cases.
+.TP 8
+.B \-view-mode turn \fP(Shortcut: \fB\-turn\fP)
+View the Klein bottle while it turns in 3d.
+.TP 8
+.B \-view-mode walk \fP(Shortcut: \fB\-walk\fP)
+View the Klein bottle as if walking on its surface.
+.PP
+The following options determine whether the surface is being deformed.
+.TP 8
+.B \-deform
+Deform the surface smoothly between the Etruscan Venus surface, the
+Roman surface, the Boy surface surface, and the Ida surface (default).
+.TP 8
+.B \-no-deform
+Don't deform the surface.
+.PP
+The following option determines the deformation speed.
+.TP 8
+.B \-deformation-speed \fIfloat\fP
+The deformation speed is measured in percent of some sensible maximum
+speed (default: 10.0).
+.PP
+The following options determine the initial deformation of the
+surface. As described above, this is mostly useful if
+\fB\-no-deform\fP is specified.
+.TP 8
+.B \-initial-deformation \fIfloat\fP
+The initial deformation is specified as a number between 0 and 4000.
+A value of 0 corresponds to the Etruscan Venus surface, a value of
+1000 to the Roman surface, a value of 2000 to the Boy surface, and a
+value of 3000 to the Ida surface. The default value is 0.
+.TP 8
+.B \-etruscan-venus
+This is a shortcut for \fB\-initial-deformation 0\fP.
+.TP 8
+.B \-roman
+This is a shortcut for \fB\-initial-deformation 1000\fP.
+.TP 8
+.B \-boy
+This is a shortcut for \fB\-initial-deformation 2000\fP.
+.TP 8
+.B \-ida
+This is a shortcut for \fB\-initial-deformation 3000\fP.
+.PP
+The following options determine whether orientation marks are shown on
+the Klein bottle.
+.TP 8
+.B \-orientation-marks
+Display orientation marks on the Klein bottle.
+.TP 8
+.B \-no-orientation-marks
+Don't display orientation marks on the Klein bottle (default).
+.PP
+The following three options are mutually exclusive. They determine
+how the Klain bottle is projected from 3d to 2d (i.e., to the screen).
+.TP 8
+.B \-projection random
+Project the Klein bottle from 3d to 2d using a random projection mode
+(default).
+.TP 8
+.B \-projection perspective \fP(Shortcut: \fB\-perspective\fP)
+Project the Klein bottle from 3d to 2d using a perspective projection.
+.TP 8
+.B \-projection orthographic \fP(Shortcut: \fB\-orthographic\fP)
+Project the Klein bottle from 3d to 2d using an orthographic
+projection.
+.PP
+The following three options determine the rotation speed of the Klein
+bottle around the three possible axes. The rotation speed is measured
+in degrees per frame. The speeds should be set to relatively small
+values, e.g., less than 4 in magnitude. In walk mode, all speeds are
+ignored.
+.TP 8
+.B \-speed-x \fIfloat\fP
+Rotation speed around the x axis (default: 1.1).
+.TP 8
+.B \-speed-y \fIfloat\fP
+Rotation speed around the y axis (default: 1.3).
+.TP 8
+.B \-speed-z \fIfloat\fP
+Rotation speed around the z axis (default: 1.5).
+.PP
+The following two options determine the walking speed and direction.
+.TP 8
+.B \-walk-direction \fIfloat\fP
+The walking direction is measured as an angle in degrees in the 2d
+square that forms the coordinate system of the surface of the Klein
+bottle (default: 83.0). A value of 0 or 180 means that the walk is
+along a circle at a randomly chosen distance from the origin (parallel
+to a distance band). A value of 90 or 270 means that the walk is
+directly along a direction band. Any other value results in a curved
+path along the surface. As noted above, walking is performed only on
+the Ida surface.
+.TP 8
+.B \-walk-speed \fIfloat\fP
+The walking speed is measured in percent of some sensible maximum
+speed (default: 20.0).
+.SH INTERACTION
+If you run this program in standalone mode in its turn mode, you can
+rotate the Klein bottle by dragging the mouse while pressing the left
+mouse button. This rotates the Klein bottle in 3d. To examine the
+Klein bottle at your leisure, it is best to set all speeds to 0.
+Otherwise, the Klein bottle will rotate while the left mouse button is
+not pressed. This kind of interaction is not available in the walk
+mode.
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get the default host and display number.
+.TP 8
+.B XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.SH SEE ALSO
+.BR X (1),
+.BR xscreensaver (1)
+.SH COPYRIGHT
+Copyright \(co 2019-2020 by Carsten Steger. Permission to use, copy,
+modify, distribute, and sell this software and its documentation for
+any purpose is hereby granted without fee, provided that the above
+copyright notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting documentation.
+No representations are made about the suitability of this software for
+any purpose. It is provided "as is" without express or implied
+warranty.
+.SH AUTHOR
+Carsten Steger <carsten@mirsanmir.org>, 05-jan-2020.
diff --git a/hacks/glx/gibson.c b/hacks/glx/gibson.c
new file mode 100644
index 0000000..8f99040
--- /dev/null
+++ b/hacks/glx/gibson.c
@@ -0,0 +1,1335 @@
+/* gibson, Copyright (c) 2020 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Hacking the Gibson, as per the 1995 classic film, HACKERS.
+ *
+ * In the movie, this was primarily a practical effect: the towers were
+ * edge-lit etched perspex, each about four feet tall.
+ */
+
+#define TOWER_FONT "-*-helvetica-bold-r-normal-*-*-480-*-*-*-*-*-*"
+
+#define DEFAULTS "*delay: 20000 \n" \
+ "*groundColor: #8A2BE2" "\n" \
+ "*towerColor: #4444FF" "\n" \
+ "*towerText: #DDDDFF" "\n" \
+ "*towerText2: #FF0000" "\n" \
+ "*towerFont: " TOWER_FONT "\n" \
+ "*showFPS: False \n" \
+ "*wireframe: False \n" \
+
+# define release_gibson 0
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#include "xlockmore.h"
+#include "colors.h"
+#include "rotator.h"
+#include "texfont.h"
+#include <ctype.h>
+
+#ifdef USE_GL /* whole file */
+
+
+#define DEF_SPEED "1.0"
+#define DEF_TEXTURE "True"
+#define DEF_GRID_WIDTH "6"
+#define DEF_GRID_HEIGHT "7"
+#define DEF_GRID_DEPTH "6"
+#define DEF_GRID_SPACING "2.0"
+#define DEF_COLUMNS "5"
+
+#define GROUND_QUAD_SIZE 30
+
+typedef struct {
+ GLfloat x, y, h;
+ GLuint fg_dlists[5], bg_dlists[5];
+ int fg_polys, bg_polys;
+ unsigned int face_mode; /* 5 bit field */
+} tower;
+
+typedef struct {
+ GLXContext *glx_context;
+ Bool button_down_p;
+ rotator *rot, *rot2;
+ GLfloat xscroll, yscroll;
+ GLfloat oxscroll, oyscroll;
+
+ GLuint ground_dlist;
+ GLuint tower_dlist;
+ int ground_polys, tower_polys;
+ GLfloat ground_y;
+ GLfloat billboard_y;
+ const char *billboard_text;
+
+ int ntowers;
+ tower *towers;
+ GLfloat tower_color[4];
+ GLfloat tower_color2[4];
+ GLfloat edge_color[4];
+ GLfloat bg_color[4];
+ Bool startup_p;
+
+ struct {
+ GLuint texid;
+ XCharStruct metrics;
+ int width, height;
+ texture_font_data *font_data;
+ int ascent, descent, em_width;
+ char *text;
+ } text[2];
+
+} gibson_configuration;
+
+static gibson_configuration *ccs = NULL;
+
+static GLfloat speed;
+static Bool do_tex;
+static int grid_width;
+static int grid_height;
+static int grid_depth;
+static GLfloat grid_spacing;
+static int columns;
+
+static XrmOptionDescRec opts[] = {
+ { "-speed", ".speed", XrmoptionSepArg, 0 },
+ { "-texture", ".texture", XrmoptionNoArg, "True" },
+ { "+texture", ".texture", XrmoptionNoArg, "False" },
+ { "-grid-width", ".gridWidth", XrmoptionSepArg, 0 },
+ { "-grid-height", ".gridHeight", XrmoptionSepArg, 0 },
+ { "-grid-depth", ".gridDepth", XrmoptionSepArg, 0 },
+ { "-spacing", ".gridSpacing", XrmoptionSepArg, 0 },
+ { "-columns", ".columns", XrmoptionSepArg, 0 },
+};
+
+static argtype vars[] = {
+ {&speed, "speed", "Speed", DEF_SPEED, t_Float},
+ {&do_tex, "texture", "Texture", DEF_TEXTURE, t_Bool},
+ {&grid_width, "gridWidth", "GridWidth", DEF_GRID_WIDTH, t_Int},
+ {&grid_height, "gridHeight", "GridHeight", DEF_GRID_HEIGHT, t_Int},
+ {&grid_depth, "gridDepth", "GridDepth", DEF_GRID_DEPTH, t_Int},
+ {&grid_spacing, "gridSpacing", "GridSpacing", DEF_GRID_SPACING, t_Float},
+ {&columns, "columns", "Columns", DEF_COLUMNS, t_Int},
+};
+
+ENTRYPOINT ModeSpecOpt gibson_opts = {
+ countof(opts), opts, countof(vars), vars, NULL};
+
+
+ENTRYPOINT void
+reshape_gibson (ModeInfo *mi, int width, int height)
+{
+ GLfloat h = (GLfloat) height / (GLfloat) width;
+ int y = 0;
+
+ glViewport (0, y, (GLint) width, (GLint) height);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+# ifdef DEBUG
+ gluPerspective (30, 1/h, 1, 100);
+# else
+ gluPerspective (100, 1/h/4,
+ 1.0,
+ 20 * grid_depth * 1.5 * (1 + grid_spacing));
+# endif
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt( 0, 0, 1,
+ 0, 0, 0,
+ 0, 1, 0);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+
+/* Copied from gltrackball.c */
+static void
+adjust_for_device_rotation (double *x, double *y, double *w, double *h)
+{
+ int rot = (int) current_device_rotation();
+ int swap;
+
+ while (rot <= -180) rot += 360;
+ while (rot > 180) rot -= 360;
+
+ if (rot > 135 || rot < -135) /* 180 */
+ {
+ *x = *w - *x;
+ *y = *h - *y;
+ }
+ else if (rot > 45) /* 90 */
+ {
+ swap = *x; *x = *y; *y = swap;
+ swap = *w; *w = *h; *h = swap;
+ *x = *w - *x;
+ }
+ else if (rot < -45) /* 270 */
+ {
+ swap = *x; *x = *y; *y = swap;
+ swap = *w; *w = *h; *h = swap;
+ *y = *h - *y;
+ }
+}
+
+
+ENTRYPOINT Bool
+gibson_handle_event (ModeInfo *mi, XEvent *event)
+{
+ gibson_configuration *bp = &ccs[MI_SCREEN(mi)];
+ double w = MI_WIDTH(mi);
+ double h = MI_HEIGHT(mi);
+ double xoff = 0, yoff = 0;
+
+ if (event->xany.type == ButtonPress ||
+ event->xany.type == ButtonRelease)
+ {
+ double x = event->xbutton.x;
+ double y = event->xbutton.y;
+ adjust_for_device_rotation (&x, &y, &w, &h);
+ xoff = (x / w) - 0.5;
+ yoff = (event->xbutton.y / h) - 0.5;
+ bp->button_down_p = (event->xany.type == ButtonPress);
+ bp->oxscroll = xoff;
+ bp->oyscroll = yoff;
+
+ return True;
+ }
+ else if (event->xany.type == MotionNotify)
+ {
+ double x = event->xmotion.x;
+ double y = event->xmotion.y;
+ adjust_for_device_rotation (&x, &y, &w, &h);
+ xoff = (x / w) - 0.5;
+ yoff = (y / h) - 0.5;
+ if (bp->button_down_p)
+ {
+ bp->xscroll += xoff - bp->oxscroll;
+ bp->yscroll += yoff - bp->oyscroll;
+ bp->oxscroll = xoff;
+ bp->oyscroll = yoff;
+ }
+ return True;
+ }
+
+ return False;
+}
+
+
+static void
+parse_color (ModeInfo *mi, char *key, GLfloat color[4])
+{
+ XColor xcolor;
+ char *string = get_string_resource (mi->dpy, key, "Color");
+ if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor))
+ {
+ fprintf (stderr, "%s: unparsable color in %s: %s\n", progname,
+ key, string);
+ exit (1);
+ }
+ free (string);
+
+ color[0] = xcolor.red / 65536.0;
+ color[1] = xcolor.green / 65536.0;
+ color[2] = xcolor.blue / 65536.0;
+ color[3] = 1;
+}
+
+
+static int
+draw_ground (ModeInfo *mi)
+{
+ int wire = MI_IS_WIREFRAME(mi);
+ int polys = 0;
+ int x, y;
+ int cells = 20;
+ GLfloat color[4];
+ GLfloat color0[4];
+ GLfloat cell_size = 1.0;
+ GLfloat z = -0.005;
+
+ parse_color (mi, "groundColor", color);
+ parse_color (mi, "towerColor", color0);
+ color0[0] *= 0.05;
+ color0[1] *= 0.05;
+ color0[2] *= 0.3;
+ color0[3] = 1;
+
+ if (!wire)
+ {
+ GLfloat fog_color[4] = { 0, 0, 0, 1 };
+
+ glFogi (GL_FOG_MODE, GL_EXP2);
+ glFogfv (GL_FOG_COLOR, fog_color);
+ glFogf (GL_FOG_DENSITY, 0.015);
+ glFogf (GL_FOG_START, -cells/2 * cell_size);
+ glEnable (GL_FOG);
+ }
+
+ glPushMatrix();
+ glScalef (1.0/cells, 1.0/cells, 1);
+ glTranslatef (-cells/2.0, -cells/2.0, 0);
+ glTranslatef (0.5, 0, 0);
+
+ glBegin (GL_QUADS); /* clipping quad */
+ glColor4fv (color0);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color0);
+
+ glVertex3f (0, 0, z);
+ glVertex3f (cells * cell_size, 0, z);
+ glVertex3f (cells * cell_size, cells * cell_size, z);
+ glVertex3f (0, cells * cell_size, z);
+ glEnd();
+ polys++;
+
+ glColor4fv (color);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+
+ for (y = 0; y < cells; y++)
+ for (x = 0; x < cells; x++)
+ {
+ GLfloat a = 0;
+ GLfloat b = 1.0/3;
+ GLfloat c = 2.0/3;
+ GLfloat d = 1.0;
+ GLfloat w = 0.02;
+
+ glPushMatrix();
+ glTranslatef (x, y, 0);
+
+ glNormal3f (0, 0, 1);
+
+ switch (random() % 4) {
+ case 0:
+ glRotatef (90, 0, 0, 1);
+ glTranslatef (0, -1, 0);
+ break;
+ case 1:
+ glRotatef (-90, 0, 0, 1);
+ glTranslatef (-1, 0, 0);
+ break;
+ case 2:
+ glRotatef (180, 0, 0, 1);
+ glTranslatef (-1, -1, 0);
+ break;
+ default: break;
+ }
+
+ switch (random() % 2) {
+ case 0:
+ glScalef (-1, -1, 1);
+ glTranslatef (-1, -1, 0);
+ break;
+ default: break;
+ }
+
+ switch (random() % 2) {
+ case 0:
+
+ glBegin (wire ? GL_LINE_LOOP : GL_QUAD_STRIP);
+ glVertex3f (a, b+w, 0);
+ glVertex3f (a, b-w, 0); polys++;
+ glVertex3f (b+w, a, 0); polys++;
+ glVertex3f (b-w, a, 0); polys++;
+ glEnd();
+
+ glBegin (wire ? GL_LINE_LOOP : GL_QUAD_STRIP);
+ glVertex3f (a, c+w, 0);
+ glVertex3f (a, c-w, 0); polys++;
+ glVertex3f (b+w, c+w, 0); polys++;
+ glVertex3f (b, c-w, 0); polys++;
+ glVertex3f (c+w, b+w, 0); polys++;
+ glVertex3f (c-w, b, 0); polys++;
+ glVertex3f (c+w, a, 0); polys++;
+ glVertex3f (c-w, a, 0); polys++;
+ glEnd();
+
+/*
+ glBegin (wire ? GL_LINE_LOOP : GL_QUAD_STRIP);
+ glVertex3f (c+w, d, 0);
+ glVertex3f (c-w, d, 0); polys++;
+ glVertex3f (d, c+w, 0); polys++;
+ glVertex3f (d, c-w, 0); polys++;
+ glEnd();
+*/
+ break;
+
+ default:
+ glBegin (wire ? GL_LINE_LOOP : GL_QUAD_STRIP);
+ glVertex3f (a+w, d, 0);
+ glVertex3f (a, d, 0); polys++;
+ glVertex3f (a+w, d, 0);
+ glVertex3f (a, d-w, 0); polys++;
+ glVertex3f (b+w, c-w, 0); polys++;
+ glVertex3f (b-w, c-w, 0); polys++;
+ glVertex3f (b+w, a, 0); polys++;
+ glVertex3f (b-w, a, 0); polys++;
+ glEnd();
+
+ glBegin (wire ? GL_LINE_LOOP : GL_QUAD_STRIP);
+ glVertex3f (b+w, d, 0);
+ glVertex3f (b-w, d, 0); polys++;
+ glVertex3f (c+w, c-w, 0); polys++;
+ glVertex3f (c-w, c-w, 0); polys++;
+ glVertex3f (c+w, a, 0); polys++;
+ glVertex3f (c-w, a, 0); polys++;
+ glEnd();
+ break;
+ }
+
+ glPopMatrix();
+ }
+ glPopMatrix();
+
+ if (!wire)
+ {
+ glDisable (GL_BLEND);
+ glDisable (GL_FOG);
+ }
+
+ return polys;
+}
+
+
+/* qsort comparator for sorting towers by y position */
+static int
+cmp_towers (const void *aa, const void *bb)
+{
+ const tower *a = (tower *) aa;
+ const tower *b = (tower *) bb;
+ return ((int) (b->y * 10000) -
+ (int) (a->y * 10000));
+}
+
+
+static GLfloat
+ease_fn (GLfloat r)
+{
+ return cos ((r/2 + 1) * M_PI) + 1; /* Smooth curve up, end at slope 1. */
+}
+
+
+static GLfloat
+ease_ratio (GLfloat r)
+{
+ GLfloat ease = 0.5;
+ if (r <= 0) return 0;
+ else if (r >= 1) return 1;
+ else if (r <= ease) return ease * ease_fn (r / ease);
+ else if (r > 1-ease) return 1 - ease * ease_fn ((1 - r) / ease);
+ else return r;
+}
+
+
+/* Draws the text quads on the face.
+ First pass is for the small background text, second is for the big block.
+ */
+static int
+draw_tower_face_text (ModeInfo *mi, GLfloat height, Bool which)
+{
+ gibson_configuration *bp = &ccs[MI_SCREEN(mi)];
+ int wire = MI_IS_WIREFRAME(mi);
+ int polys = 0;
+ Bool wire2 = False; /* Debugging quads */
+ Bool bg_p = (which == 1 && do_tex && !wire);
+
+ glColor4fv (which ? bp->tower_color2 : bp->tower_color);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
+ which ? bp->tower_color2 : bp->tower_color);
+
+ /* The texture is a tex_width x tex_height rectangle, of which we
+ only use the rbearing+lbearing x ascent+descent sub-rectangle.
+ Texture coordinates reference the tex_width x tex_height rectangle
+ as a 0.0 - 1.0 coordinate.
+ */
+ int n = which ? 1 : 0;
+ GLfloat twratio = ((bp->text[n].metrics.rbearing +
+ bp->text[n].metrics.lbearing) /
+ (GLfloat) bp->text[n].width);
+ GLfloat thratio = ((bp->text[n].metrics.ascent +
+ bp->text[n].metrics.descent) /
+ (GLfloat) bp->text[n].height);
+ GLfloat aspect = ((bp->text[n].ascent + bp->text[n].descent) /
+ (GLfloat) bp->text[n].em_width);
+
+ GLfloat sx = 1.0 / (which ? 1 : columns);
+ GLfloat sy = (which
+ ? height * 0.8
+ : sx * 4); /* Tweaked to match gluPerspective */
+
+ GLfloat lines_in_tex = ((bp->text[n].metrics.ascent +
+ bp->text[n].metrics.descent) /
+ (GLfloat)
+ (bp->text[n].ascent + bp->text[n].descent));
+ GLfloat tex_lines = (which ? 3 : 8); /* Put this many lines in each quad */
+
+ GLfloat tsx = sx * twratio;
+ GLfloat tsy = sy * thratio * tex_lines / lines_in_tex * aspect;
+ GLfloat x1, tx1;
+ GLfloat margin = 0.2;
+ GLfloat m2 = margin/2 / (which ? 1 : columns);
+ GLfloat m3 = m2 / (which ? 1 : height);
+ GLfloat h2 = height * (which ? 1-margin : 1);
+
+ glBindTexture (GL_TEXTURE_2D, bp->text[n].texid);
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ if (!wire && !wire2 && !bg_p) glBegin (GL_QUADS);
+ for (x1 = 0, tx1 = 0; x1 < 1.0; x1 += sx, tx1 += tsx)
+ {
+ GLfloat x2 = x1 + sx;
+ GLfloat tx2 = tx1 + tsx;
+ GLfloat y2, ty2;
+ GLfloat z = (which ? 0.05 : 0);
+
+ tx1 = 0;
+ tx2 = twratio;
+
+ for (y2 = h2, ty2 = thratio;
+ y2 > 0;
+ y2 -= sy, ty2 -= tsy)
+ {
+ GLfloat y1 = y2 - sy * (1-margin);
+ GLfloat ty1 = ty2 - tsy;
+ GLfloat toff = frand ((bp->text[n].metrics.ascent +
+ bp->text[n].metrics.descent)
+ * 0.8);
+
+ if (y1 < 0) /* Clip the panel to the bottom of the tower face */
+ {
+ tsy = y2 / (y2 - y1);
+ y1 = 0;
+ }
+
+ ty1 = toff;
+ ty2 = ty1 + tsy;
+
+ if (wire2 && which) glColor3f (1,0,0);
+ if (wire || wire2 || bg_p)
+ glBegin (!wire && (wire2 || bg_p) ? GL_QUADS : GL_LINE_LOOP);
+ glTexCoord2f(tx1, ty2); glVertex3f (x1+m2, y1+m3, z);
+ glTexCoord2f(tx2, ty2); glVertex3f (x2-m2, y1+m3, z);
+ glTexCoord2f(tx2, ty1); glVertex3f (x2-m2, y2-m3, z);
+ glTexCoord2f(tx1, ty1); glVertex3f (x1+m2, y2-m3, z);
+ if (wire || wire2 || bg_p)
+ glEnd();
+ polys++;
+
+ if (bg_p)
+ {
+ GLfloat bg[4] = { 1, 1, 1, 0.2 };
+ z -= 0.1;
+ m2 -= 0.03;
+ m3 -= 0.03;
+ glColor4fv (bg);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, bg);
+ }
+
+ if ((wire2 || bg_p) && !wire)
+ {
+ if (do_tex) glDisable(GL_TEXTURE_2D);
+ glBegin (bg_p ? GL_QUADS : GL_LINE_LOOP);
+ glVertex3f (x1+m2, y1+m3, z);
+ glVertex3f (x2-m2, y1+m3, z);
+ glVertex3f (x2-m2, y2-m3, z);
+ glVertex3f (x1+m2, y2-m3, z);
+ glEnd();
+ polys++;
+ if (do_tex) glEnable(GL_TEXTURE_2D);
+ }
+
+ if (which) break;
+ }
+ }
+ if (!wire && !wire2 && !bg_p) glEnd();
+
+ return polys;
+}
+
+
+/* Draws the wall of the face, and the edges, then the text quads on it.
+ */
+static int
+draw_tower_face (ModeInfo *mi, GLfloat height, int mode)
+{
+ gibson_configuration *bp = &ccs[MI_SCREEN(mi)];
+ int wire = MI_IS_WIREFRAME(mi);
+ int polys = 0;
+
+ switch (mode) {
+ case 0:
+ if (! wire)
+ {
+ GLfloat m = 0.015;
+ GLfloat z = -0.0005;
+ if (do_tex) glDisable (GL_TEXTURE_2D);
+
+ glNormal3f (0, 0, 1);
+
+ glColor4fv (bp->bg_color);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
+ bp->bg_color);
+
+ glBegin (GL_QUADS);
+ glVertex3f (0, 0, z*2); /* background */
+ glVertex3f (1, 0, z*2);
+ glVertex3f (1, height, z*2);
+ glVertex3f (0, height, z*2);
+ polys++;
+ glEnd();
+
+ glColor4fv (bp->edge_color);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
+ bp->edge_color);
+
+ glBegin (GL_QUADS);
+ glVertex3f (0, 0, z); /* left */
+ glVertex3f (m, 0, z);
+ glVertex3f (m, height, z);
+ glVertex3f (0, height, z);
+ polys++;
+
+ glVertex3f (1-m, 0, 0); /* right */
+ glVertex3f (1, 0, 0);
+ glVertex3f (1, height, z);
+ glVertex3f (1-m, height, z);
+ polys++;
+
+ glVertex3f (m, 0, 0); /* bottom */
+ glVertex3f (1-m, 0, 0);
+ glVertex3f (1-m, m, 0);
+ glVertex3f (m, m, 0);
+ polys++;
+
+ glVertex3f (m, height-m, z); /* top */
+ glVertex3f (1-m, height-m, z);
+ glVertex3f (1-m, height, z);
+ glVertex3f (m, height, z);
+ polys++;
+ glEnd();
+
+ if (do_tex) glEnable (GL_TEXTURE_2D);
+ }
+ break;
+ case 1:
+ polys += draw_tower_face_text (mi, height, 0);
+ break;
+ case 2:
+ polys += draw_tower_face_text (mi, height * 0.7, 1);
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ return polys;
+}
+
+
+/* Mode 0: draws 5 sides of the box
+ Mode 1: just background text
+ Mode 2: just foreground text
+ */
+static int
+draw_tower (ModeInfo *mi, tower *t, int mode, int face)
+{
+ GLfloat height = grid_height;
+
+ int polys = 0;
+
+ glPushMatrix();
+ glTranslatef (-0.5, 0.5, 0);
+
+ if (face == 0 || face == -1)
+ {
+ glPushMatrix(); /* top */
+ glTranslatef (0, 0, height);
+ polys += draw_tower_face (mi, 1.0, mode);
+ glPopMatrix();
+ }
+
+ if (face == 1 || face == -1)
+ {
+ glPushMatrix(); /* left */
+ glRotatef ( 90, 1, 0, 0);
+ glRotatef (-90, 0, 1, 0);
+ glTranslatef (-1, 0, 0);
+ polys += draw_tower_face (mi, height, mode);
+ glPopMatrix();
+ }
+
+ if (face == 2 || face == -1)
+ {
+ glPushMatrix(); /* back */
+ glRotatef ( 90, 1, 0, 0);
+ glRotatef (180, 0, 1, 0);
+ glTranslatef (-1, 0, 1);
+ polys += draw_tower_face (mi, height, mode);
+ glPopMatrix();
+ }
+
+ if (face == 3 || face == -1)
+ {
+ glPushMatrix(); /* right */
+ glRotatef ( 90, 1, 0, 0);
+ glRotatef ( 90, 0, 1, 0);
+ glTranslatef (0, 0, 1);
+ polys += draw_tower_face (mi, height, mode);
+ glPopMatrix();
+ }
+
+ if (face == 4 || face == -1)
+ {
+ glPushMatrix(); /* front */
+ glRotatef ( 90, 1, 0, 0);
+ polys += draw_tower_face (mi, height, mode);
+ glPopMatrix();
+ }
+
+ if (face < -1 || face > 4) abort();
+
+ glPopMatrix();
+ return polys;
+}
+
+
+static void
+animate_towers (ModeInfo *mi)
+{
+ gibson_configuration *bp = &ccs[MI_SCREEN(mi)];
+ int ii;
+ GLfloat min = -3;
+ GLfloat max = grid_depth * (1 + grid_spacing) - grid_spacing - 1;
+ GLfloat yspeed = speed * 0.05;
+
+ for (ii = 0; ii < 20; ii++)
+ {
+ int jj, kk;
+
+ /* randomly trade two towers' fg dlists */
+ if (0 == (random() % 20))
+ {
+ int i = random() % bp->ntowers;
+ int j = random() % bp->ntowers;
+ int k = random() % countof(bp->towers[i].fg_dlists);
+ GLuint d1 = bp->towers[i].bg_dlists[k];
+ GLuint d2 = bp->towers[j].bg_dlists[k];
+ bp->towers[i].bg_dlists[k] = d2;
+ bp->towers[j].bg_dlists[k] = d1;
+ }
+
+ /* randomly trade two towers' bg dlists */
+ if (1) /* (0 == (random() % 3)) */
+ {
+ int i = random() % bp->ntowers;
+ int j = random() % bp->ntowers;
+ int k = random() % countof(bp->towers[i].fg_dlists);
+ GLuint d1 = bp->towers[i].fg_dlists[k];
+ GLuint d2 = bp->towers[j].fg_dlists[k];
+ bp->towers[i].fg_dlists[k] = d2;
+ bp->towers[j].fg_dlists[k] = d1;
+ }
+
+ /* Randomize whether it's displaying fg text or bg text */
+ for (jj = 0; jj < bp->ntowers; jj++)
+ for (kk = 0; kk < countof(bp->towers[jj].fg_dlists); kk++)
+ {
+ /* Re-choose every N frames. Display fg 1 in M. */
+ int frames = 500;
+ int fg_chance = (kk == 0 ? 100000 : 10);
+ unsigned int o = !!(bp->towers[jj].face_mode & (1 << kk));
+ unsigned int n = !!((random() % frames) ? o :
+ (0 == (random() % fg_chance)));
+ bp->towers[jj].face_mode =
+ ((bp->towers[jj].face_mode & ~(1 << kk)) | (n << kk));
+ }
+ }
+
+ for (ii = 0; ii < bp->ntowers; ii++)
+ {
+ tower *t = &bp->towers[ii];
+ t->h += speed * 0.01;
+ if (t->h > 1) t->h = 1;
+
+ t->y -= yspeed;
+
+ if (t->y < min)
+ {
+ t->h = 0;
+ t->y = max;
+ }
+ }
+
+ /* Sorting by depth improves frame rate slightly. */
+ qsort (bp->towers, bp->ntowers, sizeof(*bp->towers), cmp_towers);
+
+ bp->ground_y -= yspeed / GROUND_QUAD_SIZE;
+ if (bp->ground_y < 1)
+ bp->ground_y += 1;
+
+ bp->billboard_y -= yspeed;
+ if (bp->billboard_y < min || !bp->billboard_text)
+ {
+ const char *const ss[] = {
+ "ACCESS GRANTED",
+ "ACCESS GRANTED",
+ "ACCESS DENIED",
+ "ACCESS DENIED",
+ "ACCESS DENIED",
+ "ACCESS DENIED",
+ "ACCESS DENIED",
+ "PASSWORD ACCEPTED",
+ " GIVE ME\nA COOKIE",
+ "MESS WITH THE BEST\n DIE LIKE THE REST",
+ };
+
+ bp->billboard_y = max * (1 + frand(8));
+ bp->billboard_text = ss[random() % countof(ss)];
+ }
+}
+
+
+static int
+draw_billboard (ModeInfo *mi)
+{
+ gibson_configuration *bp = &ccs[MI_SCREEN(mi)];
+ int polys = 0;
+ int wire = MI_IS_WIREFRAME(mi);
+ GLfloat w, h, s, margin, margin2;
+ XCharStruct metrics;
+ int ascent, descent;
+ texture_font_data *font = bp->text[1].font_data;
+ GLfloat color[4];
+ GLfloat y = grid_height * 0.3;
+
+ texture_string_metrics (font, bp->billboard_text,
+ &metrics, &ascent, &descent);
+ w = metrics.lbearing + metrics.rbearing;
+ h = metrics.ascent + metrics.descent;
+ s = 1.0 / w;
+ s *= 0.95;
+
+ margin = w * 0.1;
+ margin2 = margin * 1.7;
+
+ glPushMatrix();
+
+ glTranslatef (-0.5, bp->billboard_y, y);
+ glRotatef (90, 1, 0, 0);
+ glScalef (s, s * 1.5, s);
+
+ memcpy (color, bp->tower_color2, sizeof(color));
+ color[3] = 0.6;
+ glColor4fv (color);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+
+ if (do_tex && !wire)
+ glDisable (GL_TEXTURE_2D);
+
+ glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
+ glNormal3f (0, 0, 1);
+ glVertex3f (-margin, -margin2, 0);
+ glVertex3f (-margin, h+margin2, 0);
+ glVertex3f (w+margin, h+margin2, 0);
+ glVertex3f (w+margin, -margin2, 0);
+ glEnd();
+ polys++;
+
+ if (do_tex && !wire)
+ {
+ color[3] = 1;
+ glColor4fv (color);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+ glEnable (GL_TEXTURE_2D);
+ glTranslatef (-metrics.lbearing, metrics.descent, 0);
+ print_texture_string (font, bp->billboard_text);
+ polys++;
+ }
+
+ glPopMatrix();
+ return polys;
+}
+
+
+static void
+init_text (ModeInfo *mi)
+{
+ gibson_configuration *bp = &ccs[MI_SCREEN(mi)];
+ int lines = 20;
+ int i;
+ char *s;
+
+ const char *const ss[] = {
+ "\n"
+ "ACCESS TO THIS COMPUTER AND\n"
+ "ITS DATA IS RESTRICTED TO\n"
+ "AUTHORIZED PERSONNEL ONLY\n"
+ "\n",
+ "\n"
+ " PASSWORD ACCEPTED\n"
+ " GOD\n"
+ "\n",
+ "PERSONNEL >>>\n",
+ "SEA ROUTINGS >>>\n",
+ "GARBAGE >>>\n",
+ "COMP. SERVICING >>>\n",
+ "COMPANY BUDGETS >>>\n",
+ "SCIENTIFIC BUDGETS >>>\n",
+ "COMPANY POLICIES >>>\n",
+ "ANNUAL RETURNS >>>\n",
+ "MINE RESEARCH >>>\n",
+ "CENTRAL LIBRARY >>>\n",
+ "QUANTATIVE SPEC. >>>\n",
+ "PAYMENT LEVELS >>>\n",
+ "CENTRAL SERVER >>>\n",
+ "GARBAGE >>>\n",
+ "KNMTS. DVPNT. >>>\n",
+ "LICENSING >>>\n",
+ "RELATIONS >>>\n",
+ "TIME SHEET RECS. >>>\n",
+ "RD. PRT. ROUTINGS >>>\n",
+ "RECRUITMENT >>>\n",
+ "TNKR. EXPENDITURE >>>\n",
+ "MINE DEVELOPMENT >>>\n",
+ "GARBAGE >>>\n",
+ "ANNUAL BUDGETS >>>\n",
+ "OIL LOCATIONS >>>\n",
+ "TIME SHEET RECS. >>>\n",
+ "RD. PRT. ROUTINGS >>>\n",
+ "KINEMATICS >>>\n",
+ "TPS. REPORTS >>>\n",
+ "BLAST FRNC. STATUS >>>\n",
+ "ACCOUNTANTS >>>\n",
+ "SHIPPING FORCASTS >>>\n",
+ "INDST. REPORTS >>>\n",
+ "EXPLOR. DVLT. >>>\n",
+ "WRHSE. EXPEND. >>>\n",
+ "GARBAGE >>>\n",
+ "RELOCATIONS >>>\n",
+ "AIRFREIGHT STATUS >>>\n",
+ "TPGC. EXPEND. >>>\n",
+ "SEA-BOARD LAWS >>>\n",
+ "COMPOSITE PLANTS >>>\n",
+ "NUCLEAR RESEARCH >>>\n",
+ "BALLAST REPORTS >>>\n",
+ "\n"
+ "CONFIDENTAL\n"
+ "FILES\n"
+ "DO NOT DELETE\n"
+ "BEFORE FINAL\n"
+ "BACKUP IS COMPLETED\n"
+ "\n",
+ "\n"
+ "FILE 1\n"
+ "WAITING FOR BACK-UP\n"
+ "\n"
+ "FILE 2\n"
+ "WAITING FOR BACK-UP\n"
+ "\n"
+ "FILE 3\n"
+ "WAITING FOR BACK-UP\n"
+ "\n"
+ "FILE 4\n"
+ "WAITING FOR BACK-UP\n"
+ "\n"
+ };
+
+
+ bp->text[1].text = s = calloc (countof(ss) * 2 * 40, 1);
+ for (i = 0; i < countof(ss); i++)
+ {
+ int n = random() % countof(ss);
+ strcat (s, ss[n]);
+ s += strlen(s);
+ }
+
+ bp->text[0].text = s = calloc (lines * 40, 1);
+ for (i = 0; i < lines; i++)
+ {
+ switch (random() % 11) {
+ case 0: sprintf (s, "%X\n", random() % 0xFFFFFFFF); break;
+ case 1: sprintf (s, "%X\n", random() % 0xFFFFFF); break;
+ case 2: sprintf (s, "%X\n", random() % 0xFFFF); break;
+ case 3: sprintf (s, "%d\n", random() % 0xFFFFFF); break;
+ case 4: sprintf (s, "%d\n", random() % 0xFFFF); break;
+ case 5: sprintf (s, "%d\n", random() % 0xFFF); break;
+ case 6: strcat (s, "00000000\n"); break;
+ case 7: sprintf (s, "{{{{{{{{\n"); break;
+ case 8: sprintf (s, "[][][][][][]\n"); break;
+ case 9: sprintf (s, "DEFAULT\n"); break;
+ case 10: sprintf (s, "\n"); break;
+ }
+ s += strlen(s);
+ }
+}
+
+
+static void
+init_textures (ModeInfo *mi)
+{
+ gibson_configuration *bp = &ccs[MI_SCREEN(mi)];
+ int i;
+ for (i = 0; i < countof(bp->text); i++)
+ {
+ glGenTextures (1, &bp->text[i].texid);
+ glBindTexture (GL_TEXTURE_2D, bp->text[i].texid);
+ texture_string_metrics (bp->text[i].font_data, " ",
+ &bp->text[i].metrics,
+ &bp->text[i].ascent,
+ &bp->text[i].descent);
+ bp->text[i].em_width = bp->text[i].metrics.width;
+ string_to_texture (bp->text[i].font_data, bp->text[i].text,
+ &bp->text[i].metrics,
+ &bp->text[i].width,
+ &bp->text[i].height);
+ }
+ glBindTexture (GL_TEXTURE_2D, 0);
+}
+
+
+ENTRYPOINT void
+init_gibson (ModeInfo *mi)
+{
+ gibson_configuration *bp;
+
+ MI_INIT (mi, ccs);
+
+ bp = &ccs[MI_SCREEN(mi)];
+
+ if ((bp->glx_context = init_GL(mi)) != NULL) {
+ reshape_gibson (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+ }
+
+ parse_color (mi, "towerText", bp->tower_color);
+ parse_color (mi, "towerText2", bp->tower_color2);
+ parse_color (mi, "towerColor", bp->bg_color);
+ memcpy (bp->edge_color, bp->bg_color, sizeof(bp->tower_color));
+ bp->edge_color [3] = 0.7;
+ bp->bg_color [3] = 1.0;
+ bp->tower_color [3] = 1.0;
+ bp->tower_color2[3] = 1.0;
+
+ if (grid_spacing < 1) grid_spacing = 1;
+ if (grid_width < 1) grid_width = 1;
+ if (grid_height < 1) grid_height = 1;
+ if (grid_depth < 1) grid_depth = 1;
+ if (columns < 1) columns = 1;
+ bp->ntowers = grid_width * grid_depth;
+ bp->towers = (tower *) calloc (sizeof(tower), bp->ntowers);
+ bp->startup_p = True;
+
+ {
+ double wander_speed = 0.007 * speed;
+ double tilt_speed = 0.01 * speed;
+ bp->rot = make_rotator (0, 0, 0, 0, wander_speed, True);
+ bp->rot2 = make_rotator (0, 0, 0, 0, tilt_speed, True);
+ }
+
+ bp->text[0].font_data = load_texture_font (mi->dpy, "towerFont");
+ bp->text[1].font_data = load_texture_font (mi->dpy, "towerFont");
+ init_text (mi);
+ init_textures (mi);
+
+ bp->ground_dlist = glGenLists (1);
+ glNewList (bp->ground_dlist, GL_COMPILE);
+ bp->ground_polys = draw_ground (mi);
+ glEndList ();
+
+ bp->tower_dlist = glGenLists (1);
+ glNewList (bp->tower_dlist, GL_COMPILE);
+ bp->tower_polys = draw_tower (mi, &bp->towers[0], 0, -1);
+ glEndList ();
+
+ {
+ int x, y;
+ GLfloat ww = grid_width * (1 + grid_spacing) - grid_spacing;
+ GLfloat hh = grid_depth * (1 + grid_spacing) - grid_spacing;
+ for (y = 0; y < grid_depth; y++)
+ for (x = 0; x < grid_width; x++)
+ {
+ int i;
+ tower *t = &bp->towers[y * grid_width + x];
+ t->x = (x * ww / (grid_width - 1)) - ww/2;
+ t->y = (y * hh / grid_depth) + 6;
+ t->h = 0 - y / (GLfloat) grid_depth / 2;
+
+ for (i = 0; i < countof(t->fg_dlists); i++)
+ {
+ t->bg_dlists[i] = glGenLists (1);
+ glNewList (t->bg_dlists[i], GL_COMPILE);
+ t->bg_polys = draw_tower (mi, t, 1, i);
+ glEndList ();
+
+ t->fg_dlists[i] = glGenLists (1);
+ glNewList (t->fg_dlists[i], GL_COMPILE);
+ t->fg_polys += draw_tower (mi, t, 2, i);
+ glEndList ();
+ }
+ }
+ }
+
+ animate_towers (mi);
+}
+
+
+ENTRYPOINT void
+draw_gibson (ModeInfo *mi)
+{
+ gibson_configuration *bp = &ccs[MI_SCREEN(mi)];
+ Display *dpy = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+ int wire = MI_IS_WIREFRAME(mi);
+ GLfloat s;
+ int i;
+
+ static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
+ static const GLfloat bshiny = 128.0;
+ GLfloat bcolor[4] = { 0.7, 0.7, 1.0, 1.0 };
+
+ if (!bp->glx_context)
+ return;
+
+ mi->polygon_count = 0;
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
+
+ glShadeModel (GL_SMOOTH);
+ glEnable (GL_NORMALIZE);
+ glEnable (GL_CULL_FACE);
+ glDisable (GL_TEXTURE_2D);
+ glEnable (GL_DEPTH_TEST);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if (!wire)
+ {
+ GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
+ GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
+ GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
+ GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
+
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glLightfv(GL_LIGHT0, GL_POSITION, pos);
+ glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
+ glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
+ }
+
+ glPushMatrix ();
+
+ glRotatef(current_device_rotation(), 0, 0, 1);
+
+# ifdef DEBUG
+ s = 0.02;
+# else
+ s = 10;
+# endif
+
+ glScalef (s, s, s);
+
+ glTranslatef (0, -1, 0);
+
+# ifndef DEBUG
+ glRotatef (-82, 1, 0, 0);
+
+ {
+ double maxx = 40; /* up/down */
+ double maxy = 1.5; /* tilt */
+ double maxz = 100; /* left/right */
+
+ double x, y, z;
+ double minh = -(grid_height / 2.0);
+ double maxh = -(grid_height / 20.0);
+
+ get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
+ x -= 0.5;
+ z = minh + (z * (maxh - minh));
+ glTranslatef(x * grid_spacing * 0.005, 0, z);
+
+ get_position (bp->rot2, &x, &y, &z, !bp->button_down_p);
+
+ z += (bp->xscroll / 2.0);
+ x += (bp->yscroll / 2.0);
+
+ glRotatef (maxx/2 - x*maxx, 1, 0, 0);
+ glRotatef (maxy/2 - y*maxy, 0, 1, 0);
+ glRotatef (maxz/2 - z*maxz, 0, 0, 1);
+ }
+# endif /* DEBUG */
+
+ glPushMatrix();
+ glScalef (GROUND_QUAD_SIZE, GROUND_QUAD_SIZE, 1);
+
+ glTranslatef (0, bp->ground_y - 1.5, 0);
+ glCallList (bp->ground_dlist);
+ mi->polygon_count += bp->ground_polys;
+
+ glTranslatef (0, 1, 0);
+ glCallList (bp->ground_dlist);
+ mi->polygon_count += bp->ground_polys;
+ glPopMatrix();
+
+ glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
+ glMateriali (GL_FRONT, GL_SHININESS, bshiny);
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor);
+
+ glPushMatrix();
+
+ if (grid_width & 1) /* Stay between towers */
+ glTranslatef ((grid_spacing + 1) / 2.0, 0, 0);
+
+
+ if (!wire)
+ {
+ GLfloat fog_color[4] = { 0, 0, 0, 1 };
+ glFogfv (GL_FOG_COLOR, fog_color);
+ /* I so don't understand how to choose the fog parameters. */
+ glFogi (GL_FOG_MODE, GL_LINEAR);
+ glFogf (GL_FOG_START, 0);
+ glFogf (GL_FOG_END, 100);
+ glEnable (GL_FOG);
+ }
+
+ /* Clear the floor under the tower bases */
+
+ {
+ GLfloat color0[4] = { 0, 0, 0, 1 };
+ GLfloat z = 0.01;
+
+ if (do_tex && !wire) glDisable (GL_TEXTURE_2D);
+ glColor4fv (color0);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color0);
+ glDisable (GL_BLEND);
+ glDisable (GL_DEPTH_TEST);
+
+ for (i = 0; i < bp->ntowers; i++)
+ {
+ tower *t = &bp->towers[i];
+ glPushMatrix();
+ glTranslatef (t->x, t->y, 0);
+
+ glNormal3f (0, 0, 1);
+ glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* clipping quad */
+ glVertex3f (-0.5, -0.5, z);
+ glVertex3f ( 0.5, -0.5, z);
+ glVertex3f ( 0.5, 0.5, z);
+ glVertex3f (-0.5, 0.5, z);
+ glEnd();
+ mi->polygon_count++;
+ glPopMatrix();
+ }
+ }
+
+ glEnable (GL_DEPTH_TEST);
+
+ if (!wire)
+ {
+ if (do_tex)
+ {
+ glEnable (GL_TEXTURE_2D);
+ enable_texture_string_parameters();
+ }
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE);
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ glDisable (GL_CULL_FACE);
+ if (bp->startup_p)
+ glEnable (GL_DEPTH_TEST);
+ else
+ glDisable (GL_DEPTH_TEST);
+ }
+
+ /* Draw the towers */
+
+ for (i = 0; i < bp->ntowers; i++)
+ {
+ tower *t = &bp->towers[i];
+ glPushMatrix();
+ glTranslatef (t->x, t->y-1, -grid_height * ease_ratio (1 - t->h));
+
+ glCallList (bp->tower_dlist);
+ mi->polygon_count += bp->tower_polys;
+
+ if (wire || do_tex)
+ {
+ int j;
+ for (j = 0; j < countof(t->fg_dlists); j++)
+ {
+ if (! (t->face_mode & (1 << j)))
+ {
+ glCallList (t->bg_dlists[j]);
+ mi->polygon_count += t->bg_polys;
+ }
+ else
+ {
+ glCallList (t->fg_dlists[j]);
+ mi->polygon_count += t->fg_polys;
+ }
+ }
+ }
+ glPopMatrix();
+ }
+
+ glPopMatrix();
+
+ mi->polygon_count += draw_billboard (mi);
+ glPopMatrix();
+
+ if (!bp->button_down_p)
+ animate_towers (mi);
+
+ if (bp->startup_p && bp->towers[bp->ntowers-1].h >= 1)
+ bp->startup_p = False;
+
+ if (mi->fps_p) do_fps (mi);
+ glFinish();
+
+ glXSwapBuffers(dpy, window);
+}
+
+
+ENTRYPOINT void
+free_gibson (ModeInfo *mi)
+{
+ int i, j;
+ gibson_configuration *bp = &ccs[MI_SCREEN(mi)];
+ if (!bp->glx_context) return;
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
+
+ if (bp->towers) free (bp->towers);
+ if (bp->rot) free_rotator (bp->rot);
+ if (bp->rot2) free_rotator (bp->rot2);
+ if (glIsList(bp->ground_dlist)) glDeleteLists(bp->ground_dlist, 1);
+ if (glIsList(bp->tower_dlist)) glDeleteLists(bp->tower_dlist, 1);
+ for (i = 0; i < countof(bp->text); i++)
+ {
+ if (bp->text[i].font_data) free_texture_font (bp->text[i].font_data);
+ if (bp->text[i].text) free (bp->text[i].text);
+ }
+ for (i = 0; i < bp->ntowers; i++)
+ {
+ for (j = 0; j < countof(bp->towers[i].fg_dlists); j++)
+ {
+ if (glIsList(bp->towers[i].fg_dlists[j]))
+ glDeleteLists(bp->towers[i].fg_dlists[j], 1);
+ if (glIsList(bp->towers[i].bg_dlists[j]))
+ glDeleteLists(bp->towers[i].bg_dlists[j], 1);
+ }
+ }
+}
+
+
+XSCREENSAVER_MODULE ("Gibson", gibson)
+/* Greets to Crash Override, The Phantom Freak, and also Joey */
+
+#endif /* USE_GL */
diff --git a/hacks/glx/gibson.man b/hacks/glx/gibson.man
new file mode 100644
index 0000000..a57e018
--- /dev/null
+++ b/hacks/glx/gibson.man
@@ -0,0 +1,88 @@
+.TH XScreenSaver 1 "" "X Version 11"
+.SH NAME
+Hacking the Gibson - screen saver.
+.SH SYNOPSIS
+.B gibson
+[\-display \fIhost:display.screen\fP]
+[\-visual \fIvisual\fP]
+[\-window]
+[\-root]
+[\-delay \fInumber\fP]
+[\-speed \fInumber\fP]
+[\-grid-width \fInumber\fP]
+[\-grid-depth \fInumber\fP]
+[\-grid-height \fInumber\fP]
+[\-grid-spacing \fInumber\fP]
+[\-columns \fInumber\fP]
+[\-no-texture]
+[\-wireframe]
+[\-fps]
+.SH DESCRIPTION
+Hacking the Gibson, as per the 1995 classic film, \fIHACKERS\fP.
+
+"Hackers penetrate and ravage delicate public and privately owned
+computer systems, infecting them with viruses, and stealing materials
+for their own ends. These people, they are terrorists."
+.SH OPTIONS
+.TP 8
+.B \-visual \fIvisual\fP
+Specify which visual to use. Legal values are the name of a visual class,
+or the id number (decimal or hex) of a specific visual.
+.TP 8
+.B \-window
+Draw on a newly-created window. This is the default.
+.TP 8
+.B \-root
+Draw on the root window.
+.TP 8
+.B \-delay \fInumber\fP
+Per-frame delay, in microseconds. Default: 20000 (0.02 seconds).
+.TP 8
+.B \-speed \fInumber\fP
+Animation speed. 2.0 means twice as fast, 0.5 means half as fast.
+.TP 8
+.B \-grid-width \fInumber\fP
+Number of towers across. 1 - 20. Default: 10.
+.TP 8
+.B \-grid-depth \fInumber\fP
+Number of towers deep. 1 - 20. Default: 10.
+.TP 8
+.B \-grid-height \fInumber\fP
+Height of the towers. 1 - 20. Default: 7.
+.TP 8
+.B \-grid-spacing \fInumber\fP
+Space between towers. 1 - 5. Default: 2.0.
+.TP 8
+.B \-columns \fInumber\fP
+Columns of text on the towers. 1 - 20. Default: 6.
+.TP 8
+.B \-texture | \-no-texture
+Whether to draw text. Default true.
+.TP 8
+.B \-wireframe | \-no-wireframe
+Render in wireframe instead of solid.
+.TP 8
+.B \-fps | \-no-fps
+Whether to show a frames-per-second display at the bottom of the screen.
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get the default host and display number.
+.TP 8
+.B XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.SH SEE ALSO
+.BR X (1),
+.BR xscreensaver (1)
+.SH COPYRIGHT
+Copyright \(co 2020 by Jamie Zawinski. Permission to use, copy, modify,
+distribute, and sell this software and its documentation for any purpose is
+hereby granted without fee, provided that the above copyright notice appear
+in all copies and that both that copyright notice and this permission notice
+appear in supporting documentation. No representations are made about the
+suitability of this software for any purpose. It is provided "as is" without
+express or implied warranty.
+.SH AUTHOR
+Jamie Zawinski.
diff --git a/hacks/glx/glhanoi.c b/hacks/glx/glhanoi.c
index 46cdc68..11ac75f 100644
--- a/hacks/glx/glhanoi.c
+++ b/hacks/glx/glhanoi.c
@@ -1424,13 +1424,14 @@ static GLubyte *makeTexture(glhcfg *glhanoi, int x_size, int y_size, int z_size,
tex_col_t *), tex_col_t * colours)
{
int i, j, k;
- GLubyte *textureData;
+ GLuint *textureData;
GLuint *texturePtr;
double x, y, z;
double xi, yi, zi;
+ /* As we use GL_RGBA format, we must assign 4 bytes per element */
if((textureData =
- calloc(x_size * y_size * z_size, sizeof(GLubyte))) == NULL) {
+ calloc(x_size * y_size * z_size, sizeof(*texturePtr))) == NULL) {
return NULL;
}
@@ -1439,7 +1440,7 @@ static GLubyte *makeTexture(glhcfg *glhanoi, int x_size, int y_size, int z_size,
zi = 1.0 / z_size;
z = 0.0;
- texturePtr = (void *)textureData;
+ texturePtr = textureData;
for(k = 0; k < z_size; k++, z += zi) {
y = 0.0;
for(j = 0; j < y_size; j++, y += yi) {
@@ -1450,7 +1451,7 @@ static GLubyte *makeTexture(glhcfg *glhanoi, int x_size, int y_size, int z_size,
}
}
}
- return textureData;
+ return (GLubyte *)textureData;
}
static void freeTexCols(tex_col_t*p)
diff --git a/hacks/glx/glmatrix.c b/hacks/glx/glmatrix.c
index 1e1c4f6..a638583 100644
--- a/hacks/glx/glmatrix.c
+++ b/hacks/glx/glmatrix.c
@@ -251,7 +251,7 @@ reset_strip (ModeInfo *mi, strip *s)
if (do_clock &&
!time_displayed_p &&
(i < GRID_SIZE-5) && /* display approx. once per 5 strips */
- !(random() % (GRID_SIZE-5)*5))
+ !(random() % ((GRID_SIZE-5)*5)))
{
int j;
char text[80];
diff --git a/hacks/glx/gravitywell.c b/hacks/glx/gravitywell.c
index 7078764..90edcf0 100644
--- a/hacks/glx/gravitywell.c
+++ b/hacks/glx/gravitywell.c
@@ -430,6 +430,8 @@ draw_row (ModeInfo *mi, int w, int y, Bool swap)
polys += 1;
ci = EASE (bp->vtx[vp + 2] / MAX_MASS_COLOR) * bp->ncolors;
+ if (ci < 0) ci = 0;
+ if (ci >= bp->ncolors) ci = bp->ncolors - 1;
bp->col[cp] = bp->colors[ci].red / 65536.0;
bp->col[cp+1] = bp->colors[ci].green / 65536.0;
bp->col[cp+2] = bp->colors[ci].blue / 65536.0;
@@ -452,6 +454,8 @@ draw_row (ModeInfo *mi, int w, int y, Bool swap)
bp->vtx[vp + 2] = gridp[x * GRID_SEG + i];
ci = EASE (bp->vtx[vp + 2] / MAX_MASS_COLOR) * bp->ncolors;
+ if (ci < 0) ci = 0;
+ if (ci >= bp->ncolors) ci = bp->ncolors - 1;
bp->col[cp] = bp->colors[ci].red / 65536.0;
bp->col[cp+1] = bp->colors[ci].green / 65536.0;
bp->col[cp+2] = bp->colors[ci].blue / 65536.0;
@@ -689,6 +693,8 @@ draw_gw (ModeInfo *mi)
GLfloat th, color[4];
int ci;
ci = EASE (s->depth / MAX_MASS_COLOR) * bp->ncolors;
+ if (ci < 0) ci = 0;
+ if (ci >= bp->ncolors) ci = bp->ncolors - 1;
color[0] = bp->colors[ci].red / 65536.0;
color[1] = bp->colors[ci].green / 65536.0;
color[2] = bp->colors[ci].blue / 65536.0;
diff --git a/hacks/glx/hypertorus.c b/hacks/glx/hypertorus.c
index 8aa8d4d..41f4e0a 100644
--- a/hacks/glx/hypertorus.c
+++ b/hacks/glx/hypertorus.c
@@ -1,10 +1,10 @@
/* hypertorus --- Shows a hypertorus that rotates in 4d */
#if 0
-static const char sccsid[] = "@(#)hypertorus.c 1.2 05/09/28 xlockmore";
+static const char sccsid[] = "@(#)hypertorus.c 1.2 28/09/05 xlockmore";
#endif
-/* Copyright (c) 2003-2009 Carsten Steger <carsten@mirsanmir.org>. */
+/* Copyright (c) 2003-2020 Carsten Steger <carsten@mirsanmir.org>. */
/*
* Permission to use, copy, modify, and distribute this software and its
@@ -25,30 +25,44 @@ static const char sccsid[] = "@(#)hypertorus.c 1.2 05/09/28 xlockmore";
* and trackball support
* C. Steger - 07/01/23: Improved 4d trackball support
* C. Steger - 09/08/22: Removed check-config.pl warnings
+ * C. Steger - 11/01/20: Added the changing colors mode.
*/
/*
- * This program shows the Clifford torus as it rotates in 4d. The Clifford
- * torus is a torus lies on the "surface" of the hypersphere in 4d. The
- * program projects the 4d torus to 3d using either a perspective or an
- * orthographic projection. Of the two alternatives, the perspecitve
- * projection looks much more appealing. In orthographic projections the
- * torus degenerates into a doubly covered cylinder for some angles. The
- * projected 3d torus can then be projected to the screen either perspectively
- * or orthographically. There are three display modes for the torus: mesh
- * (wireframe), solid, or transparent. Furthermore, the appearance of the
- * torus can be as a solid object or as a set of see-through bands or
- * see-through spirals. Finally, the colors with with the torus is drawn can
- * be set to either two-sided or to colorwheel. In the first case, the torus
- * is drawn with red on the outside and green on the inside. This mode
- * enables you to see that the torus turns inside-out as it rotates in 4d.
- * The second mode draws the torus in a fully saturated color wheel. This
- * gives a very nice effect when combined with the see-through bands or
- * see-through spirals mode. The rotation speed for each of the six planes
- * around which the torus rotates can be chosen. This program is very much
- * inspired by Thomas Banchoff's book "Beyond the Third Dimension: Geometry,
- * Computer Graphics, and Higher Dimensions", Scientific American Library,
- * 1990.
+ * This program shows the Clifford torus as it rotates in 4d. The
+ * Clifford torus is a torus lies on the "surface" of the hypersphere
+ * in 4d. The program projects the 4d torus to 3d using either a
+ * perspective or an orthographic projection. Of the two
+ * alternatives, the perspective projection looks much more appealing.
+ * In orthographic projections the torus degenerates into a doubly
+ * covered cylinder for some angles. The projected 3d torus can then
+ * be projected to the screen either perspectively or
+ * orthographically.
+ *
+ * There are three display modes for the torus: mesh (wireframe),
+ * solid, or transparent. Furthermore, the appearance of the torus
+ * can be as a solid object or as a set of see-through bands or
+ * see-through spirals. Finally, the colors with with the torus is
+ * drawn can be set to one-sided, two-sided, or to a color wheel. The
+ * colors can be static or changing dynamically. In one-sided color
+ * mode, the torus is drawn with the same color on the inside and the
+ * outside. In two-sided color mode, the torus is drawn with red on
+ * the outside and green on the inside if static colors are used. If
+ * changing colors are used, dynamically varying complementary colors
+ * are used for the two sides. This mode enables you to see that the
+ * 3d projection of the torus turns inside-out as it rotates in 4d.
+ * The color wheel mode draws the torus with a fully saturated color
+ * wheel. If changing colors are used, the colors of the color wheel
+ * are varying dynamically. The color wheel mode gives a very nice
+ * effect when combined with the see-through bands or see-through
+ * spirals mode.
+ *
+ * Finally, the rotation speed for each of the six planes around which
+ * the torus rotates can be chosen.
+ *
+ * This program is inspired by Thomas Banchoff's book "Beyond the
+ * Third Dimension: Geometry, Computer Graphics, and Higher
+ * Dimensions", Scientific American Library, 1990.
*/
#ifndef M_PI
@@ -63,8 +77,9 @@ static const char sccsid[] = "@(#)hypertorus.c 1.2 05/09/28 xlockmore";
#define APPEARANCE_BANDS 1
#define APPEARANCE_SPIRALS 2
-#define COLORS_TWOSIDED 0
-#define COLORS_COLORWHEEL 1
+#define COLORS_ONESIDED 0
+#define COLORS_TWOSIDED 1
+#define COLORS_COLORWHEEL 2
#define DISP_3D_PERSPECTIVE 0
#define DISP_3D_ORTHOGRAPHIC 1
@@ -75,6 +90,7 @@ static const char sccsid[] = "@(#)hypertorus.c 1.2 05/09/28 xlockmore";
#define DEF_DISPLAY_MODE "surface"
#define DEF_APPEARANCE "bands"
#define DEF_COLORS "colorwheel"
+#define DEF_CHANGE_COLORS "False"
#define DEF_PROJECTION_3D "perspective"
#define DEF_PROJECTION_4D "perspective"
#define DEF_SPEEDWX "1.1"
@@ -117,6 +133,7 @@ static int appearance;
static int num_spirals;
static char *color_mode;
static int colors;
+static Bool change_colors;
static char *proj_3d;
static int projection_3d;
static char *proj_4d;
@@ -146,8 +163,11 @@ static XrmOptionDescRec opts[] =
{"-spirals-4", ".appearance", XrmoptionNoArg, "spirals-4" },
{"-spirals-8", ".appearance", XrmoptionNoArg, "spirals-8" },
{"-spirals-16", ".appearance", XrmoptionNoArg, "spirals-16" },
+ {"-onesided", ".colors", XrmoptionNoArg, "onesided" },
{"-twosided", ".colors", XrmoptionNoArg, "twosided" },
{"-colorwheel", ".colors", XrmoptionNoArg, "colorwheel" },
+ {"-change-colors", ".changeColors", XrmoptionNoArg, "on"},
+ {"+change-colors", ".changeColors", XrmoptionNoArg, "off"},
{"-perspective-3d", ".projection3d", XrmoptionNoArg, "perspective" },
{"-orthographic-3d", ".projection3d", XrmoptionNoArg, "orthographic" },
{"-perspective-4d", ".projection4d", XrmoptionNoArg, "perspective" },
@@ -162,43 +182,28 @@ static XrmOptionDescRec opts[] =
static argtype vars[] =
{
- { &mode, "displayMode", "DisplayMode", DEF_DISPLAY_MODE, t_String },
- { &appear, "appearance", "Appearance", DEF_APPEARANCE, t_String },
- { &color_mode, "colors", "Colors", DEF_COLORS, t_String },
- { &proj_3d, "projection3d", "Projection3d", DEF_PROJECTION_3D, t_String },
- { &proj_4d, "projection4d", "Projection4d", DEF_PROJECTION_4D, t_String },
- { &speed_wx, "speedwx", "Speedwx", DEF_SPEEDWX, t_Float},
- { &speed_wy, "speedwy", "Speedwy", DEF_SPEEDWY, t_Float},
- { &speed_wz, "speedwz", "Speedwz", DEF_SPEEDWZ, t_Float},
- { &speed_xy, "speedxy", "Speedxy", DEF_SPEEDXY, t_Float},
- { &speed_xz, "speedxz", "Speedxz", DEF_SPEEDXZ, t_Float},
- { &speed_yz, "speedyz", "Speedyz", DEF_SPEEDYZ, t_Float}
-};
-
-static OptionStruct desc[] =
-{
- { "-wireframe", "display the torus as a wireframe mesh" },
- { "-surface", "display the torus as a solid surface" },
- { "-transparent", "display the torus as a transparent surface" },
- { "-solid", "display the torus as a solid object" },
- { "-bands", "display the torus as see-through bands" },
- { "-spirals-{1,2,4,8,16}", "display the torus as see-through spirals" },
- { "-twosided", "display the torus with two colors" },
- { "-colorwheel", "display the torus with a smooth color wheel" },
- { "-perspective-3d", "project the torus perspectively from 3d to 2d" },
- { "-orthographic-3d", "project the torus orthographically from 3d to 2d" },
- { "-perspective-4d", "project the torus perspectively from 4d to 3d" },
- { "-orthographic-4d", "project the torus orthographically from 4d to 3d" },
- { "-speed-wx <arg>", "rotation speed around the wx plane" },
- { "-speed-wy <arg>", "rotation speed around the wy plane" },
- { "-speed-wz <arg>", "rotation speed around the wz plane" },
- { "-speed-xy <arg>", "rotation speed around the xy plane" },
- { "-speed-xz <arg>", "rotation speed around the xz plane" },
- { "-speed-yz <arg>", "rotation speed around the yz plane" }
+ { &mode, "displayMode", "DisplayMode", DEF_DISPLAY_MODE, t_String },
+ { &appear, "appearance", "Appearance", DEF_APPEARANCE, t_String },
+ { &color_mode, "colors", "Colors", DEF_COLORS, t_String },
+ { &change_colors, "changeColors", "ChangeColors", DEF_CHANGE_COLORS, t_Bool },
+ { &proj_3d, "projection3d", "Projection3d", DEF_PROJECTION_3D, t_String },
+ { &proj_4d, "projection4d", "Projection4d", DEF_PROJECTION_4D, t_String },
+ { &speed_wx, "speedwx", "Speedwx", DEF_SPEEDWX, t_Float},
+ { &speed_wy, "speedwy", "Speedwy", DEF_SPEEDWY, t_Float},
+ { &speed_wz, "speedwz", "Speedwz", DEF_SPEEDWZ, t_Float},
+ { &speed_xy, "speedxy", "Speedxy", DEF_SPEEDXY, t_Float},
+ { &speed_xz, "speedxz", "Speedxz", DEF_SPEEDXZ, t_Float},
+ { &speed_yz, "speedyz", "Speedyz", DEF_SPEEDYZ, t_Float}
};
ENTRYPOINT ModeSpecOpt hypertorus_opts =
-{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
+{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, NULL};
+
+
+/* Color change speeds */
+#define DRHO 0.7
+#define DSIGMA 1.1
+#define DTAU 1.7
typedef struct {
@@ -206,15 +211,15 @@ typedef struct {
GLXContext *glx_context;
/* 4D rotation angles */
float alpha, beta, delta, zeta, eta, theta;
+ /* Color rotation angles */
+ float rho, sigma, tau;
/* Aspect ratio of the current window */
float aspect;
/* Trackball states */
trackball_state *trackballs[2];
int current_trackball;
Bool button_pressed;
-
float speed_scale;
-
} hypertorusstruct;
static hypertorusstruct *hyper = (hypertorusstruct *) NULL;
@@ -352,6 +357,77 @@ static void rotateall(float al, float be, float de, float ze, float et,
}
+/* Add a rotation around the x-axis to the matrix m. */
+static void rotatex(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI/180.0;
+ c = cos(phi);
+ s = sin(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][1];
+ v = m[i][2];
+ m[i][1] = c*u+s*v;
+ m[i][2] = -s*u+c*v;
+ }
+}
+
+
+/* Add a rotation around the y-axis to the matrix m. */
+static void rotatey(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI/180.0;
+ c = cos(phi);
+ s = sin(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][0];
+ v = m[i][2];
+ m[i][0] = c*u-s*v;
+ m[i][2] = s*u+c*v;
+ }
+}
+
+
+/* Add a rotation around the z-axis to the matrix m. */
+static void rotatez(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI/180.0;
+ c = cos(phi);
+ s = sin(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][0];
+ v = m[i][1];
+ m[i][0] = c*u+s*v;
+ m[i][1] = -s*u+c*v;
+ }
+}
+
+
+/* Compute the 3d rotation matrix m from the 3d rotation angles. */
+static void rotateall3d(float al, float be, float de, float m[3][3])
+{
+ int i, j;
+
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ m[i][j] = (i==j);
+ rotatex(m,al);
+ rotatey(m,be);
+ rotatez(m,de);
+}
+
+
/* Multiply two rotation matrices: o=m*n. */
static void mult_rotmat(float m[4][4], float n[4][4], float o[4][4])
{
@@ -400,62 +476,84 @@ static void quats_to_rotmat(float p[4], float q[4], float m[4][4])
/* Compute a fully saturated and bright color based on an angle. */
-static void color(double angle)
+static void color(double angle, float mat[3][3], float col[4])
{
int s;
- double t;
- float color[4];
+ double t, ca, sa;
+ float m;
- if (colors != COLORS_COLORWHEEL)
- return;
+ if (!change_colors)
+ {
+ if (colors == COLORS_ONESIDED || colors == COLORS_TWOSIDED)
+ return;
- if (angle >= 0.0)
- angle = fmod(angle,2*M_PI);
- else
- angle = fmod(angle,-2*M_PI);
- s = floor(angle/(M_PI/3));
- t = angle/(M_PI/3)-s;
- if (s >= 6)
- s = 0;
- switch (s)
+ if (angle >= 0.0)
+ angle = fmod(angle,2*M_PI);
+ else
+ angle = fmod(angle,-2*M_PI);
+ s = floor(angle/(M_PI/3));
+ t = angle/(M_PI/3)-s;
+ if (s >= 6)
+ s = 0;
+ switch (s)
+ {
+ case 0:
+ col[0] = 1.0;
+ col[1] = t;
+ col[2] = 0.0;
+ break;
+ case 1:
+ col[0] = 1.0-t;
+ col[1] = 1.0;
+ col[2] = 0.0;
+ break;
+ case 2:
+ col[0] = 0.0;
+ col[1] = 1.0;
+ col[2] = t;
+ break;
+ case 3:
+ col[0] = 0.0;
+ col[1] = 1.0-t;
+ col[2] = 1.0;
+ break;
+ case 4:
+ col[0] = t;
+ col[1] = 0.0;
+ col[2] = 1.0;
+ break;
+ case 5:
+ col[0] = 1.0;
+ col[1] = 0.0;
+ col[2] = 1.0-t;
+ break;
+ }
+ }
+ else /* change_colors */
{
- case 0:
- color[0] = 1.0;
- color[1] = t;
- color[2] = 0.0;
- break;
- case 1:
- color[0] = 1.0-t;
- color[1] = 1.0;
- color[2] = 0.0;
- break;
- case 2:
- color[0] = 0.0;
- color[1] = 1.0;
- color[2] = t;
- break;
- case 3:
- color[0] = 0.0;
- color[1] = 1.0-t;
- color[2] = 1.0;
- break;
- case 4:
- color[0] = t;
- color[1] = 0.0;
- color[2] = 1.0;
- break;
- case 5:
- color[0] = 1.0;
- color[1] = 0.0;
- color[2] = 1.0-t;
- break;
+ if (colors == COLORS_ONESIDED || colors == COLORS_TWOSIDED)
+ {
+ col[0] = mat[0][2];
+ col[1] = mat[1][2];
+ col[2] = mat[2][2];
+ }
+ else
+ {
+ ca = cos(angle);
+ sa = sin(angle);
+ col[0] = ca*mat[0][0]+sa*mat[0][1];
+ col[1] = ca*mat[1][0]+sa*mat[1][1];
+ col[2] = ca*mat[2][0]+sa*mat[2][1];
+ }
+ m = 0.5f/fmaxf(fmaxf(fabsf(col[0]),fabsf(col[1])),fabsf(col[2]));
+ col[0] = m*col[0]+0.5f;
+ col[1] = m*col[1]+0.5f;
+ col[2] = m*col[2]+0.5f;
}
if (display_mode == DISP_TRANSPARENT)
- color[3] = 0.7;
+ col[3] = 0.7;
else
- color[3] = 1.0;
- glColor3fv(color);
- glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,color);
+ col[3] = 1.0;
}
@@ -464,14 +562,17 @@ static void color(double angle)
power of 2. Similarly, the banded appearance will only work correctly
if numu and numv are divisible by 4. */
static int hypertorus(ModeInfo *mi, double umin, double umax, double vmin,
- double vmax, int numu, int numv)
+ double vmax, int numu, int numv)
{
int polys = 0;
- static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
- static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
- static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
- static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
- float p[3], pu[3], pv[3], n[3], mat[4][4];
+ static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_oneside[] = { 0.9, 0.4, 0.3, 1.0 };
+ static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_oneside[] = { 0.9, 0.4, 0.3, 0.7 };
+ float mat_diff_dyn[4], mat_diff_dyn_compl[4];
+ float p[3], pu[3], pv[3], n[3], mat[4][4], matc[3][3], col[4];
int i, j, k, l, m, b, skew;
double u, v, ur, vr;
double cu, su, cv, sv;
@@ -480,6 +581,9 @@ static int hypertorus(ModeInfo *mi, double umin, double umax, double vmin,
float q1[4], q2[4], r1[4][4], r2[4][4];
hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
+ if (change_colors)
+ rotateall3d(hp->rho,hp->sigma,hp->tau,matc);
+
rotateall(hp->alpha,hp->beta,hp->delta,hp->zeta,hp->eta,hp->theta,r1);
gltrackball_get_quaternion(hp->trackballs[0],q1);
@@ -488,31 +592,56 @@ static int hypertorus(ModeInfo *mi, double umin, double umax, double vmin,
mult_rotmat(r2,r1,mat);
- if (colors != COLORS_COLORWHEEL)
+ if (!change_colors)
{
- glColor3fv(mat_diff_red);
- if (display_mode == DISP_TRANSPARENT)
+ if (colors == COLORS_ONESIDED)
{
- glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
- glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ glColor3fv(mat_diff_oneside);
+ if (display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_trans_oneside);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_oneside);
+ }
}
- else
+ else if (colors == COLORS_TWOSIDED)
{
- glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
- glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ glColor3fv(mat_diff_red);
+ if (display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ }
}
}
-
-#if 0 /* #### not working */
-# ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
+ else /* change_colors */
{
- GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi);
- int o = (int) current_device_rotation();
- if (o != 0 && o != 180 && o != -180)
- glScalef (1/h, 1/h, 1/h);
+ color(0.0,matc,mat_diff_dyn);
+ if (colors == COLORS_ONESIDED)
+ {
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ }
+ else if (colors == COLORS_TWOSIDED)
+ {
+ mat_diff_dyn_compl[0] = 1.0f-mat_diff_dyn[0];
+ mat_diff_dyn_compl[1] = 1.0f-mat_diff_dyn[1];
+ mat_diff_dyn_compl[2] = 1.0f-mat_diff_dyn[2];
+ mat_diff_dyn_compl[3] = mat_diff_dyn[3];
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn_compl);
+ }
}
-# endif
-#endif
skew = num_spirals;
ur = umax-umin;
@@ -538,11 +667,16 @@ static int hypertorus(ModeInfo *mi, double umin, double umax, double vmin,
{
u += 4.0*skew/numv*v;
b = ((i/4)&(skew-1))*(numu/(4*skew));
- color(ur*4*b/numu+umin);
+ color(ur*4*b/numu+umin,matc,col);
}
else
{
- color(u);
+ color(u,matc,col);
+ }
+ if (colors == COLORS_COLORWHEEL)
+ {
+ glColor3fv(col);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,col);
}
cu = cos(u);
su = sin(u);
@@ -631,6 +765,10 @@ static void init(ModeInfo *mi)
hp->eta = 0.0;
hp->theta = 0.0;
+ hp->rho = frand(360.0);
+ hp->sigma = frand(360.0);
+ hp->tau = frand(360.0);
+
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (projection_3d == DISP_3D_PERSPECTIVE)
@@ -718,6 +856,19 @@ static void display_hypertorus(ModeInfo *mi)
hp->theta += speed_yz * hp->speed_scale;
if (hp->theta >= 360.0)
hp->theta -= 360.0;
+
+ if (change_colors)
+ {
+ hp->rho += DRHO;
+ if (hp->rho >= 360.0)
+ hp->rho -= 360.0;
+ hp->sigma += DSIGMA;
+ if (hp->sigma >= 360.0)
+ hp->sigma -= 360.0;
+ hp->tau += DTAU;
+ if (hp->tau >= 360.0)
+ hp->tau -= 360.0;
+ }
}
glMatrixMode(GL_PROJECTION);
@@ -736,10 +887,6 @@ static void display_hypertorus(ModeInfo *mi)
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
- /* Let's keep a constant aspect ratio rather than stretching with the
- shape of the window */
- glScalef (hp->WindH / (GLfloat) hp->WindW, 1, 1);
-
mi->polygon_count = hypertorus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI,64,64);
}
@@ -747,19 +894,11 @@ static void display_hypertorus(ModeInfo *mi)
ENTRYPOINT void reshape_hypertorus(ModeInfo *mi, int width, int height)
{
hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
- double h = (GLfloat) height / (GLfloat) width;
- int y = 0;
-
- if (width > height * 5) { /* tiny window: show middle */
- height = width * 9/16;
- y = -height/2;
- h = height / (GLfloat) width;
- }
hp->WindW = (GLint)width;
hp->WindH = (GLint)height;
- glViewport(0,y,width,height);
- hp->aspect = h;
+ glViewport(0,0,width,height);
+ hp->aspect = (GLfloat)width/(GLfloat)height;
}
@@ -910,7 +1049,11 @@ ENTRYPOINT void init_hypertorus(ModeInfo *mi)
}
/* Set the color mode. */
- if (!strcasecmp(color_mode,"twosided"))
+ if (!strcasecmp(color_mode,"onesided"))
+ {
+ colors = COLORS_ONESIDED;
+ }
+ else if (!strcasecmp(color_mode,"twosided"))
{
colors = COLORS_TWOSIDED;
}
diff --git a/hacks/glx/hypertorus.man b/hacks/glx/hypertorus.man
index 8c7075f..8f0837e 100644
--- a/hacks/glx/hypertorus.man
+++ b/hacks/glx/hypertorus.man
@@ -16,8 +16,10 @@ hypertorus - Draws a hypertorus that rotates in 4d
[\-solid]
[\-bands]
[\-spirals-{1,2,4,8,16}]
+[\-onesided]
[\-twosided]
[\-colorwheel]
+[\-change-colors]
[\-perspective-3d]
[\-orthographic-3d]
[\-perspective-4d]
@@ -37,18 +39,28 @@ alternatives, the perspective projection looks much more appealing.
In orthographic projections the torus degenerates into a doubly
covered cylinder for some angles. The projected 3d torus can then be
projected to the screen either perspectively or orthographically.
+.PP
There are three display modes for the torus: mesh (wireframe), solid,
or transparent. Furthermore, the appearance of the torus can be as a
solid object or as a set of see-through bands or see-through spirals.
-Finally, the colors with with the torus is drawn can be set to either
-two-sided or to a color wheel. In the first case, the torus is drawn
-with red on the outside and green on the inside. This mode enables
-you to see that the torus turns inside-out as it rotates in 4d. The
-second mode draws the torus with a fully saturated color wheel. This
-gives a very nice effect when combined with the see-through bands or
-see-through spirals mode. The rotation speed for each of the six
-planes around which the torus rotates can be chosen. This program is
-very much inspired by Thomas Banchoff's book "Beyond the Third
+Finally, the colors with with the torus is drawn can be set to
+one-sided, two-sided, or to a color wheel. The colors can be static
+or changing dynamically. In one-sided color mode, the torus is drawn
+with the same color on the inside and the outside. In two-sided color
+mode, the torus is drawn with red on the outside and green on the
+inside if static colors are used. If changing colors are used,
+dynamically varying complementary colors are used for the two sides.
+This mode enables you to see that the 3d projection of the torus turns
+inside-out as it rotates in 4d. The color wheel mode draws the torus
+with a fully saturated color wheel. If changing colors are used, the
+colors of the color wheel are varying dynamically. The color wheel
+mode gives a very nice effect when combined with the see-through bands
+or see-through spirals mode.
+.PP
+Finally, the rotation speed for each of the six planes around which
+the torus rotates can be chosen.
+.PP
+This program is inspired by Thomas Banchoff's book "Beyond the Third
Dimension: Geometry, Computer Graphics, and Higher Dimensions",
Scientific American Library, 1990.
.SH OPTIONS
@@ -97,18 +109,31 @@ Display the torus as see-through bands (default).
Display the torus as see-through spirals with the indicated number of
spirals.
.PP
-The following two options are mutually exclusive. They determine how
-to color the torus.
+The following three options are mutually exclusive. They determine
+how to color the torus.
+.TP 8
+.B \-onesided
+Display the torus with a single color.
.TP 8
.B \-twosided
-Display the torus with two colors: red on the outside and green on
-the inside.
+Display the torus with two colors: one color on the outside and the
+complementary on the inside. For static colors, the colors are red
+and green.
.TP 8
.B \-colorwheel
Display the torus with a fully saturated color wheel (default). If
-the torus is displayed as see-through bands each band will be
+the torus is displayed as see-through bands, each band will be
displayed with a different color. Likewise, if the torus is displayed
-as see-through spirals each spiral will receive a different color.
+as see-through spirals, each spiral will receive a different color.
+.PP
+The following options determine whether the colors with which the
+torus is displayed are static or are changing dynamically.
+.TP 8
+.B \-change-colors
+Change the colors with which the torus is displayed dynamically.
+.TP 8
+.B \-no-change-colors
+Use static colors to display the torus (default).
.PP
The following two options are mutually exclusive. They determine how
the torus is projected from 3d to 2d (i.e., to the screen).
@@ -158,9 +183,9 @@ Display the current frame rate, CPU load, and polygon count.
.SH INTERACTION
If you run this program in standalone mode you can rotate the
hypertorus by dragging the mouse while pressing the left mouse button.
-This rotates the hypertorus in 3D, i.e., around the wx, wy, and wz
+This rotates the hypertorus in 3d, i.e., around the wx, wy, and wz
planes. If you press the shift key while dragging the mouse with the
-left button pressed the hypertorus is rotated in 4D, i.e., around the
+left button pressed the hypertorus is rotated in 4d, i.e., around the
xy, xz, and yz planes. To examine the hypertorus at your leisure, it
is best to set all speeds to 0. Otherwise, the hypertorus will rotate
while the left mouse button is not pressed.
@@ -177,12 +202,13 @@ stored in the RESOURCE_MANAGER property.
.BR X (1),
.BR xscreensaver (1)
.SH COPYRIGHT
-Copyright \(co 2003-2005 by Carsten Steger. Permission to use, copy, modify,
-distribute, and sell this software and its documentation for any purpose is
-hereby granted without fee, provided that the above copyright notice appear
-in all copies and that both that copyright notice and this permission notice
-appear in supporting documentation. No representations are made about the
-suitability of this software for any purpose. It is provided "as is" without
-express or implied warranty.
+Copyright \(co 2003-2020 by Carsten Steger. Permission to use, copy,
+modify, distribute, and sell this software and its documentation for
+any purpose is hereby granted without fee, provided that the above
+copyright notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting documentation.
+No representations are made about the suitability of this software for
+any purpose. It is provided "as is" without express or implied
+warranty.
.SH AUTHOR
-Carsten Steger <carsten@mirsanmir.org>, 28-sep-2005.
+Carsten Steger <carsten@mirsanmir.org>, 11-jan-2020.
diff --git a/hacks/glx/klein.c b/hacks/glx/klein.c
index 0fac5d2..7be5157 100644
--- a/hacks/glx/klein.c
+++ b/hacks/glx/klein.c
@@ -2,10 +2,10 @@
can walk */
#if 0
-static const char sccsid[] = "@(#)klein.c 1.1 08/10/04 xlockmore";
+static const char sccsid[] = "@(#)klein.c 1.1 04/10/08 xlockmore";
#endif
-/* Copyright (c) 2005-2014 Carsten Steger <carsten@mirsanmir.org>. */
+/* Copyright (c) 2005-2020 Carsten Steger <carsten@mirsanmir.org>. */
/*
* Permission to use, copy, modify, and distribute this software and its
@@ -21,61 +21,83 @@ static const char sccsid[] = "@(#)klein.c 1.1 08/10/04 xlockmore";
* other special, indirect and consequential damages.
*
* REVISION HISTORY:
- * C. Steger - 08/10/04: Initial version
- * C. Steger - 09/08/03: Changes to the parameter handling
- * C. Steger - 13/12/25: Added the squeezed torus Klein bottle
- * C. Steger - 14/10/03: Moved the curlicue texture to curlicue.h
+ * C. Steger - 04/10/08: Initial version
+ * C. Steger - 03/08/09: Changes to the parameter handling
+ * C. Steger - 25/12/13: Added the squeezed torus Klein bottle
+ * C. Steger - 03/10/14: Moved the curlicue texture to curlicue.h
+ * C. Steger - 11/01/20: Added the changing colors mode.
*/
/*
- * This program shows three different Klein bottles in 4d: the figure-8 Klein
- * bottle, the squeezed torus Klein bottle, or the Lawson Klein bottle. You
- * can walk on the Klein bottle, see it turn in 4d, or walk on it while it
- * turns in 4d. The figure-8 Klein bottle is well known in its 3d form. The
- * 4d form used in this program is an extension of the 3d form to 4d that
- * does not intersect itself in 4d (which can be seen in the depth colors
- * mode). The squeezed torus Klein bottle also does not intersect itself in
- * 4d (which can be seen in the depth colors mode). The Lawson Klein bottle,
- * on the other hand, does intersect itself in 4d. Its primary use is that
- * it has a nice appearance for walking and for turning in 3d. The Klein
- * bottle is a non-orientable surface. To make this apparent, the two-sided
- * color mode can be used. Alternatively, orientation markers (curling
- * arrows) can be drawn as a texture map on the surface of the Klein bottle.
- * While walking on the Klein bottle, you will notice that the orientation
- * of the curling arrows changes (which it must because the Klein bottle is
- * non-orientable). The program projects the 4d Klein bottle to 3d using
- * either a perspective or an orthographic projection. Which of the two
- * alternatives looks more appealing depends on the viewing mode and the
- * Klein bottle. For example, the Lawson Klein bottle looks nicest when
- * projected perspectively. The figure-8 Klein bottle, on the other
- * hand, looks nicer while walking when projected orthographically from 4d.
- * For the squeezed torus Klein bottle, both projection modes give equally
- * acceptable projections. The projected Klein bottle can then be projected
- * to the screen either perspectively or orthographically. When using the
- * walking modes, perspective projection to the screen should be used. There
- * are three display modes for the Klein bottle: mesh (wireframe), solid, or
- * transparent. Furthermore, the appearance of the Klein bottle can be as
- * a solid object or as a set of see-through bands. Finally, the colors
- * with with the Klein bottle is drawn can be set to two-sided, rainbow, or
- * depth. In the first case, the Klein bottle is drawn with red on one
- * "side" and green on the "other side". Of course, the Klein bottle only
- * has one side, so the color jumps from red to green along a curve on the
- * surface of the Klein bottle. This mode enables you to see that the Klein
- * bottle is non-orientable. The second mode draws the Klein bottle with
- * fully saturated rainbow colors. This gives a very nice effect when
- * combined with the see-through bands mode or with the orientation markers
- * drawn. The third mode draws the Klein bottle with colors that are chosen
- * according to the 4d "depth" of the points. This mode enables you to see
- * that the figure-8 and squeezed torus Klein bottles do not intersect
- * themselves in 4d, while the Lawson Klein bottle does intersect itself.
- * The rotation speed for each of the six planes around which the Klein
- * bottle rotates can be chosen. For the walk-and-turn more, only the
- * rotation speeds around the true 4d planes are used (the xy, xz, and yz
- * planes). Furthermore, in the walking modes the walking direction in the
- * 2d base square of the Klein bottle and the walking speed can be chosen.
- * This program is somewhat inspired by Thomas Banchoff's book "Beyond the
- * Third Dimension: Geometry, Computer Graphics, and Higher Dimensions",
- * Scientific American Library, 1990.
+ * This program shows three different Klein bottles in 4d: the
+ * figure-8 Klein bottle, the pinched torus Klein bottle, or the
+ * Lawson Klein bottle. You can walk on the Klein bottle, see it turn
+ * in 4d, or walk on it while it turns in 4d. The figure-8 Klein
+ * bottle is well known in its 3d form. The 4d form used in this
+ * program is an extension of the 3d form to 4d that does not
+ * intersect itself in 4d (which can be seen in the depth colors mode
+ * when using static colors). The pinched torus Klein bottle also
+ * does not intersect itself in 4d (which can be seen in the depth
+ * colors mode when using static colors). The Lawson Klein bottle, on
+ * the other hand, does intersect itself in 4d. Its primary use is
+ * that it has a nice appearance for walking and for turning in 3d.
+ *
+ * The Klein bottle is a non-orientable surface. To make this
+ * apparent, the two-sided color mode can be used. Alternatively,
+ * orientation markers (curling arrows) can be drawn as a texture map
+ * on the surface of the Klein bottle. While walking on the Klein
+ * bottle, you will notice that the orientation of the curling arrows
+ * changes (which it must because the Klein bottle is non-orientable).
+ *
+ * The program projects the 4d Klein bottle to 3d using either a
+ * perspective or an orthographic projection. Which of the two
+ * alternatives looks more appealing depends on the viewing mode and
+ * the Klein bottle. For example, the Lawson Klein bottle looks
+ * nicest when projected perspectively. The figure-8 Klein bottle, on
+ * the other hand, looks nicer while walking when projected
+ * orthographically from 4d. For the pinched torus Klein bottle, both
+ * projection modes give equally acceptable projections.
+ *
+ * The projected Klein bottle can then be projected to the screen
+ * either perspectively or orthographically. When using the walking
+ * modes, perspective projection to the screen should be used.
+ *
+ * There are three display modes for the Klein bottle: mesh
+ * (wireframe), solid, or transparent. Furthermore, the appearance of
+ * the Klein bottle can be as a solid object or as a set of
+ * see-through bands. Finally, the colors with with the Klein bottle
+ * is drawn can be set to one-sided, two-sided, rainbow, or depth. In
+ * one-sided mode, the Klein bottle is drawn with the same color on
+ * both "sides." In two-sided mode (using static colors), the Klein
+ * bottle is drawn with red on one "side" and green on the "other
+ * side." Of course, the Klein bottle only has one side, so the color
+ * jumps from red to green along a curve on the surface of the Klein
+ * bottle. This mode enables you to see that the Klein bottle is
+ * non-orientable. If changing colors are used in two-sided mode,
+ * changing complementary colors are used on the respective "sides."
+ * The rainbow color mode (using static colors) draws the Klein bottle
+ * with a color wheel of fully saturated rainbow colors. If changing
+ * colors are used, the color wheel's colors change dynamically. The
+ * rainbow color mode gives a very nice effect when combined with the
+ * see-through bands mode or with the orientation markers drawn. The
+ * depth color mode draws the Klein bottle with colors that are chosen
+ * according to the 4d "depth" of the points. If static colors are
+ * used, this mode enables you to see that the figure-8 and pinched
+ * torus Klein bottles do not intersect themselves in 4d, while the
+ * Lawson Klein bottle does intersect itself.
+ *
+ * The rotation speed for each of the six planes around which the
+ * Klein bottle rotates can be chosen. For the walk-and-turn mode,
+ * only the rotation speeds around the true 4d planes are used (the
+ * xy, xz, and yz planes).
+ *
+ * Furthermore, in the walking modes the walking direction in the 2d
+ * base square of the Klein bottle and the walking speed can be
+ * chosen.
+ *
+ * This program is somewhat inspired by Thomas Banchoff's book "Beyond
+ * the Third Dimension: Geometry, Computer Graphics, and Higher
+ * Dimensions", Scientific American Library, 1990.
*/
#include "curlicue.h"
@@ -84,54 +106,56 @@ static const char sccsid[] = "@(#)klein.c 1.1 08/10/04 xlockmore";
#define M_PI 3.14159265358979323846
#endif
-#define KLEIN_BOTTLE_FIGURE_8 0
-#define KLEIN_BOTTLE_SQUEEZED_TORUS 1
-#define KLEIN_BOTTLE_LAWSON 2
-#define NUM_KLEIN_BOTTLES 3
-
-#define DISP_WIREFRAME 0
-#define DISP_SURFACE 1
-#define DISP_TRANSPARENT 2
-#define NUM_DISPLAY_MODES 3
-
-#define APPEARANCE_SOLID 0
-#define APPEARANCE_BANDS 1
-#define NUM_APPEARANCES 2
-
-#define COLORS_TWOSIDED 0
-#define COLORS_RAINBOW 1
-#define COLORS_DEPTH 2
-#define NUM_COLORS 3
-
-#define VIEW_WALK 0
-#define VIEW_TURN 1
-#define VIEW_WALKTURN 2
-#define NUM_VIEW_MODES 3
-
-#define DISP_3D_PERSPECTIVE 0
-#define DISP_3D_ORTHOGRAPHIC 1
-#define NUM_DISP_3D_MODES 2
-
-#define DISP_4D_PERSPECTIVE 0
-#define DISP_4D_ORTHOGRAPHIC 1
-#define NUM_DISP_4D_MODES 2
-
-#define DEF_KLEIN_BOTTLE "random"
-#define DEF_DISPLAY_MODE "random"
-#define DEF_APPEARANCE "random"
-#define DEF_COLORS "random"
-#define DEF_VIEW_MODE "random"
-#define DEF_MARKS "False"
-#define DEF_PROJECTION_3D "random"
-#define DEF_PROJECTION_4D "random"
-#define DEF_SPEEDWX "1.1"
-#define DEF_SPEEDWY "1.3"
-#define DEF_SPEEDWZ "1.5"
-#define DEF_SPEEDXY "1.7"
-#define DEF_SPEEDXZ "1.9"
-#define DEF_SPEEDYZ "2.1"
-#define DEF_WALK_DIRECTION "7.0"
-#define DEF_WALK_SPEED "20.0"
+#define KLEIN_BOTTLE_FIGURE_8 0
+#define KLEIN_BOTTLE_PINCHED_TORUS 1
+#define KLEIN_BOTTLE_LAWSON 2
+#define NUM_KLEIN_BOTTLES 3
+
+#define DISP_WIREFRAME 0
+#define DISP_SURFACE 1
+#define DISP_TRANSPARENT 2
+#define NUM_DISPLAY_MODES 3
+
+#define APPEARANCE_SOLID 0
+#define APPEARANCE_BANDS 1
+#define NUM_APPEARANCES 2
+
+#define COLORS_ONESIDED 0
+#define COLORS_TWOSIDED 1
+#define COLORS_RAINBOW 2
+#define COLORS_DEPTH 3
+#define NUM_COLORS 4
+
+#define VIEW_WALK 0
+#define VIEW_TURN 1
+#define VIEW_WALKTURN 2
+#define NUM_VIEW_MODES 3
+
+#define DISP_3D_PERSPECTIVE 0
+#define DISP_3D_ORTHOGRAPHIC 1
+#define NUM_DISP_3D_MODES 2
+
+#define DISP_4D_PERSPECTIVE 0
+#define DISP_4D_ORTHOGRAPHIC 1
+#define NUM_DISP_4D_MODES 2
+
+#define DEF_KLEIN_BOTTLE "random"
+#define DEF_DISPLAY_MODE "random"
+#define DEF_APPEARANCE "random"
+#define DEF_COLORS "random"
+#define DEF_VIEW_MODE "random"
+#define DEF_MARKS "False"
+#define DEF_CHANGE_COLORS "False"
+#define DEF_PROJECTION_3D "random"
+#define DEF_PROJECTION_4D "random"
+#define DEF_SPEEDWX "1.1"
+#define DEF_SPEEDWY "1.3"
+#define DEF_SPEEDWZ "1.5"
+#define DEF_SPEEDXY "1.7"
+#define DEF_SPEEDXZ "1.9"
+#define DEF_SPEEDYZ "2.1"
+#define DEF_WALK_DIRECTION "7.0"
+#define DEF_WALK_SPEED "20.0"
#ifdef STANDALONE
# define DEFAULTS "*delay: 10000 \n" \
@@ -168,6 +192,7 @@ static char *appear;
static char *color_mode;
static char *view_mode;
static Bool marks;
+static Bool change_colors;
static char *proj_3d;
static char *proj_4d;
static float speed_wx;
@@ -184,7 +209,7 @@ static XrmOptionDescRec opts[] =
{
{"-klein-bottle", ".kleinBottle", XrmoptionSepArg, 0 },
{"-figure-8", ".kleinBottle", XrmoptionNoArg, "figure-8" },
- {"-squeezed-torus", ".kleinBottle", XrmoptionNoArg, "squeezed-torus" },
+ {"-pinched-torus", ".kleinBottle", XrmoptionNoArg, "pinched-torus" },
{"-lawson", ".kleinBottle", XrmoptionNoArg, "lawson" },
{"-mode", ".displayMode", XrmoptionSepArg, 0 },
{"-wireframe", ".displayMode", XrmoptionNoArg, "wireframe" },
@@ -194,15 +219,18 @@ static XrmOptionDescRec opts[] =
{"-solid", ".appearance", XrmoptionNoArg, "solid" },
{"-bands", ".appearance", XrmoptionNoArg, "bands" },
{"-colors", ".colors", XrmoptionSepArg, 0 },
+ {"-onesided", ".colors", XrmoptionNoArg, "one-sided" },
{"-twosided", ".colors", XrmoptionNoArg, "two-sided" },
{"-rainbow", ".colors", XrmoptionNoArg, "rainbow" },
{"-depth", ".colors", XrmoptionNoArg, "depth" },
+ {"-change-colors", ".changeColors", XrmoptionNoArg, "on"},
+ {"+change-colors", ".changeColors", XrmoptionNoArg, "off"},
{"-view-mode", ".viewMode", XrmoptionSepArg, 0 },
{"-walk", ".viewMode", XrmoptionNoArg, "walk" },
{"-turn", ".viewMode", XrmoptionNoArg, "turn" },
{"-walk-turn", ".viewMode", XrmoptionNoArg, "walk-turn" },
- {"-orientation-marks", ".marks", XrmoptionNoArg, "on"},
- {"+orientation-marks", ".marks", XrmoptionNoArg, "off"},
+ {"-orientation-marks", ".marks", XrmoptionNoArg, "on"},
+ {"+orientation-marks", ".marks", XrmoptionNoArg, "off"},
{"-projection-3d", ".projection3d", XrmoptionSepArg, 0 },
{"-perspective-3d", ".projection3d", XrmoptionNoArg, "perspective" },
{"-orthographic-3d", ".projection3d", XrmoptionNoArg, "orthographic" },
@@ -225,6 +253,7 @@ static argtype vars[] =
{ &mode, "displayMode", "DisplayMode", DEF_DISPLAY_MODE, t_String },
{ &appear, "appearance", "Appearance", DEF_APPEARANCE, t_String },
{ &color_mode, "colors", "Colors", DEF_COLORS, t_String },
+ { &change_colors, "changeColors", "ChangeColors", DEF_CHANGE_COLORS, t_Bool },
{ &view_mode, "viewMode", "ViewMode", DEF_VIEW_MODE, t_String },
{ &marks, "marks", "Marks", DEF_MARKS, t_Bool },
{ &proj_3d, "projection3d", "Projection3d", DEF_PROJECTION_3D, t_String },
@@ -246,12 +275,17 @@ ENTRYPOINT ModeSpecOpt klein_opts =
/* Radius of the figure-8 Klein bottle */
#define FIGURE_8_RADIUS 2.0
-/* Radius of the squeezed torus Klein bottle */
-#define SQUEEZED_TORUS_RADIUS 2.0
+/* Radius of the pinched torus Klein bottle */
+#define PINCHED_TORUS_RADIUS 2.0
/* Offset by which we walk above the Klein bottle */
#define DELTAY 0.02
+/* Color change speeds */
+#define DRHO 0.7
+#define DSIGMA 1.1
+#define DTAU 1.7
+
/* Number of subdivisions of the Klein bottle */
#define NUMU 128
#define NUMV 128
@@ -268,11 +302,14 @@ typedef struct {
int display_mode;
int appearance;
int colors;
+ Bool change_colors;
int view;
int projection_3d;
int projection_4d;
/* 4D rotation angles */
float alpha, beta, delta, zeta, eta, theta;
+ /* Color rotation angles */
+ float rho, sigma, tau;
/* Movement parameters */
float umove, vmove, dumove, dvmove;
int side;
@@ -451,6 +488,77 @@ static void rotateall4d(float ze, float et, float th, float m[4][4])
}
+/* Add a rotation around the x-axis to the matrix m. */
+static void rotatex(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI/180.0;
+ c = cos(phi);
+ s = sin(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][1];
+ v = m[i][2];
+ m[i][1] = c*u+s*v;
+ m[i][2] = -s*u+c*v;
+ }
+}
+
+
+/* Add a rotation around the y-axis to the matrix m. */
+static void rotatey(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI/180.0;
+ c = cos(phi);
+ s = sin(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][0];
+ v = m[i][2];
+ m[i][0] = c*u-s*v;
+ m[i][2] = s*u+c*v;
+ }
+}
+
+
+/* Add a rotation around the z-axis to the matrix m. */
+static void rotatez(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI/180.0;
+ c = cos(phi);
+ s = sin(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][0];
+ v = m[i][1];
+ m[i][0] = c*u+s*v;
+ m[i][1] = -s*u+c*v;
+ }
+}
+
+
+/* Compute the 3d rotation matrix m from the 3d rotation angles. */
+static void rotateall3d(float al, float be, float de, float m[3][3])
+{
+ int i, j;
+
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ m[i][j] = (i==j);
+ rotatex(m,al);
+ rotatey(m,be);
+ rotatez(m,de);
+}
+
+
/* Multiply two rotation matrices: o=m*n. */
static void mult_rotmat(float m[4][4], float n[4][4], float o[4][4])
{
@@ -499,54 +607,79 @@ static void quats_to_rotmat(float p[4], float q[4], float m[4][4])
/* Compute a fully saturated and bright color based on an angle. */
-static void color(kleinstruct *kb, double angle, float col[4])
+static void color(kleinstruct *kb, double angle, float mat[3][3], float col[4])
{
int s;
- double t;
+ double t, ca, sa;
+ float m;
- if (kb->colors == COLORS_TWOSIDED)
- return;
+ if (!kb->change_colors)
+ {
+ if (kb->colors == COLORS_ONESIDED || kb->colors == COLORS_TWOSIDED)
+ return;
- if (angle >= 0.0)
- angle = fmod(angle,2.0*M_PI);
- else
- angle = fmod(angle,-2.0*M_PI);
- s = floor(angle/(M_PI/3));
- t = angle/(M_PI/3)-s;
- if (s >= 6)
- s = 0;
- switch (s)
- {
- case 0:
- col[0] = 1.0;
- col[1] = t;
- col[2] = 0.0;
- break;
- case 1:
- col[0] = 1.0-t;
- col[1] = 1.0;
- col[2] = 0.0;
- break;
- case 2:
- col[0] = 0.0;
- col[1] = 1.0;
- col[2] = t;
- break;
- case 3:
- col[0] = 0.0;
- col[1] = 1.0-t;
- col[2] = 1.0;
- break;
- case 4:
- col[0] = t;
- col[1] = 0.0;
- col[2] = 1.0;
- break;
- case 5:
- col[0] = 1.0;
- col[1] = 0.0;
- col[2] = 1.0-t;
- break;
+ if (angle >= 0.0)
+ angle = fmod(angle,2.0*M_PI);
+ else
+ angle = fmod(angle,-2.0*M_PI);
+ s = floor(angle/(M_PI/3));
+ t = angle/(M_PI/3)-s;
+ if (s >= 6)
+ s = 0;
+ switch (s)
+ {
+ case 0:
+ col[0] = 1.0;
+ col[1] = t;
+ col[2] = 0.0;
+ break;
+ case 1:
+ col[0] = 1.0-t;
+ col[1] = 1.0;
+ col[2] = 0.0;
+ break;
+ case 2:
+ col[0] = 0.0;
+ col[1] = 1.0;
+ col[2] = t;
+ break;
+ case 3:
+ col[0] = 0.0;
+ col[1] = 1.0-t;
+ col[2] = 1.0;
+ break;
+ case 4:
+ col[0] = t;
+ col[1] = 0.0;
+ col[2] = 1.0;
+ break;
+ case 5:
+ col[0] = 1.0;
+ col[1] = 0.0;
+ col[2] = 1.0-t;
+ break;
+ }
+ }
+ else /* kb->change_colors */
+ {
+ if (kb->colors == COLORS_ONESIDED || kb->colors == COLORS_TWOSIDED)
+ {
+ col[0] = mat[0][2];
+ col[1] = mat[1][2];
+ col[2] = mat[2][2];
+ }
+ else
+ {
+ ca = cos(angle);
+ sa = sin(angle);
+ col[0] = ca*mat[0][0]+sa*mat[0][1];
+ col[1] = ca*mat[1][0]+sa*mat[1][1];
+ col[2] = ca*mat[2][0]+sa*mat[2][1];
+ }
+ m = 0.5f/fmaxf(fmaxf(fabsf(col[0]),fabsf(col[1])),fabsf(col[2]));
+ col[0] = m*col[0]+0.5f;
+ col[1] = m*col[1]+0.5f;
+ col[2] = m*col[2]+0.5f;
}
if (kb->display_mode == DISP_TRANSPARENT)
col[3] = 0.7;
@@ -573,10 +706,13 @@ static void setup_figure8(ModeInfo *mi, double umin, double umax, double vmin,
k = i*(NUMV+1)+j;
u = -ur*j/NUMU+umin;
v = vr*i/NUMV+vmin;
- if (kb->colors == COLORS_DEPTH)
- color(kb,(cos(u)+1.0)*M_PI*2.0/3.0,kb->col[k]);
- else
- color(kb,v,kb->col[k]);
+ if (!kb->change_colors)
+ {
+ if (kb->colors == COLORS_DEPTH)
+ color(kb,(cos(u)+1.0)*M_PI*2.0/3.0,NULL,kb->col[k]);
+ else if (kb->colors == COLORS_RAINBOW)
+ color(kb,v,NULL,kb->col[k]);
+ }
kb->tex[k][0] = -32*u/(2.0*M_PI);
kb->tex[k][1] = 32*v/(2.0*M_PI);
cu = cos(u);
@@ -612,9 +748,9 @@ static void setup_figure8(ModeInfo *mi, double umin, double umax, double vmin,
}
-/* Set up the squeezed torus Klein bottle coordinates, colors, and texture. */
-static void setup_squeezed_torus(ModeInfo *mi, double umin, double umax,
- double vmin, double vmax)
+/* Set up the pinched torus Klein bottle coordinates, colors, and texture. */
+static void setup_pinched_torus(ModeInfo *mi, double umin, double umax,
+ double vmin, double vmax)
{
int i, j, k, l;
double u, v, ur, vr;
@@ -630,10 +766,13 @@ static void setup_squeezed_torus(ModeInfo *mi, double umin, double umax,
k = i*(NUMV+1)+j;
u = -ur*j/NUMU+umin;
v = vr*i/NUMV+vmin;
- if (kb->colors == COLORS_DEPTH)
- color(kb,(sin(u)*sin(0.5*v)+1.0)*M_PI*2.0/3.0,kb->col[k]);
- else
- color(kb,v,kb->col[k]);
+ if (!kb->change_colors)
+ {
+ if (kb->colors == COLORS_DEPTH)
+ color(kb,(sin(u)*sin(0.5*v)+1.0)*M_PI*2.0/3.0,NULL,kb->col[k]);
+ else if (kb->colors == COLORS_RAINBOW)
+ color(kb,v,NULL,kb->col[k]);
+ }
kb->tex[k][0] = -32*u/(2.0*M_PI);
kb->tex[k][1] = 32*v/(2.0*M_PI);
cu = cos(u);
@@ -642,23 +781,23 @@ static void setup_squeezed_torus(ModeInfo *mi, double umin, double umax,
sv = sin(v);
cv2 = cos(0.5*v);
sv2 = sin(0.5*v);
- kb->x[k][0] = (SQUEEZED_TORUS_RADIUS+cu)*cv;
- kb->x[k][1] = (SQUEEZED_TORUS_RADIUS+cu)*sv;
+ kb->x[k][0] = (PINCHED_TORUS_RADIUS+cu)*cv;
+ kb->x[k][1] = (PINCHED_TORUS_RADIUS+cu)*sv;
kb->x[k][2] = su*cv2;
kb->x[k][3] = su*sv2;
kb->xu[k][0] = -su*cv;
kb->xu[k][1] = -su*sv;
kb->xu[k][2] = cu*cv2;
kb->xu[k][3] = cu*sv2;
- kb->xv[k][0] = -(SQUEEZED_TORUS_RADIUS+cu)*sv;
- kb->xv[k][1] = (SQUEEZED_TORUS_RADIUS+cu)*cv;
+ kb->xv[k][0] = -(PINCHED_TORUS_RADIUS+cu)*sv;
+ kb->xv[k][1] = (PINCHED_TORUS_RADIUS+cu)*cv;
kb->xv[k][2] = -0.5*su*sv2;
kb->xv[k][3] = 0.5*su*cv2;
for (l=0; l<4; l++)
{
- kb->x[k][l] /= SQUEEZED_TORUS_RADIUS+1.25;
- kb->xu[k][l] /= SQUEEZED_TORUS_RADIUS+1.25;
- kb->xv[k][l] /= SQUEEZED_TORUS_RADIUS+1.25;
+ kb->x[k][l] /= PINCHED_TORUS_RADIUS+1.25;
+ kb->xu[k][l] /= PINCHED_TORUS_RADIUS+1.25;
+ kb->xv[k][l] /= PINCHED_TORUS_RADIUS+1.25;
}
}
}
@@ -683,10 +822,13 @@ static void setup_lawson(ModeInfo *mi, double umin, double umax, double vmin,
k = i*(NUMU+1)+j;
u = -ur*j/NUMU+umin;
v = vr*i/NUMV+vmin;
- if (kb->colors == COLORS_DEPTH)
- color(kb,(sin(u)*cos(0.5*v)+1.0)*M_PI*2.0/3.0,kb->col[k]);
- else
- color(kb,v,kb->col[k]);
+ if (!kb->change_colors)
+ {
+ if (kb->colors == COLORS_DEPTH)
+ color(kb,(sin(u)*cos(0.5*v)+1.0)*M_PI*2.0/3.0,NULL,kb->col[k]);
+ else if (kb->colors == COLORS_RAINBOW)
+ color(kb,v,NULL,kb->col[k]);
+ }
kb->tex[k][0] = -32*u/(2.0*M_PI);
kb->tex[k][1] = 32*v/(2.0*M_PI);
cu = cos(u);
@@ -717,19 +859,25 @@ static int figure8(ModeInfo *mi, double umin, double umax, double vmin,
double vmax)
{
int polys = 0;
- static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
- static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
- static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
- static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
- float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[4][4];
+ static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_oneside[] = { 0.9, 0.4, 0.3, 1.0 };
+ static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_oneside[] = { 0.9, 0.4, 0.3, 0.7 };
+ float mat_diff_dyn[4], mat_diff_dyn_compl[4];
+ float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[4][4], matc[3][3];
int i, j, k, l, m, o;
- double u, v;
+ double u, v, ur, vr;
double xx[4], xxu[4], xxv[4], y[4], yu[4], yv[4];
double q, r, s, t;
double cu, su, cv, sv, cv2, sv2, c2u, s2u;
float q1[4], q2[4], r1[4][4], r2[4][4];
kleinstruct *kb = &klein[MI_SCREEN(mi)];
+ if (kb->change_colors)
+ rotateall3d(kb->rho,kb->sigma,kb->tau,matc);
+
if (kb->view == VIEW_WALK || kb->view == VIEW_WALKTURN)
{
/* Compute the rotation that rotates the Klein bottle in 4D without the
@@ -931,22 +1079,60 @@ static int figure8(ModeInfo *mi, double umin, double umax, double vmin,
}
}
- if (kb->colors == COLORS_TWOSIDED)
+ if (!kb->change_colors)
{
- glColor3fv(mat_diff_red);
- if (kb->display_mode == DISP_TRANSPARENT)
+ if (kb->colors == COLORS_ONESIDED)
{
- glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
- glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ glColor3fv(mat_diff_oneside);
+ if (kb->display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_trans_oneside);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_oneside);
+ }
}
- else
+ else if (kb->colors == COLORS_TWOSIDED)
{
- glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
- glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ glColor3fv(mat_diff_red);
+ if (kb->display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ }
+ }
+ }
+ else /* kb->change_colors */
+ {
+ color(kb,0.0,matc,mat_diff_dyn);
+ if (kb->colors == COLORS_ONESIDED)
+ {
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ }
+ else if (kb->colors == COLORS_TWOSIDED)
+ {
+ mat_diff_dyn_compl[0] = 1.0f-mat_diff_dyn[0];
+ mat_diff_dyn_compl[1] = 1.0f-mat_diff_dyn[1];
+ mat_diff_dyn_compl[2] = 1.0f-mat_diff_dyn[2];
+ mat_diff_dyn_compl[3] = mat_diff_dyn[3];
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn_compl);
}
}
glBindTexture(GL_TEXTURE_2D,kb->tex_name);
+ ur = umax-umin;
+ vr = vmax-vmin;
for (i=0; i<NUMU; i++)
{
if (kb->appearance == APPEARANCE_BANDS && ((i & (NUMB-1)) >= NUMB/2))
@@ -964,7 +1150,20 @@ static int figure8(ModeInfo *mi, double umin, double umax, double vmin,
o = l*(NUMV+1)+m;
glNormal3fv(kb->pn[o]);
glTexCoord2fv(kb->tex[o]);
- if (kb->colors != COLORS_TWOSIDED)
+ if (kb->change_colors)
+ {
+ if (kb->colors == COLORS_DEPTH)
+ {
+ u = -ur*m/NUMU+umin;
+ color(kb,(cos(u)+1.0)*M_PI*2.0/3.0,matc,kb->col[o]);
+ }
+ else if (kb->colors == COLORS_RAINBOW)
+ {
+ v = vr*l/NUMV+vmin;
+ color(kb,v,matc,kb->col[o]);
+ }
+ }
+ if (kb->colors != COLORS_ONESIDED && kb->colors != COLORS_TWOSIDED)
{
glColor3fv(kb->col[o]);
glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,kb->col[o]);
@@ -980,24 +1179,30 @@ static int figure8(ModeInfo *mi, double umin, double umax, double vmin,
}
-/* Draw a squeezed torus Klein bottle projected into 3D. */
-static int squeezed_torus(ModeInfo *mi, double umin, double umax, double vmin,
- double vmax)
+/* Draw a pinched torus Klein bottle projected into 3D. */
+static int pinched_torus(ModeInfo *mi, double umin, double umax, double vmin,
+ double vmax)
{
int polys = 0;
- static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
- static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
- static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
- static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
- float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[4][4];
+ static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_oneside[] = { 0.9, 0.4, 0.3, 1.0 };
+ static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_oneside[] = { 0.9, 0.4, 0.3, 0.7 };
+ float mat_diff_dyn[4], mat_diff_dyn_compl[4];
+ float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[4][4], matc[3][3];
int i, j, k, l, m, o;
- double u, v;
+ double u, v, ur, vr;
double xx[4], xxu[4], xxv[4], y[4], yu[4], yv[4];
double q, r, s, t;
double cu, su, cv, sv, cv2, sv2;
float q1[4], q2[4], r1[4][4], r2[4][4];
kleinstruct *kb = &klein[MI_SCREEN(mi)];
+ if (kb->change_colors)
+ rotateall3d(kb->rho,kb->sigma,kb->tau,matc);
+
if (kb->view == VIEW_WALK || kb->view == VIEW_WALKTURN)
{
/* Compute the rotation that rotates the Klein bottle in 4D without the
@@ -1012,23 +1217,23 @@ static int squeezed_torus(ModeInfo *mi, double umin, double umax, double vmin,
sv = sin(v);
cv2 = cos(0.5*v);
sv2 = sin(0.5*v);
- xx[0] = (SQUEEZED_TORUS_RADIUS+cu)*cv;
- xx[1] = (SQUEEZED_TORUS_RADIUS+cu)*sv;
+ xx[0] = (PINCHED_TORUS_RADIUS+cu)*cv;
+ xx[1] = (PINCHED_TORUS_RADIUS+cu)*sv;
xx[2] = su*cv2;
xx[3] = su*sv2;
xxu[0] = -su*cv;
xxu[1] = -su*sv;
xxu[2] = cu*cv2;
xxu[3] = cu*sv2;
- xxv[0] = -(SQUEEZED_TORUS_RADIUS+cu)*sv;
- xxv[1] = (SQUEEZED_TORUS_RADIUS+cu)*cv;
+ xxv[0] = -(PINCHED_TORUS_RADIUS+cu)*sv;
+ xxv[1] = (PINCHED_TORUS_RADIUS+cu)*cv;
xxv[2] = -0.5*su*sv2;
xxv[3] = 0.5*su*cv2;
for (l=0; l<4; l++)
{
- xx[l] /= SQUEEZED_TORUS_RADIUS+1.25;
- xxu[l] /= SQUEEZED_TORUS_RADIUS+1.25;
- xxv[l] /= SQUEEZED_TORUS_RADIUS+1.25;
+ xx[l] /= PINCHED_TORUS_RADIUS+1.25;
+ xxu[l] /= PINCHED_TORUS_RADIUS+1.25;
+ xxv[l] /= PINCHED_TORUS_RADIUS+1.25;
}
for (l=0; l<4; l++)
{
@@ -1103,12 +1308,12 @@ static int squeezed_torus(ModeInfo *mi, double umin, double umax, double vmin,
sv = sin(v);
cv2 = cos(0.5*v);
sv2 = sin(0.5*v);
- xx[0] = (SQUEEZED_TORUS_RADIUS+cu)*cv;
- xx[1] = (SQUEEZED_TORUS_RADIUS+cu)*sv;
+ xx[0] = (PINCHED_TORUS_RADIUS+cu)*cv;
+ xx[1] = (PINCHED_TORUS_RADIUS+cu)*sv;
xx[2] = su*cv2;
xx[3] = su*sv2;
for (l=0; l<4; l++)
- xx[l] /= SQUEEZED_TORUS_RADIUS+1.25;
+ xx[l] /= PINCHED_TORUS_RADIUS+1.25;
for (l=0; l<4; l++)
{
r = 0.0;
@@ -1193,22 +1398,60 @@ static int squeezed_torus(ModeInfo *mi, double umin, double umax, double vmin,
}
}
- if (kb->colors == COLORS_TWOSIDED)
+ if (!kb->change_colors)
{
- glColor3fv(mat_diff_red);
- if (kb->display_mode == DISP_TRANSPARENT)
+ if (kb->colors == COLORS_ONESIDED)
{
- glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
- glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ glColor3fv(mat_diff_oneside);
+ if (kb->display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_trans_oneside);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_oneside);
+ }
}
- else
+ else if (kb->colors == COLORS_TWOSIDED)
{
- glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
- glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ glColor3fv(mat_diff_red);
+ if (kb->display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ }
+ }
+ }
+ else /* kb->change_colors */
+ {
+ color(kb,0.0,matc,mat_diff_dyn);
+ if (kb->colors == COLORS_ONESIDED)
+ {
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ }
+ else if (kb->colors == COLORS_TWOSIDED)
+ {
+ mat_diff_dyn_compl[0] = 1.0f-mat_diff_dyn[0];
+ mat_diff_dyn_compl[1] = 1.0f-mat_diff_dyn[1];
+ mat_diff_dyn_compl[2] = 1.0f-mat_diff_dyn[2];
+ mat_diff_dyn_compl[3] = mat_diff_dyn[3];
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn_compl);
}
}
glBindTexture(GL_TEXTURE_2D,kb->tex_name);
+ ur = umax-umin;
+ vr = vmax-vmin;
for (i=0; i<NUMU; i++)
{
if (kb->appearance == APPEARANCE_BANDS && ((i & (NUMB-1)) >= NUMB/2))
@@ -1226,7 +1469,20 @@ static int squeezed_torus(ModeInfo *mi, double umin, double umax, double vmin,
o = l*(NUMV+1)+m;
glNormal3fv(kb->pn[o]);
glTexCoord2fv(kb->tex[o]);
- if (kb->colors != COLORS_TWOSIDED)
+ if (kb->change_colors)
+ {
+ v = vr*l/NUMV+vmin;
+ if (kb->colors == COLORS_DEPTH)
+ {
+ u = -ur*m/NUMU+umin;
+ color(kb,(sin(u)*sin(0.5*v)+1.0)*M_PI*2.0/3.0,matc,kb->col[o]);
+ }
+ else if (kb->colors == COLORS_RAINBOW)
+ {
+ color(kb,v,matc,kb->col[o]);
+ }
+ }
+ if (kb->colors != COLORS_ONESIDED && kb->colors != COLORS_TWOSIDED)
{
glColor3fv(kb->col[o]);
glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,kb->col[o]);
@@ -1247,19 +1503,25 @@ static int lawson(ModeInfo *mi, double umin, double umax, double vmin,
double vmax)
{
int polys = 0;
- static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
- static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
- static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
- static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
- float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[4][4];
+ static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_oneside[] = { 0.9, 0.4, 0.3, 1.0 };
+ static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_oneside[] = { 0.9, 0.4, 0.3, 0.7 };
+ float mat_diff_dyn[4], mat_diff_dyn_compl[4];
+ float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[4][4], matc[3][3];
int i, j, k, l, m, o;
- double u, v;
+ double u, v, ur, vr;
double cu, su, cv, sv, cv2, sv2;
double xx[4], xxu[4], xxv[4], y[4], yu[4], yv[4];
double q, r, s, t;
float q1[4], q2[4], r1[4][4], r2[4][4];
kleinstruct *kb = &klein[MI_SCREEN(mi)];
+ if (kb->change_colors)
+ rotateall3d(kb->rho,kb->sigma,kb->tau,matc);
+
if (kb->view == VIEW_WALK || kb->view == VIEW_WALKTURN)
{
/* Compute the rotation that rotates the Klein bottle in 4D without the
@@ -1447,22 +1709,60 @@ static int lawson(ModeInfo *mi, double umin, double umax, double vmin,
}
}
- if (kb->colors == COLORS_TWOSIDED)
+ if (!kb->change_colors)
{
- glColor3fv(mat_diff_red);
- if (kb->display_mode == DISP_TRANSPARENT)
+ if (kb->colors == COLORS_ONESIDED)
{
- glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
- glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ glColor3fv(mat_diff_oneside);
+ if (kb->display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_trans_oneside);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_oneside);
+ }
}
- else
+ else if (kb->colors == COLORS_TWOSIDED)
+ {
+ glColor3fv(mat_diff_red);
+ if (kb->display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ }
+ }
+ }
+ else /* kb->change_colors */
+ {
+ color(kb,0.0,matc,mat_diff_dyn);
+ if (kb->colors == COLORS_ONESIDED)
+ {
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ }
+ else if (kb->colors == COLORS_TWOSIDED)
{
- glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
- glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ mat_diff_dyn_compl[0] = 1.0f-mat_diff_dyn[0];
+ mat_diff_dyn_compl[1] = 1.0f-mat_diff_dyn[1];
+ mat_diff_dyn_compl[2] = 1.0f-mat_diff_dyn[2];
+ mat_diff_dyn_compl[3] = mat_diff_dyn[3];
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn_compl);
}
}
glBindTexture(GL_TEXTURE_2D,kb->tex_name);
+ ur = umax-umin;
+ vr = vmax-vmin;
for (i=0; i<NUMV; i++)
{
if (kb->appearance == APPEARANCE_BANDS && ((i & (NUMB-1)) >= NUMB/2))
@@ -1480,7 +1780,20 @@ static int lawson(ModeInfo *mi, double umin, double umax, double vmin,
o = l*(NUMU+1)+m;
glNormal3fv(kb->pn[o]);
glTexCoord2fv(kb->tex[o]);
- if (kb->colors != COLORS_TWOSIDED)
+ if (kb->change_colors)
+ {
+ v = vr*l/NUMV+vmin;
+ if (kb->colors == COLORS_DEPTH)
+ {
+ u = -ur*m/NUMU+umin;
+ color(kb,(sin(u)*cos(0.5*v)+1.0)*M_PI*2.0/3.0,matc,kb->col[o]);
+ }
+ else if (kb->colors == COLORS_RAINBOW)
+ {
+ color(kb,v,matc,kb->col[o]);
+ }
+ }
+ if (kb->colors != COLORS_ONESIDED && kb->colors != COLORS_TWOSIDED)
{
glColor3fv(kb->col[o]);
glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,kb->col[o]);
@@ -1509,7 +1822,7 @@ static void gen_texture(ModeInfo *mi)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
- glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,TEX_DIMENSION,TEX_DIMENSION,0,
+ glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,TEX_DIMENSION,TEX_DIMENSION,0,
GL_LUMINANCE,GL_UNSIGNED_BYTE,texture);
}
@@ -1540,7 +1853,7 @@ static void init(ModeInfo *mi)
}
kb->zeta = 0.0;
if (kb->bottle_type == KLEIN_BOTTLE_FIGURE_8 ||
- kb->bottle_type == KLEIN_BOTTLE_SQUEEZED_TORUS)
+ kb->bottle_type == KLEIN_BOTTLE_PINCHED_TORUS)
kb->eta = 0.0;
else
kb->eta = 45.0;
@@ -1551,6 +1864,10 @@ static void init(ModeInfo *mi)
kb->dvmove = 0.0;
kb->side = 1;
+ kb->rho = frand(360.0);
+ kb->sigma = frand(360.0);
+ kb->tau = frand(360.0);
+
if (kb->bottle_type == KLEIN_BOTTLE_FIGURE_8)
{
kb->offset4d[0] = 0.0;
@@ -1565,7 +1882,7 @@ static void init(ModeInfo *mi)
kb->offset3d[2] = -1.9;
kb->offset3d[3] = 0.0;
}
- else if (kb->bottle_type == KLEIN_BOTTLE_SQUEEZED_TORUS)
+ else if (kb->bottle_type == KLEIN_BOTTLE_PINCHED_TORUS)
{
kb->offset4d[0] = 0.0;
kb->offset4d[1] = 0.0;
@@ -1598,8 +1915,8 @@ static void init(ModeInfo *mi)
gen_texture(mi);
if (kb->bottle_type == KLEIN_BOTTLE_FIGURE_8)
setup_figure8(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
- else if (kb->bottle_type == KLEIN_BOTTLE_SQUEEZED_TORUS)
- setup_squeezed_torus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
+ else if (kb->bottle_type == KLEIN_BOTTLE_PINCHED_TORUS)
+ setup_pinched_torus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
else /* kb->bottle_type == KLEIN_BOTTLE_LAWSON */
setup_lawson(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
@@ -1736,6 +2053,18 @@ static void display_klein(ModeInfo *mi)
if (kb->umove < 0.0)
kb->umove += 2.0*M_PI;
}
+ if (kb->change_colors)
+ {
+ kb->rho += DRHO;
+ if (kb->rho >= 360.0)
+ kb->rho -= 360.0;
+ kb->sigma += DSIGMA;
+ if (kb->sigma >= 360.0)
+ kb->sigma -= 360.0;
+ kb->tau += DTAU;
+ if (kb->tau >= 360.0)
+ kb->tau -= 360.0;
+ }
}
glMatrixMode(GL_PROJECTION);
@@ -1760,8 +2089,8 @@ static void display_klein(ModeInfo *mi)
if (kb->bottle_type == KLEIN_BOTTLE_FIGURE_8)
mi->polygon_count = figure8(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
- else if (kb->bottle_type == KLEIN_BOTTLE_SQUEEZED_TORUS)
- mi->polygon_count = squeezed_torus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
+ else if (kb->bottle_type == KLEIN_BOTTLE_PINCHED_TORUS)
+ mi->polygon_count = pinched_torus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
else /* kb->bottle_type == KLEIN_BOTTLE_LAWSON */
mi->polygon_count = lawson(mi,0.0,2.0*M_PI,0.0,2.0*M_PI);
}
@@ -1874,9 +2203,9 @@ ENTRYPOINT void init_klein(ModeInfo *mi)
{
kb->bottle_type = KLEIN_BOTTLE_FIGURE_8;
}
- else if (!strcasecmp(klein_bottle,"squeezed-torus"))
+ else if (!strcasecmp(klein_bottle,"pinched-torus"))
{
- kb->bottle_type = KLEIN_BOTTLE_SQUEEZED_TORUS;
+ kb->bottle_type = KLEIN_BOTTLE_PINCHED_TORUS;
}
else if (!strcasecmp(klein_bottle,"lawson"))
{
@@ -1936,6 +2265,10 @@ ENTRYPOINT void init_klein(ModeInfo *mi)
{
kb->colors = random() % NUM_COLORS;
}
+ else if (!strcasecmp(color_mode,"one-sided"))
+ {
+ kb->colors = COLORS_ONESIDED;
+ }
else if (!strcasecmp(color_mode,"two-sided"))
{
kb->colors = COLORS_TWOSIDED;
@@ -1953,6 +2286,8 @@ ENTRYPOINT void init_klein(ModeInfo *mi)
kb->colors = random() % NUM_COLORS;
}
+ kb->change_colors = change_colors;
+
/* Set the view mode. */
if (!strcasecmp(view_mode,"random"))
{
diff --git a/hacks/glx/klein.man b/hacks/glx/klein.man
index 1b456bc..342c11f 100644
--- a/hacks/glx/klein.man
+++ b/hacks/glx/klein.man
@@ -12,7 +12,7 @@ klein - Draws a 4d Klein bottle.
[\-fps]
[\-klein-bottle \fIbottle-name\fP]
[-figure-8]
-[-squeezed-torus]
+[-pinched-torus]
[-lawson]
[\-mode \fIdisplay-mode\fP]
[\-wireframe]
@@ -22,9 +22,11 @@ klein - Draws a 4d Klein bottle.
[\-solid]
[\-bands]
[\-colors \fIcolor-scheme\fP]
+[\-onesided]
[\-twosided]
[\-rainbow]
[\-depth]
+[\-change-colors]
[\-view-mode \fIview-mode\fP]
[\-walk]
[\-turn]
@@ -46,56 +48,72 @@ klein - Draws a 4d Klein bottle.
[\-walk-speed \fIfloat\fP]
.SH DESCRIPTION
The \fIklein\fP program shows three different Klein bottles in 4d: the
-figure-8 Klein bottle, the squeezed torus Klein bottle, or the Lawson
+figure-8 Klein bottle, the pinched torus Klein bottle, or the Lawson
Klein bottle. You can walk on the Klein bottle, see it turn in 4d, or
walk on it while it turns in 4d. The figure-8 Klein bottle is well
known in its 3d form. The 4d form used in this program is an
extension of the 3d form to 4d that does not intersect itself in 4d
-(which can be seen in the depth colors mode). The squeezed torus
-Klein bottle also does not intersect itself in 4d (which can be seen
-in the depth colors mode). The Lawson Klein bottle, on the other
-hand, does intersect itself in 4d. Its primary use is that it has a
-nice appearance for walking and for turning in 3d. The Klein bottle
-is a non-orientable surface. To make this apparent, the two-sided
-color mode can be used. Alternatively, orientation markers (curling
-arrows) can be drawn as a texture map on the surface of the Klein
-bottle. While walking on the Klein bottle, you will notice that the
-orientation of the curling arrows changes (which it must because the
-Klein bottle is non-orientable). The program projects the 4d Klein
-bottle to 3d using either a perspective or an orthographic projection.
-Which of the two alternatives looks more appealing depends on the
-viewing mode and the Klein bottle. For example, the Lawson Klein
-bottle looks nicest when projected perspectively. The figure-8 Klein
-bottle, on the other hand, looks nicer while walking when projected
-orthographically from 4d. For the squeezed torus Klein bottle, both
-projection modes give equally acceptable projections. The projected
-Klein bottle can then be projected to the screen either perspectively
-or orthographically. When using the walking modes, perspective
-projection to the screen should be used. There are three display
-modes for the Klein bottle: mesh (wireframe), solid, or transparent.
-Furthermore, the appearance of the Klein bottle can be as a solid
-object or as a set of see-through bands. Finally, the colors with
-with the Klein bottle is drawn can be set to two-sided, rainbow, or
-depth. In the first case, the Klein bottle is drawn with red on one
-"side" and green on the "other side". Of course, the Klein bottle
+(which can be seen in the depth colors mode when using static colors).
+The pinched torus Klein bottle also does not intersect itself in 4d
+(which can be seen in the depth colors mode when using static colors).
+The Lawson Klein bottle, on the other hand, does intersect itself in
+4d. Its primary use is that it has a nice appearance for walking and
+for turning in 3d.
+.PP
+The Klein bottle is a non-orientable surface. To make this apparent,
+the two-sided color mode can be used. Alternatively, orientation
+markers (curling arrows) can be drawn as a texture map on the surface
+of the Klein bottle. While walking on the Klein bottle, you will
+notice that the orientation of the curling arrows changes (which it
+must because the Klein bottle is non-orientable).
+.PP
+The program projects the 4d Klein bottle to 3d using either a
+perspective or an orthographic projection. Which of the two
+alternatives looks more appealing depends on the viewing mode and the
+Klein bottle. For example, the Lawson Klein bottle looks nicest when
+projected perspectively. The figure-8 Klein bottle, on the other
+hand, looks nicer while walking when projected orthographically from
+4d. For the pinched torus Klein bottle, both projection modes give
+equally acceptable projections.
+.PP
+The projected Klein bottle can then be projected to the screen either
+perspectively or orthographically. When using the walking modes,
+perspective projection to the screen should be used.
+.PP
+There are three display modes for the Klein bottle: mesh (wireframe),
+solid, or transparent. Furthermore, the appearance of the Klein
+bottle can be as a solid object or as a set of see-through bands.
+Finally, the colors with with the Klein bottle is drawn can be set to
+one-sided, two-sided, rainbow, or depth. In one-sided mode, the Klein
+bottle is drawn with the same color on both "sides." In two-sided
+mode (using static colors), the Klein bottle is drawn with red on one
+"side" and green on the "other side." Of course, the Klein bottle
only has one side, so the color jumps from red to green along a curve
on the surface of the Klein bottle. This mode enables you to see that
-the Klein bottle is non-orientable. The second mode draws the Klein
-bottle with fully saturated rainbow colors. This gives a very nice
-effect when combined with the see-through bands mode or with the
-orientation markers drawn. The third mode draws the Klein bottle with
-colors that are chosen according to the 4d "depth" of the points.
-This mode enables you to see that the figure-8 and squeezed torus
-Klein bottles do not intersect themselves in 4d, while the Lawson
-Klein bottle does intersect itself. The rotation speed for each of
-the six planes around which the Klein bottle rotates can be chosen.
-For the walk-and-turn mode, only the rotation speeds around the true
-4d planes are used (the xy, xz, and yz planes). Furthermore, in the
-walking modes the walking direction in the 2d base square of the Klein
-bottle and the walking speed can be chosen. This program is somewhat
-inspired by Thomas Banchoff's book "Beyond the Third Dimension:
-Geometry, Computer Graphics, and Higher Dimensions", Scientific
-American Library, 1990.
+the Klein bottle is non-orientable. If changing colors are used in
+two-sided mode, changing complementary colors are used on the
+respective "sides." The rainbow color mode (using static colors)
+draws the Klein bottle with a color wheel of fully saturated rainbow
+colors. If changing colors are used, the color wheel's colors change
+dynamically. The rainbow color mode gives a very nice effect when
+combined with the see-through bands mode or with the orientation
+markers drawn. The depth color mode draws the Klein bottle with
+colors that are chosen according to the 4d "depth" of the points. If
+static colors are used, this mode enables you to see that the figure-8
+and pinched torus Klein bottles do not intersect themselves in 4d,
+while the Lawson Klein bottle does intersect itself.
+.PP
+The rotation speed for each of the six planes around which the Klein
+bottle rotates can be chosen. For the walk-and-turn mode, only the
+rotation speeds around the true 4d planes are used (the xy, xz, and yz
+planes).
+.PP
+Furthermore, in the walking modes the walking direction in the 2d base
+square of the Klein bottle and the walking speed can be chosen.
+.PP
+This program is somewhat inspired by Thomas Banchoff's book "Beyond
+the Third Dimension: Geometry, Computer Graphics, and Higher
+Dimensions", Scientific American Library, 1990.
.SH OPTIONS
.I klein
accepts the following options:
@@ -129,8 +147,8 @@ Display a random Klein bottle (default).
.B \-klein-bottle figure-8 \fP(Shortcut: \fB\-figure-8\fP)
Display the figure-8 Klein bottle.
.TP 8
-.B \-klein-bottle squeezed-torus \fP(Shortcut: \fB\-squeezed-torus\fP)
-Display the squeezed torus Klein bottle.
+.B \-klein-bottle pinched-torus \fP(Shortcut: \fB\-pinched-torus\fP)
+Display the pinched torus Klein bottle.
.TP 8
.B \-klein-bottle lawson \fP(Shortcut: \fB\-lawson\fP)
Display the Lawson Klein bottle.
@@ -162,15 +180,19 @@ Display the Klein bottle as a solid object.
.B \-appearance bands \fP(Shortcut: \fB\-bands\fP)
Display the Klein bottle as see-through bands.
.PP
-The following four options are mutually exclusive. They determine
+The following five options are mutually exclusive. They determine
how to color the Klein bottle.
.TP 8
.B \-colors random
Display the Klein bottle with a random color scheme (default).
.TP 8
-.B \-colors twosided \fP(Shortcut: \fB\-twosided\fP)
-Display the Klein bottle with two colors: red on one "side" and green
-on the "other side".
+.B \-colors one-sided \fP(Shortcut: \fB\-onesided\fP)
+Display the Klein bottle with a single color.
+.TP 8
+.B \-colors two-sided \fP(Shortcut: \fB\-twosided\fP)
+Display the Klein bottle with two colors: one color one "side" and the
+complementary color on the "other side." For static colors, the
+colors are red and green.
.TP 8
.B \-colors rainbow \fP(Shortcut: \fB\-rainbow\fP)
Display the Klein bottle with fully saturated rainbow colors. If the
@@ -181,6 +203,16 @@ displayed with a different color.
Display the Klein bottle with colors chosen depending on the 4d
"depth" of the points.
.PP
+The following options determine whether the colors with which the
+Klein bottle is displayed are static or are changing dynamically.
+.TP 8
+.B \-change-colors
+Change the colors with which the Klein bottle is displayed
+dynamically.
+.TP 8
+.B \-no-change-colors
+Use static colors to display the Klein bottle (default).
+.PP
The following four options are mutually exclusive. They determine
how to view the Klein bottle.
.TP 8
@@ -296,7 +328,7 @@ stored in the RESOURCE_MANAGER property.
.BR X (1),
.BR xscreensaver (1)
.SH COPYRIGHT
-Copyright \(co 2005-2014 by Carsten Steger. Permission to use, copy,
+Copyright \(co 2005-2020 by Carsten Steger. Permission to use, copy,
modify, distribute, and sell this software and its documentation for
any purpose is hereby granted without fee, provided that the above
copyright notice appear in all copies and that both that copyright
@@ -305,4 +337,4 @@ No representations are made about the suitability of this software for
any purpose. It is provided "as is" without express or implied
warranty.
.SH AUTHOR
-Carsten Steger <carsten@mirsanmir.org>, 03-oct-2014.
+Carsten Steger <carsten@mirsanmir.org>, 11-jan-2020.
diff --git a/hacks/glx/polytopes.c b/hacks/glx/polytopes.c
index c704722..333fcde 100644
--- a/hacks/glx/polytopes.c
+++ b/hacks/glx/polytopes.c
@@ -4,7 +4,7 @@
static const char sccsid[] = "@(#)polytopes.c 1.2 05/09/28 xlockmore";
#endif
-/* Copyright (c) 2003-2009 Carsten Steger <carsten@mirsanmir.org>. */
+/* Copyright (c) 2003-2019 Carsten Steger <carsten@mirsanmir.org>. */
/*
* Permission to use, copy, modify, and distribute this software and its
@@ -2862,16 +2862,10 @@ static void display_polytopes(ModeInfo *mi)
ENTRYPOINT void reshape_polytopes(ModeInfo *mi, int width, int height)
{
polytopesstruct *pp = &poly[MI_SCREEN(mi)];
- int y = 0;
-
- if (width > height * 5) { /* tiny window: show middle */
- height = width;
- y = -height/2;
- }
pp->WindW = (GLint)width;
pp->WindH = (GLint)height;
- glViewport(0,y,width,height);
+ glViewport(0,0,width,height);
pp->aspect = (GLfloat)width/(GLfloat)height;
}
diff --git a/hacks/glx/projectiveplane.c b/hacks/glx/projectiveplane.c
index 3a052ea..5f42d60 100644
--- a/hacks/glx/projectiveplane.c
+++ b/hacks/glx/projectiveplane.c
@@ -2,10 +2,10 @@
that rotates in 4d or on which you can walk */
#if 0
-static const char sccsid[] = "@(#)projectiveplane.c 1.1 14/01/01 xlockmore";
+static const char sccsid[] = "@(#)projectiveplane.c 1.1 03/01/14 xlockmore";
#endif
-/* Copyright (c) 2005-2014 Carsten Steger <carsten@mirsanmir.org>. */
+/* Copyright (c) 2013-2020 Carsten Steger <carsten@mirsanmir.org>. */
/*
* Permission to use, copy, modify, and distribute this software and its
@@ -21,8 +21,9 @@ static const char sccsid[] = "@(#)projectiveplane.c 1.1 14/01/01 xlockmore";
* other special, indirect and consequential damages.
*
* REVISION HISTORY:
- * C. Steger - 14/01/03: Initial version
- * C. Steger - 14/10/03: Moved the curlicue texture to curlicue.h
+ * C. Steger - 03/01/14: Initial version
+ * C. Steger - 03/10/14: Moved the curlicue texture to curlicue.h
+ * C. Steger - 06/01/20: Added the changing colors mode.
*/
/*
@@ -30,12 +31,13 @@ static const char sccsid[] = "@(#)projectiveplane.c 1.1 14/01/01 xlockmore";
* You can walk on the projective plane, see it turn in 4d, or walk on
* it while it turns in 4d. The fact that the surface is an embedding
* of the real projective plane in 4d can be seen in the depth colors
- * mode: set all rotation speeds to 0 and the projection mode to 4d
- * orthographic projection. In its default orientation, the embedding
- * of the real projective plane will then project to the Roman
- * surface, which has three lines of self-intersection. However, at
- * the three lines of self-intersection the parts of the surface that
- * intersect have different colors, i.e., different 4d depths.
+ * mode (using static colors): set all rotation speeds to 0 and the
+ * projection mode to 4d orthographic projection. In its default
+ * orientation, the embedding of the real projective plane will then
+ * project to the Roman surface, which has three lines of
+ * self-intersection. However, at the three lines of
+ * self-intersection the parts of the surface that intersect have
+ * different colors, i.e., different 4d depths.
*
* The real projective plane is a non-orientable surface. To make
* this apparent, the two-sided color mode can be used.
@@ -48,33 +50,35 @@ static const char sccsid[] = "@(#)projectiveplane.c 1.1 14/01/01 xlockmore";
* The real projective plane is a model for the projective geometry in
* 2d space. One point can be singled out as the origin. A line can
* be singled out as the line at infinity, i.e., a line that lies at
- * an infinite distance to the origin. The line at infinity is
- * topologically a circle. Points on the line at infinity are also
- * used to model directions in projective geometry. The origin can be
- * visualized in different manners. When using distance colors, the
+ * an infinite distance to the origin. The line at infinity, like all
+ * lines in the projective plane, is topologically a circle. Points
+ * on the line at infinity are also used to model directions in
+ * projective geometry. The origin can be visualized in different
+ * manners. When using distance colors (and using static colors), the
* origin is the point that is displayed as fully saturated red, which
* is easier to see as the center of the reddish area on the
* projective plane. Alternatively, when using distance bands, the
- * origin is the center of the only band that projects to a disc.
+ * origin is the center of the only band that projects to a disk.
* When using direction bands, the origin is the point where all
* direction bands collapse to a point. Finally, when orientation
* markers are being displayed, the origin the the point where all
* orientation markers are compressed to a point. The line at
* infinity can also be visualized in different ways. When using
- * distance colors, the line at infinity is the line that is displayed
- * as fully saturated magenta. When two-sided colors are used, the
- * line at infinity lies at the points where the red and green "sides"
- * of the projective plane meet (of course, the real projective plane
- * only has one side, so this is a design choice of the
- * visualization). Alternatively, when orientation markers are being
- * displayed, the line at infinity is the place where the orientation
- * markers change their orientation.
+ * distance colors (and using static colors), the line at infinity is
+ * the line that is displayed as fully saturated magenta. When
+ * two-sided (and static) colors are used, the line at infinity lies
+ * at the points where the red and green "sides" of the projective
+ * plane meet (of course, the real projective plane only has one side,
+ * so this is a design choice of the visualization). Alternatively,
+ * when orientation markers are being displayed, the line at infinity
+ * is the place where the orientation markers change their
+ * orientation.
*
* Note that when the projective plane is displayed with bands, the
* orientation markers are placed in the middle of the bands. For
* distance bands, the bands are chosen in such a way that the band at
* the origin is only half as wide as the remaining bands, which
- * results in a disc being displayed at the origin that has the same
+ * results in a disk being displayed at the origin that has the same
* diameter as the remaining bands. This choice, however, also
* implies that the band at infinity is half as wide as the other
* bands. Since the projective plane is attached to itself (in a
@@ -119,33 +123,37 @@ static const char sccsid[] = "@(#)projectiveplane.c 1.1 14/01/01 xlockmore";
* projective plane is non-orientable.
*
* Finally, the colors with with the projective plane is drawn can be
- * set to two-sided, distance, direction, or depth. In two-sided
- * mode, the projective plane is drawn with red on one "side" and
- * green on the "other side". As described above, the projective
- * plane only has one side, so the color jumps from red to green along
- * the line at infinity. This mode enables you to see that the
- * projective plane is non-orientable. In distance mode, the
- * projective plane is displayed with fully saturated colors that
- * depend on the distance of the points on the projective plane to the
- * origin. The origin is displayed in red, the line at infinity is
- * displayed in magenta. If the projective plane is displayed as
- * distance bands, each band will be displayed with a different color.
- * In direction mode, the projective plane is displayed with fully
+ * set to one-sided, two-sided, distance, direction, or depth. In
+ * one-sided mode, the projective plane is drawn with the same color
+ * on both "sides." In two-sided mode (using static colors), the
+ * projective plane is drawn with red on one "side" and green on the
+ * "other side." As described above, the projective plane only has
+ * one side, so the color jumps from red to green along the line at
+ * infinity. This mode enables you to see that the projective plane
+ * is non-orientable. If changing colors are used in two-sided mode,
+ * changing complementary colors are used on the respective "sides."
+ * In distance mode, the projective plane is displayed with fully
+ * saturated colors that depend on the distance of the points on the
+ * projective plane to the origin. If static colors are used, the
+ * origin is displayed in red, while the line at infinity is displayed
+ * in magenta. If the projective plane is displayed as distance
+ * bands, each band will be displayed with a different color. In
+ * direction mode, the projective plane is displayed with fully
* saturated colors that depend on the angle of the points on the
* projective plane with respect to the origin. Angles in opposite
* directions to the origin (e.g., 15 and 205 degrees) are displayed
* in the same color since they are projectively equivalent. If the
* projective plane is displayed as direction bands, each band will be
* displayed with a different color. Finally, in depth mode the
- * projective plane with colors chosen depending on the 4d "depth"
- * (i.e., the w coordinate) of the points on the projective plane at
- * its default orientation in 4d. As discussed above, this mode
- * enables you to see that the projective plane does not intersect
- * itself in 4d.
+ * projective plane is displayed with colors chosen depending on the
+ * 4d "depth" (i.e., the w coordinate) of the points on the projective
+ * plane at its default orientation in 4d. As discussed above, this
+ * mode enables you to see that the projective plane does not
+ * intersect itself in 4d.
*
* The rotation speed for each of the six planes around which the
* projective plane rotates can be chosen. For the walk-and-turn
- * more, only the rotation speeds around the true 4d planes are used
+ * mode, only the rotation speeds around the true 4d planes are used
* (the xy, xz, and yz planes).
*
* Furthermore, in the walking modes the walking direction in the 2d
@@ -180,11 +188,12 @@ static const char sccsid[] = "@(#)projectiveplane.c 1.1 14/01/01 xlockmore";
#define APPEARANCE_DIRECTION_BANDS 2
#define NUM_APPEARANCES 3
-#define COLORS_TWOSIDED 0
-#define COLORS_DISTANCE 1
-#define COLORS_DIRECTION 2
-#define COLORS_DEPTH 3
-#define NUM_COLORS 4
+#define COLORS_ONESIDED 0
+#define COLORS_TWOSIDED 1
+#define COLORS_DISTANCE 2
+#define COLORS_DIRECTION 3
+#define COLORS_DEPTH 4
+#define NUM_COLORS 5
#define VIEW_WALK 0
#define VIEW_TURN 1
@@ -204,6 +213,7 @@ static const char sccsid[] = "@(#)projectiveplane.c 1.1 14/01/01 xlockmore";
#define DEF_COLORS "random"
#define DEF_VIEW_MODE "random"
#define DEF_MARKS "False"
+#define DEF_CHANGE_COLORS "False"
#define DEF_PROJECTION_3D "random"
#define DEF_PROJECTION_4D "random"
#define DEF_SPEEDWX "1.1"
@@ -252,6 +262,7 @@ static char *appear;
static char *color_mode;
static char *view_mode;
static Bool marks;
+static Bool change_colors;
static char *proj_3d;
static char *proj_4d;
static float speed_wx;
@@ -275,16 +286,19 @@ static XrmOptionDescRec opts[] =
{"-distance-bands", ".appearance", XrmoptionNoArg, "distance-bands" },
{"-direction-bands", ".appearance", XrmoptionNoArg, "direction-bands" },
{"-colors", ".colors", XrmoptionSepArg, 0 },
+ {"-onesided-colors", ".colors", XrmoptionNoArg, "one-sided" },
{"-twosided-colors", ".colors", XrmoptionNoArg, "two-sided" },
{"-distance-colors", ".colors", XrmoptionNoArg, "distance" },
{"-direction-colors", ".colors", XrmoptionNoArg, "direction" },
{"-depth-colors", ".colors", XrmoptionNoArg, "depth" },
+ {"-change-colors", ".changeColors", XrmoptionNoArg, "on"},
+ {"+change-colors", ".changeColors", XrmoptionNoArg, "off"},
{"-view-mode", ".viewMode", XrmoptionSepArg, 0 },
{"-walk", ".viewMode", XrmoptionNoArg, "walk" },
{"-turn", ".viewMode", XrmoptionNoArg, "turn" },
{"-walk-turn", ".viewMode", XrmoptionNoArg, "walk-turn" },
- {"-orientation-marks", ".marks", XrmoptionNoArg, "on"},
- {"+orientation-marks", ".marks", XrmoptionNoArg, "off"},
+ {"-orientation-marks", ".marks", XrmoptionNoArg, "on"},
+ {"+orientation-marks", ".marks", XrmoptionNoArg, "off"},
{"-projection-3d", ".projection3d", XrmoptionSepArg, 0 },
{"-perspective-3d", ".projection3d", XrmoptionNoArg, "perspective" },
{"-orthographic-3d", ".projection3d", XrmoptionNoArg, "orthographic" },
@@ -306,6 +320,7 @@ static argtype vars[] =
{ &mode, "displayMode", "DisplayMode", DEF_DISPLAY_MODE, t_String },
{ &appear, "appearance", "Appearance", DEF_APPEARANCE, t_String },
{ &color_mode, "colors", "Colors", DEF_COLORS, t_String },
+ { &change_colors, "changeColors", "ChangeColors", DEF_CHANGE_COLORS, t_Bool },
{ &view_mode, "viewMode", "ViewMode", DEF_VIEW_MODE, t_String },
{ &marks, "marks", "Marks", DEF_MARKS, t_Bool },
{ &proj_3d, "projection3d", "Projection3d", DEF_PROJECTION_3D, t_String },
@@ -327,6 +342,11 @@ ENTRYPOINT ModeSpecOpt projectiveplane_opts =
/* Offset by which we walk above the projective plane */
#define DELTAY 0.01
+/* Color change speeds */
+#define DRHO 0.7
+#define DSIGMA 1.1
+#define DTAU 1.7
+
/* Number of subdivisions of the projective plane */
#define NUMU 128
#define NUMV 128
@@ -342,12 +362,15 @@ typedef struct {
int display_mode;
int appearance;
int colors;
+ Bool change_colors;
int view;
Bool marks;
int projection_3d;
int projection_4d;
/* 4D rotation angles */
float alpha, beta, delta, zeta, eta, theta;
+ /* Color rotation angles */
+ float rho, sigma, tau;
/* Movement parameters */
float umove, vmove, dumove, dvmove;
int side, dir;
@@ -526,6 +549,77 @@ static void rotateall4d(float ze, float et, float th, float m[4][4])
}
+/* Add a rotation around the x-axis to the matrix m. */
+static void rotatex(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI/180.0;
+ c = cos(phi);
+ s = sin(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][1];
+ v = m[i][2];
+ m[i][1] = c*u+s*v;
+ m[i][2] = -s*u+c*v;
+ }
+}
+
+
+/* Add a rotation around the y-axis to the matrix m. */
+static void rotatey(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI/180.0;
+ c = cos(phi);
+ s = sin(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][0];
+ v = m[i][2];
+ m[i][0] = c*u-s*v;
+ m[i][2] = s*u+c*v;
+ }
+}
+
+
+/* Add a rotation around the z-axis to the matrix m. */
+static void rotatez(float m[3][3], float phi)
+{
+ float c, s, u, v;
+ int i;
+
+ phi *= M_PI/180.0;
+ c = cos(phi);
+ s = sin(phi);
+ for (i=0; i<3; i++)
+ {
+ u = m[i][0];
+ v = m[i][1];
+ m[i][0] = c*u+s*v;
+ m[i][1] = -s*u+c*v;
+ }
+}
+
+
+/* Compute the 3d rotation matrix m from the 3d rotation angles. */
+static void rotateall3d(float al, float be, float de, float m[3][3])
+{
+ int i, j;
+
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ m[i][j] = (i==j);
+ rotatex(m,al);
+ rotatey(m,be);
+ rotatez(m,de);
+}
+
+
/* Multiply two rotation matrices: o=m*n. */
static void mult_rotmat(float m[4][4], float n[4][4], float o[4][4])
{
@@ -574,54 +668,80 @@ static void quats_to_rotmat(float p[4], float q[4], float m[4][4])
/* Compute a fully saturated and bright color based on an angle. */
-static void color(projectiveplanestruct *pp, double angle, float col[4])
+static void color(projectiveplanestruct *pp, double angle, float mat[3][3],
+ float col[4])
{
int s;
- double t;
+ double t, ca, sa;
+ float m;
- if (pp->colors == COLORS_TWOSIDED)
- return;
+ if (!pp->change_colors)
+ {
+ if (pp->colors == COLORS_ONESIDED || pp->colors == COLORS_TWOSIDED)
+ return;
- if (angle >= 0.0)
- angle = fmod(angle,2.0*M_PI);
- else
- angle = fmod(angle,-2.0*M_PI);
- s = floor(angle/(M_PI/3));
- t = angle/(M_PI/3)-s;
- if (s >= 6)
- s = 0;
- switch (s)
- {
- case 0:
- col[0] = 1.0;
- col[1] = t;
- col[2] = 0.0;
- break;
- case 1:
- col[0] = 1.0-t;
- col[1] = 1.0;
- col[2] = 0.0;
- break;
- case 2:
- col[0] = 0.0;
- col[1] = 1.0;
- col[2] = t;
- break;
- case 3:
- col[0] = 0.0;
- col[1] = 1.0-t;
- col[2] = 1.0;
- break;
- case 4:
- col[0] = t;
- col[1] = 0.0;
- col[2] = 1.0;
- break;
- case 5:
- col[0] = 1.0;
- col[1] = 0.0;
- col[2] = 1.0-t;
- break;
+ if (angle >= 0.0)
+ angle = fmod(angle,2.0*M_PI);
+ else
+ angle = fmod(angle,-2.0*M_PI);
+ s = floor(angle/(M_PI/3));
+ t = angle/(M_PI/3)-s;
+ if (s >= 6)
+ s = 0;
+ switch (s)
+ {
+ case 0:
+ col[0] = 1.0;
+ col[1] = t;
+ col[2] = 0.0;
+ break;
+ case 1:
+ col[0] = 1.0-t;
+ col[1] = 1.0;
+ col[2] = 0.0;
+ break;
+ case 2:
+ col[0] = 0.0;
+ col[1] = 1.0;
+ col[2] = t;
+ break;
+ case 3:
+ col[0] = 0.0;
+ col[1] = 1.0-t;
+ col[2] = 1.0;
+ break;
+ case 4:
+ col[0] = t;
+ col[1] = 0.0;
+ col[2] = 1.0;
+ break;
+ case 5:
+ col[0] = 1.0;
+ col[1] = 0.0;
+ col[2] = 1.0-t;
+ break;
+ }
+ }
+ else /* pp->change_colors */
+ {
+ if (pp->colors == COLORS_ONESIDED || pp->colors == COLORS_TWOSIDED)
+ {
+ col[0] = mat[0][2];
+ col[1] = mat[1][2];
+ col[2] = mat[2][2];
+ }
+ else
+ {
+ ca = cos(angle);
+ sa = sin(angle);
+ col[0] = ca*mat[0][0]+sa*mat[0][1];
+ col[1] = ca*mat[1][0]+sa*mat[1][1];
+ col[2] = ca*mat[2][0]+sa*mat[2][1];
+ }
+ m = 0.5f/fmaxf(fmaxf(fabsf(col[0]),fabsf(col[1])),fabsf(col[2]));
+ col[0] = m*col[0]+0.5f;
+ col[1] = m*col[1]+0.5f;
+ col[2] = m*col[2]+0.5f;
}
if (pp->display_mode == DISP_TRANSPARENT)
col[3] = 0.7;
@@ -635,7 +755,7 @@ static void setup_projective_plane(ModeInfo *mi, double umin, double umax,
double vmin, double vmax)
{
int i, j, k;
- double u, v, ur, vr;
+ double u, v, w, ur, vr;
double cu, su, cv2, sv2, cv4, sv4, c2u, s2u;
projectiveplanestruct *pp = &projectiveplane[MI_SCREEN(mi)];
@@ -658,12 +778,16 @@ static void setup_projective_plane(ModeInfo *mi, double umin, double umax,
sv2 = sin(0.5*v);
cv4 = cos(0.25*v);
sv4 = sin(0.25*v);
- if (pp->colors == COLORS_DEPTH)
- color(pp,((su*su*sv4*sv4-cv4*cv4)+1.0)*M_PI*2.0/3.0,pp->col[k]);
- else if (pp->colors == COLORS_DIRECTION)
- color(pp,2.0*M_PI+fmod(2.0*u,2.0*M_PI),pp->col[k]);
- else /* pp->colors == COLORS_DISTANCE */
- color(pp,v*(5.0/6.0),pp->col[k]);
+ w = 0.5*(su*su*sv4*sv4-cv4*cv4);
+ if (!pp->change_colors)
+ {
+ if (pp->colors == COLORS_DEPTH)
+ color(pp,(2.0*w+1.0)*M_PI*2.0/3.0,NULL,pp->col[k]);
+ else if (pp->colors == COLORS_DIRECTION)
+ color(pp,2.0*M_PI+fmod(2.0*u,2.0*M_PI),NULL,pp->col[k]);
+ else /* pp->colors == COLORS_DISTANCE */
+ color(pp,v*(5.0/6.0),NULL,pp->col[k]);
+ }
pp->tex[k][0] = -32*u/(2.0*M_PI);
if (pp->appearance != APPEARANCE_DISTANCE_BANDS)
pp->tex[k][1] = 32*v/(2.0*M_PI);
@@ -672,7 +796,7 @@ static void setup_projective_plane(ModeInfo *mi, double umin, double umax,
pp->x[k][0] = 0.5*s2u*sv4*sv4;
pp->x[k][1] = 0.5*su*sv2;
pp->x[k][2] = 0.5*cu*sv2;
- pp->x[k][3] = 0.5*(su*su*sv4*sv4-cv4*cv4);
+ pp->x[k][3] = w;
/* Avoid degenerate tangential plane basis vectors. */
if (v < FLT_EPSILON)
v = FLT_EPSILON;
@@ -697,19 +821,25 @@ static int projective_plane(ModeInfo *mi, double umin, double umax,
double vmin, double vmax)
{
int polys = 0;
- static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
- static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
- static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
- static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
- float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[4][4];
+ static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_oneside[] = { 0.9, 0.4, 0.3, 1.0 };
+ static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_oneside[] = { 0.9, 0.4, 0.3, 0.7 };
+ float mat_diff_dyn[4], mat_diff_dyn_compl[4];
+ float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[4][4], matc[3][3];
int i, j, k, l, m, o;
- double u, v;
+ double u, v, ur, vr;
double xx[4], xxu[4], xxv[4], y[4], yu[4], yv[4];
double q, r, s, t;
double cu, su, cv2, sv2, cv4, sv4, c2u, s2u;
float q1[4], q2[4], r1[4][4], r2[4][4];
projectiveplanestruct *pp = &projectiveplane[MI_SCREEN(mi)];
+ if (pp->change_colors)
+ rotateall3d(pp->rho,pp->sigma,pp->tau,matc);
+
if (pp->view == VIEW_WALK || pp->view == VIEW_WALKTURN)
{
/* Compute the rotation that rotates the projective plane in 4D without
@@ -904,22 +1034,60 @@ static int projective_plane(ModeInfo *mi, double umin, double umax,
}
}
- if (pp->colors == COLORS_TWOSIDED)
+ if (!pp->change_colors)
{
- glColor3fv(mat_diff_red);
- if (pp->display_mode == DISP_TRANSPARENT)
+ if (pp->colors == COLORS_ONESIDED)
{
- glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
- glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ glColor3fv(mat_diff_oneside);
+ if (pp->display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_trans_oneside);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_oneside);
+ }
}
- else
+ else if (pp->colors == COLORS_TWOSIDED)
+ {
+ glColor3fv(mat_diff_red);
+ if (pp->display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ }
+ }
+ }
+ else /* pp->change_colors */
+ {
+ color(pp,0.0,matc,mat_diff_dyn);
+ if (pp->colors == COLORS_ONESIDED)
+ {
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ }
+ else if (pp->colors == COLORS_TWOSIDED)
{
- glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
- glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ mat_diff_dyn_compl[0] = 1.0f-mat_diff_dyn[0];
+ mat_diff_dyn_compl[1] = 1.0f-mat_diff_dyn[1];
+ mat_diff_dyn_compl[2] = 1.0f-mat_diff_dyn[2];
+ mat_diff_dyn_compl[3] = mat_diff_dyn[3];
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn_compl);
}
}
glBindTexture(GL_TEXTURE_2D,pp->tex_name);
+ ur = umax-umin;
+ vr = vmax-vmin;
if (pp->appearance != APPEARANCE_DIRECTION_BANDS)
{
for (i=0; i<NUMV; i++)
@@ -940,7 +1108,24 @@ static int projective_plane(ModeInfo *mi, double umin, double umax,
o = l*(NUMU+1)+m;
glNormal3fv(pp->pn[o]);
glTexCoord2fv(pp->tex[o]);
- if (pp->colors != COLORS_TWOSIDED)
+ if (pp->change_colors)
+ {
+ if (pp->colors == COLORS_DEPTH)
+ {
+ color(pp,(2.0*pp->x[o][3]+1.0)*M_PI*2.0/3.0,matc,pp->col[o]);
+ }
+ else if (pp->colors == COLORS_DIRECTION)
+ {
+ u = -ur*m/NUMU+umin;
+ color(pp,2.0*M_PI+fmod(2.0*u,2.0*M_PI),matc,pp->col[o]);
+ }
+ else if (pp->colors == COLORS_DISTANCE)
+ {
+ v = vr*l/NUMV+vmin;
+ color(pp,v*(5.0/6.0),matc,pp->col[o]);
+ }
+ }
+ if (pp->colors != COLORS_ONESIDED && pp->colors != COLORS_TWOSIDED)
{
glColor3fv(pp->col[o]);
glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,pp->col[o]);
@@ -971,7 +1156,24 @@ static int projective_plane(ModeInfo *mi, double umin, double umax,
o = l*(NUMU+1)+m;
glNormal3fv(pp->pn[o]);
glTexCoord2fv(pp->tex[o]);
- if (pp->colors != COLORS_TWOSIDED)
+ if (pp->change_colors)
+ {
+ if (pp->colors == COLORS_DEPTH)
+ {
+ color(pp,(2.0*pp->x[o][3]+1.0)*M_PI*2.0/3.0,matc,pp->col[o]);
+ }
+ else if (pp->colors == COLORS_DIRECTION)
+ {
+ u = ur*m/NUMU+umin;
+ color(pp,2.0*M_PI+fmod(2.0*u,2.0*M_PI),matc,pp->col[o]);
+ }
+ else if (pp->colors == COLORS_DISTANCE)
+ {
+ v = vr*l/NUMV+vmin;
+ color(pp,v*(5.0/6.0),matc,pp->col[o]);
+ }
+ }
+ if (pp->colors != COLORS_ONESIDED && pp->colors != COLORS_TWOSIDED)
{
glColor3fv(pp->col[o]);
glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,pp->col[o]);
@@ -1002,7 +1204,7 @@ static void gen_texture(ModeInfo *mi)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
- glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,TEX_DIMENSION,TEX_DIMENSION,0,
+ glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,TEX_DIMENSION,TEX_DIMENSION,0,
GL_LUMINANCE,GL_UNSIGNED_BYTE,texture);
}
@@ -1047,6 +1249,10 @@ static void init(ModeInfo *mi)
else
pp->dir = -1;
+ pp->rho = frand(360.0);
+ pp->sigma = frand(360.0);
+ pp->tau = frand(360.0);
+
pp->offset4d[0] = 0.0;
pp->offset4d[1] = 0.0;
pp->offset4d[2] = 0.0;
@@ -1205,6 +1411,18 @@ static void display_projectiveplane(ModeInfo *mi)
if (pp->umove < 0.0)
pp->umove += 2.0*M_PI;
}
+ if (pp->change_colors)
+ {
+ pp->rho += DRHO;
+ if (pp->rho >= 360.0)
+ pp->rho -= 360.0;
+ pp->sigma += DSIGMA;
+ if (pp->sigma >= 360.0)
+ pp->sigma -= 360.0;
+ pp->tau += DTAU;
+ if (pp->tau >= 360.0)
+ pp->tau -= 360.0;
+ }
}
glMatrixMode(GL_PROJECTION);
@@ -1383,6 +1601,10 @@ ENTRYPOINT void init_projectiveplane(ModeInfo *mi)
{
pp->colors = random() % NUM_COLORS;
}
+ else if (!strcasecmp(color_mode,"one-sided"))
+ {
+ pp->colors = COLORS_ONESIDED;
+ }
else if (!strcasecmp(color_mode,"two-sided"))
{
pp->colors = COLORS_TWOSIDED;
@@ -1404,6 +1626,8 @@ ENTRYPOINT void init_projectiveplane(ModeInfo *mi)
pp->colors = random() % NUM_COLORS;
}
+ pp->change_colors = change_colors;
+
/* Set the view mode. */
if (!strcasecmp(view_mode,"random"))
{
diff --git a/hacks/glx/projectiveplane.man b/hacks/glx/projectiveplane.man
index 8adea81..7df253d 100644
--- a/hacks/glx/projectiveplane.man
+++ b/hacks/glx/projectiveplane.man
@@ -19,9 +19,11 @@ projectiveplane - Draws a 4d embedding of the real projective plane.
[\-distance-bands]
[\-direction-bands]
[\-colors \fIcolor-scheme\fP]
+[\-onesided-colors]
[\-twosided-colors]
[\-distance-colors]
[\-direction-colors]
+[\-change-colors]
[\-depth-colors]
[\-view-mode \fIview-mode\fP]
[\-walk]
@@ -47,12 +49,13 @@ The \fIprojectiveplane\fP program shows a 4d embedding of the real
projective plane. You can walk on the projective plane, see it turn
in 4d, or walk on it while it turns in 4d. The fact that the surface
is an embedding of the real projective plane in 4d can be seen in the
-depth colors mode: set all rotation speeds to 0 and the projection
-mode to 4d orthographic projection. In its default orientation, the
-embedding of the real projective plane will then project to the Roman
-surface, which has three lines of self-intersection. However, at the
-three lines of self-intersection the parts of the surface that
-intersect have different colors, i.e., different 4d depths.
+depth colors mode (using static colors): set all rotation speeds to 0
+and the projection mode to 4d orthographic projection. In its default
+orientation, the embedding of the real projective plane will then
+project to the Roman surface, which has three lines of
+self-intersection. However, at the three lines of self-intersection
+the parts of the surface that intersect have different colors, i.e.,
+different 4d depths.
.PP
The real projective plane is a non-orientable surface. To make this
apparent, the two-sided color mode can be used. Alternatively,
@@ -65,26 +68,27 @@ non-orientable).
The real projective plane is a model for the projective geometry in 2d
space. One point can be singled out as the origin. A line can be
singled out as the line at infinity, i.e., a line that lies at an
-infinite distance to the origin. The line at infinity is
-topologically a circle. Points on the line at infinity are also used
-to model directions in projective geometry. The origin can be
-visualized in different manners. When using distance colors, the
-origin is the point that is displayed as fully saturated red, which is
-easier to see as the center of the reddish area on the projective
-plane. Alternatively, when using distance bands, the origin is the
-center of the only band that projects to a disk. When using direction
-bands, the origin is the point where all direction bands collapse to a
-point. Finally, when orientation markers are being displayed, the
-origin the the point where all orientation markers are compressed to a
-point. The line at infinity can also be visualized in different ways.
-When using distance colors, the line at infinity is the line that is
-displayed as fully saturated magenta. When two-sided colors are used,
-the line at infinity lies at the points where the red and green
-"sides" of the projective plane meet (of course, the real projective
-plane only has one side, so this is a design choice of the
-visualization). Alternatively, when orientation markers are being
-displayed, the line at infinity is the place where the orientation
-markers change their orientation.
+infinite distance to the origin. The line at infinity, like all lines
+in the projective plane, is topologically a circle. Points on the
+line at infinity are also used to model directions in projective
+geometry. The origin can be visualized in different manners. When
+using distance colors (and using static colors), the origin is the
+point that is displayed as fully saturated red, which is easier to see
+as the center of the reddish area on the projective plane.
+Alternatively, when using distance bands, the origin is the center of
+the only band that projects to a disk. When using direction bands,
+the origin is the point where all direction bands collapse to a point.
+Finally, when orientation markers are being displayed, the origin the
+the point where all orientation markers are compressed to a point.
+The line at infinity can also be visualized in different ways. When
+using distance colors (and using static colors), the line at infinity
+is the line that is displayed as fully saturated magenta. When
+two-sided (and static) colors are used, the line at infinity lies at
+the points where the red and green "sides" of the projective plane
+meet (of course, the real projective plane only has one side, so this
+is a design choice of the visualization). Alternatively, when
+orientation markers are being displayed, the line at infinity is the
+place where the orientation markers change their orientation.
.PP
Note that when the projective plane is displayed with bands, the
orientation markers are placed in the middle of the bands. For
@@ -134,27 +138,32 @@ origin) is a Moebius strip, which also shows that the projective plane
is non-orientable.
.PP
Finally, the colors with with the projective plane is drawn can be set
-to two-sided, distance, direction, or depth. In two-sided mode, the
-projective plane is drawn with red on one "side" and green on the
-"other side". As described above, the projective plane only has one
-side, so the color jumps from red to green along the line at infinity.
-This mode enables you to see that the projective plane is
-non-orientable. In distance mode, the projective plane is displayed
-with fully saturated colors that depend on the distance of the points
-on the projective plane to the origin. The origin is displayed in
-red, the line at infinity is displayed in magenta. If the projective
-plane is displayed as distance bands, each band will be displayed with
-a different color. In direction mode, the projective plane is
-displayed with fully saturated colors that depend on the angle of the
-points on the projective plane with respect to the origin. Angles in
-opposite directions to the origin (e.g., 15 and 205 degrees) are
-displayed in the same color since they are projectively equivalent.
-If the projective plane is displayed as direction bands, each band
-will be displayed with a different color. Finally, in depth mode the
-projective plane with colors chosen depending on the 4d "depth" (i.e.,
-the w coordinate) of the points on the projective plane at its default
-orientation in 4d. As discussed above, this mode enables you to see
-that the projective plane does not intersect itself in 4d.
+to one-sided, two-sided, distance, direction, or depth. In one-sided
+mode, the projective plane is drawn with the same color on both
+"sides." In two-sided mode (using static colors), the projective
+plane is drawn with red on one "side" and green on the "other side."
+As described above, the projective plane only has one side, so the
+color jumps from red to green along the line at infinity. This mode
+enables you to see that the projective plane is non-orientable. If
+changing colors are used in two-sided mode, changing complementary
+colors are used on the respective "sides." In distance mode, the
+projective plane is displayed with fully saturated colors that depend
+on the distance of the points on the projective plane to the origin.
+If static colors are used, the origin is displayed in red, while the
+line at infinity is displayed in magenta. If the projective plane is
+displayed as distance bands, each band will be displayed with a
+different color. In direction mode, the projective plane is displayed
+with fully saturated colors that depend on the angle of the points on
+the projective plane with respect to the origin. Angles in opposite
+directions to the origin (e.g., 15 and 205 degrees) are displayed in
+the same color since they are projectively equivalent. If the
+projective plane is displayed as direction bands, each band will be
+displayed with a different color. Finally, in depth mode the
+projective plane is displayed with colors chosen depending on the 4d
+"depth" (i.e., the w coordinate) of the points on the projective plane
+at its default orientation in 4d. As discussed above, this mode
+enables you to see that the projective plane does not intersect itself
+in 4d.
.PP
The rotation speed for each of the six planes around which the
projective plane rotates can be chosen. For the walk-and-turn mode,
@@ -238,18 +247,23 @@ to color the projective plane.
.B \-colors random
Display the projective plane with a random color scheme (default).
.TP 8
+.B \-colors onesided \fP(Shortcut: \fB\-onesided-colors\fP)
+Display the projective plane with a single color.
+.TP 8
.B \-colors twosided \fP(Shortcut: \fB\-twosided-colors\fP)
-Display the projective plane with two colors: red on one "side" and
-green on the "other side." Note that the line at infinity lies at the
+Display the projective plane with two colors: one color one "side" and
+the complementary color on the "other side." For static colors, the
+colors are red and green. Note that the line at infinity lies at the
points where the red and green "sides" of the projective plane meet,
i.e., where the orientation of the projective plane reverses.
.TP 8
.B \-colors distance \fP(Shortcut: \fB\-distance-colors\fP)
Display the projective plane with fully saturated colors that depend
on the distance of the points on the projective plane to the origin.
-The origin is displayed in red, the line at infinity is displayed in
-magenta. If the projective plane is displayed as distance bands, each
-band will be displayed with a different color.
+For static colors, the origin is displayed in red, while the line at
+infinity is displayed in magenta. If the projective plane is
+displayed as distance bands, each band will be displayed with a
+different color.
.TP 8
.B \-colors direction \fP(Shortcut: \fB\-direction-colors\fP)
Display the projective plane with fully saturated colors that depend
@@ -264,6 +278,16 @@ Display the projective plane with colors chosen depending on the 4d
"depth" (i.e., the w coordinate) of the points on the projective plane
at its default orientation in 4d.
.PP
+The following options determine whether the colors with which the
+projective plane is displayed are static or are changing dynamically.
+.TP 8
+.B \-change-colors
+Change the colors with which the projective plane is displayed
+dynamically.
+.TP 8
+.B \-no-change-colors
+Use static colors to display the projective plane (default).
+.PP
The following four options are mutually exclusive. They determine how
to view the projective plane.
.TP 8
@@ -388,7 +412,7 @@ stored in the RESOURCE_MANAGER property.
.BR X (1),
.BR xscreensaver (1)
.SH COPYRIGHT
-Copyright \(co 2005-2014 by Carsten Steger. Permission to use, copy,
+Copyright \(co 2013-2020 by Carsten Steger. Permission to use, copy,
modify, distribute, and sell this software and its documentation for
any purpose is hereby granted without fee, provided that the above
copyright notice appear in all copies and that both that copyright
@@ -397,4 +421,4 @@ No representations are made about the suitability of this software for
any purpose. It is provided "as is" without express or implied
warranty.
.SH AUTHOR
-Carsten Steger <carsten@mirsanmir.org>, 03-oct-2014.
+Carsten Steger <carsten@mirsanmir.org>, 06-jan-2020.
diff --git a/hacks/glx/romanboy.c b/hacks/glx/romanboy.c
index e906082..6957556 100644
--- a/hacks/glx/romanboy.c
+++ b/hacks/glx/romanboy.c
@@ -3,10 +3,10 @@
smoothly between the Roman surface and the Boy surface. */
#if 0
-static const char sccsid[] = "@(#)romanboy.c 1.1 14/10/03 xlockmore";
+static const char sccsid[] = "@(#)romanboy.c 1.1 03/10/14 xlockmore";
#endif
-/* Copyright (c) 2013-2014 Carsten Steger <carsten@mirsanmir.org>. */
+/* Copyright (c) 2014-2020 Carsten Steger <carsten@mirsanmir.org>. */
/*
* Permission to use, copy, modify, and distribute this software and its
@@ -22,15 +22,16 @@ static const char sccsid[] = "@(#)romanboy.c 1.1 14/10/03 xlockmore";
* other special, indirect and consequential damages.
*
* REVISION HISTORY:
- * C. Steger - 14/10/03: Initial version
+ * C. Steger - 03/10/14: Initial version
+ * C. Steger - 06/01/20: Added the changing colors mode.
*/
/*
- * This program shows a 3d immersion of the real projective plane
- * that smoothly deforms between the Roman surface and the Boy
- * surface. You can walk on the projective plane or turn in 3d. The
- * smooth deformation (homotopy) between these two famous immersions
- * of the real projective plane was constructed by François Apéry.
+ * This program shows a 3d immersion of the real projective plane that
+ * smoothly deforms between the Roman surface and the Boy surface.
+ * You can walk on the projective plane or turn in 3d. The smooth
+ * deformation (homotopy) between these two famous immersions of the
+ * real projective plane was constructed by François Apéry.
*
* The real projective plane is a non-orientable surface. To make
* this apparent, the two-sided color mode can be used.
@@ -43,10 +44,11 @@ static const char sccsid[] = "@(#)romanboy.c 1.1 14/10/03 xlockmore";
* The real projective plane is a model for the projective geometry in
* 2d space. One point can be singled out as the origin. A line can
* be singled out as the line at infinity, i.e., a line that lies at
- * an infinite distance to the origin. The line at infinity is
- * topologically a circle. Points on the line at infinity are also
- * used to model directions in projective geometry. The origin can be
- * visualized in different manners. When using distance colors, the
+ * an infinite distance to the origin. The line at infinity, like all
+ * lines in the projective plane, is topologically a circle. Points
+ * on the line at infinity are also used to model directions in
+ * projective geometry. The origin can be visualized in different
+ * manners. When using distance colors (and using static colors), the
* origin is the point that is displayed as fully saturated red, which
* is easier to see as the center of the reddish area on the
* projective plane. Alternatively, when using distance bands, the
@@ -56,14 +58,15 @@ static const char sccsid[] = "@(#)romanboy.c 1.1 14/10/03 xlockmore";
* markers are being displayed, the origin the the point where all
* orientation markers are compressed to a point. The line at
* infinity can also be visualized in different ways. When using
- * distance colors, the line at infinity is the line that is displayed
- * as fully saturated magenta. When two-sided colors are used, the
- * line at infinity lies at the points where the red and green "sides"
- * of the projective plane meet (of course, the real projective plane
- * only has one side, so this is a design choice of the
- * visualization). Alternatively, when orientation markers are being
- * displayed, the line at infinity is the place where the orientation
- * markers change their orientation.
+ * distance colors (and using static colors), the line at infinity is
+ * the line that is displayed as fully saturated magenta. When
+ * two-sided (and static) colors are used, the line at infinity lies
+ * at the points where the red and green "sides" of the projective
+ * plane meet (of course, the real projective plane only has one side,
+ * so this is a design choice of the visualization). Alternatively,
+ * when orientation markers are being displayed, the line at infinity
+ * is the place where the orientation markers change their
+ * orientation.
*
* Note that when the projective plane is displayed with bands, the
* orientation markers are placed in the middle of the bands. For
@@ -100,24 +103,28 @@ static const char sccsid[] = "@(#)romanboy.c 1.1 14/10/03 xlockmore";
* projective plane is non-orientable.
*
* Finally, the colors with with the projective plane is drawn can be
- * set to two-sided, distance, or direction. In two-sided mode, the
- * projective plane is drawn with red on one "side" and green on the
- * "other side". As described above, the projective plane only has
- * one side, so the color jumps from red to green along the line at
- * infinity. This mode enables you to see that the projective plane
- * is non-orientable. In distance mode, the projective plane is
- * displayed with fully saturated colors that depend on the distance
- * of the points on the projective plane to the origin. The origin is
- * displayed in red, the line at infinity is displayed in magenta. If
- * the projective plane is displayed as distance bands, each band will
- * be displayed with a different color. In direction mode, the
- * projective plane is displayed with fully saturated colors that
- * depend on the angle of the points on the projective plane with
- * respect to the origin. Angles in opposite directions to the origin
- * (e.g., 15 and 205 degrees) are displayed in the same color since
- * they are projectively equivalent. If the projective plane is
- * displayed as direction bands, each band will be displayed with a
- * different color.
+ * set to one-sided, two-sided, distance, or direction. In one-sided
+ * mode, the projective plane is drawn with the same color on both
+ * "sides." In two-sided mode (using static colors), the projective
+ * plane is drawn with red on one "side" and green on the "other
+ * side." As described above, the projective plane only has one side,
+ * so the color jumps from red to green along the line at infinity.
+ * This mode enables you to see that the projective plane is
+ * non-orientable. If changing colors are used in two-sided mode,
+ * changing complementary colors are used on the respective "sides."
+ * In distance mode, the projective plane is displayed with fully
+ * saturated colors that depend on the distance of the points on the
+ * projective plane to the origin. If static colors are used, the
+ * origin is displayed in red, while the line at infinity is displayed
+ * in magenta. If the projective plane is displayed as distance
+ * bands, each band will be displayed with a different color. In
+ * direction mode, the projective plane is displayed with fully
+ * saturated colors that depend on the angle of the points on the
+ * projective plane with respect to the origin. Angles in opposite
+ * directions to the origin (e.g., 15 and 205 degrees) are displayed
+ * in the same color since they are projectively equivalent. If the
+ * projective plane is displayed as direction bands, each band will be
+ * displayed with a different color.
*
* The rotation speed for each of the three coordinate axes around
* which the projective plane rotates can be chosen.
@@ -172,10 +179,11 @@ static const char sccsid[] = "@(#)romanboy.c 1.1 14/10/03 xlockmore";
#define APPEARANCE_DIRECTION_BANDS 2
#define NUM_APPEARANCES 3
-#define COLORS_TWOSIDED 0
-#define COLORS_DISTANCE 1
-#define COLORS_DIRECTION 2
-#define NUM_COLORS 3
+#define COLORS_ONESIDED 0
+#define COLORS_TWOSIDED 1
+#define COLORS_DISTANCE 2
+#define COLORS_DIRECTION 3
+#define NUM_COLORS 4
#define VIEW_WALK 0
#define VIEW_TURN 1
@@ -190,6 +198,7 @@ static const char sccsid[] = "@(#)romanboy.c 1.1 14/10/03 xlockmore";
#define DEF_COLORS "random"
#define DEF_VIEW_MODE "random"
#define DEF_MARKS "False"
+#define DEF_CHANGE_COLORS "False"
#define DEF_DEFORM "True"
#define DEF_PROJECTION "random"
#define DEF_SPEEDX "1.1"
@@ -239,6 +248,7 @@ static char *color_mode;
static char *view_mode;
static Bool marks;
static Bool deform;
+static Bool change_colors;
static char *proj;
static float speed_x;
static float speed_y;
@@ -261,16 +271,19 @@ static XrmOptionDescRec opts[] =
{"-distance-bands", ".appearance", XrmoptionNoArg, "distance-bands" },
{"-direction-bands", ".appearance", XrmoptionNoArg, "direction-bands" },
{"-colors", ".colors", XrmoptionSepArg, 0 },
+ {"-onesided-colors", ".colors", XrmoptionNoArg, "one-sided" },
{"-twosided-colors", ".colors", XrmoptionNoArg, "two-sided" },
{"-distance-colors", ".colors", XrmoptionNoArg, "distance" },
{"-direction-colors", ".colors", XrmoptionNoArg, "direction" },
+ {"-change-colors", ".changeColors", XrmoptionNoArg, "on"},
+ {"+change-colors", ".changeColors", XrmoptionNoArg, "off"},
{"-view-mode", ".viewMode", XrmoptionSepArg, 0 },
{"-walk", ".viewMode", XrmoptionNoArg, "walk" },
{"-turn", ".viewMode", XrmoptionNoArg, "turn" },
- {"-deform", ".deform", XrmoptionNoArg, "on"},
- {"+deform", ".deform", XrmoptionNoArg, "off"},
- {"-orientation-marks", ".marks", XrmoptionNoArg, "on"},
- {"+orientation-marks", ".marks", XrmoptionNoArg, "off"},
+ {"-deform", ".deform", XrmoptionNoArg, "on"},
+ {"+deform", ".deform", XrmoptionNoArg, "off"},
+ {"-orientation-marks", ".marks", XrmoptionNoArg, "on"},
+ {"+orientation-marks", ".marks", XrmoptionNoArg, "off"},
{"-projection", ".projection", XrmoptionSepArg, 0 },
{"-perspective", ".projection", XrmoptionNoArg, "perspective" },
{"-orthographic", ".projection", XrmoptionNoArg, "orthographic" },
@@ -291,6 +304,7 @@ static argtype vars[] =
{ &mode, "displayMode", "DisplayMode", DEF_DISPLAY_MODE, t_String },
{ &appear, "appearance", "Appearance", DEF_APPEARANCE, t_String },
{ &color_mode, "colors", "Colors", DEF_COLORS, t_String },
+ { &change_colors, "changeColors", "ChangeColors", DEF_CHANGE_COLORS, t_Bool },
{ &view_mode, "viewMode", "ViewMode", DEF_VIEW_MODE, t_String },
{ &deform, "deform", "Deform", DEF_DEFORM, t_Bool },
{ &marks, "marks", "Marks", DEF_MARKS, t_Bool },
@@ -312,6 +326,11 @@ ENTRYPOINT ModeSpecOpt romanboy_opts =
/* Offset by which we walk above the projective plane */
#define DELTAY 0.01
+/* Color change speeds */
+#define DRHO 0.7
+#define DSIGMA 1.1
+#define DTAU 1.7
+
/* Number of subdivisions of the projective plane */
#define NUMU 64
#define NUMV 128
@@ -327,11 +346,14 @@ typedef struct {
int display_mode;
int appearance;
int colors;
+ Bool change_colors;
int view;
int projection;
Bool marks;
/* 3D rotation angles */
float alpha, beta, delta;
+ /* Color rotation angles */
+ float rho, sigma, tau;
/* Movement parameters */
float umove, vmove, dumove, dvmove;
int side, dir;
@@ -472,54 +494,80 @@ static void quat_to_rotmat(float p[4], float m[3][3])
/* Compute a fully saturated and bright color based on an angle. */
-static void color(romanboystruct *pp, double angle, float col[4])
+static void color(romanboystruct *pp, double angle, float mat[3][3],
+ float col[4])
{
int s;
- double t;
+ double t, ca, sa;
+ float m;
- if (pp->colors == COLORS_TWOSIDED)
- return;
+ if (!pp->change_colors)
+ {
+ if (pp->colors == COLORS_ONESIDED || pp->colors == COLORS_TWOSIDED)
+ return;
- if (angle >= 0.0)
- angle = fmod(angle,2.0*M_PI);
- else
- angle = fmod(angle,-2.0*M_PI);
- s = floor(angle/(M_PI/3));
- t = angle/(M_PI/3)-s;
- if (s >= 6)
- s = 0;
- switch (s)
+ if (angle >= 0.0)
+ angle = fmod(angle,2.0*M_PI);
+ else
+ angle = fmod(angle,-2.0*M_PI);
+ s = floor(angle/(M_PI/3));
+ t = angle/(M_PI/3)-s;
+ if (s >= 6)
+ s = 0;
+ switch (s)
+ {
+ case 0:
+ col[0] = 1.0;
+ col[1] = t;
+ col[2] = 0.0;
+ break;
+ case 1:
+ col[0] = 1.0-t;
+ col[1] = 1.0;
+ col[2] = 0.0;
+ break;
+ case 2:
+ col[0] = 0.0;
+ col[1] = 1.0;
+ col[2] = t;
+ break;
+ case 3:
+ col[0] = 0.0;
+ col[1] = 1.0-t;
+ col[2] = 1.0;
+ break;
+ case 4:
+ col[0] = t;
+ col[1] = 0.0;
+ col[2] = 1.0;
+ break;
+ case 5:
+ col[0] = 1.0;
+ col[1] = 0.0;
+ col[2] = 1.0-t;
+ break;
+ }
+ }
+ else /* pp->change_colors */
{
- case 0:
- col[0] = 1.0;
- col[1] = t;
- col[2] = 0.0;
- break;
- case 1:
- col[0] = 1.0-t;
- col[1] = 1.0;
- col[2] = 0.0;
- break;
- case 2:
- col[0] = 0.0;
- col[1] = 1.0;
- col[2] = t;
- break;
- case 3:
- col[0] = 0.0;
- col[1] = 1.0-t;
- col[2] = 1.0;
- break;
- case 4:
- col[0] = t;
- col[1] = 0.0;
- col[2] = 1.0;
- break;
- case 5:
- col[0] = 1.0;
- col[1] = 0.0;
- col[2] = 1.0-t;
- break;
+ if (pp->colors == COLORS_ONESIDED || pp->colors == COLORS_TWOSIDED)
+ {
+ col[0] = mat[0][2];
+ col[1] = mat[1][2];
+ col[2] = mat[2][2];
+ }
+ else
+ {
+ ca = cos(angle);
+ sa = sin(angle);
+ col[0] = ca*mat[0][0]+sa*mat[0][1];
+ col[1] = ca*mat[1][0]+sa*mat[1][1];
+ col[2] = ca*mat[2][0]+sa*mat[2][1];
+ }
+ m = 0.5f/fmaxf(fmaxf(fabsf(col[0]),fabsf(col[1])),fabsf(col[2]));
+ col[0] = m*col[0]+0.5f;
+ col[1] = m*col[1]+0.5f;
+ col[2] = m*col[2]+0.5f;
}
if (pp->display_mode == DISP_TRANSPARENT)
col[3] = 0.7;
@@ -550,10 +598,13 @@ static void setup_roman_boy_color_texture(ModeInfo *mi, double umin,
else
u = ur*j/numu+umin;
v = vr*i/numv+vmin;
- if (pp->colors == COLORS_DIRECTION)
- color(pp,2.0*M_PI-fmod(2.0*u,2.0*M_PI),&pp->col[4*k]);
- else /* pp->colors == COLORS_DISTANCE */
- color(pp,v*(5.0/6.0),&pp->col[4*k]);
+ if (!pp->change_colors)
+ {
+ if (pp->colors == COLORS_DIRECTION)
+ color(pp,2.0*M_PI-fmod(2.0*u,2.0*M_PI),NULL,&pp->col[4*k]);
+ else /* pp->colors == COLORS_DISTANCE */
+ color(pp,v*(5.0/6.0),NULL,&pp->col[4*k]);
+ }
pp->tex[2*k+0] = -16*g*u/(2.0*M_PI);
if (pp->appearance == APPEARANCE_DISTANCE_BANDS)
pp->tex[2*k+1] = 32*v/(2.0*M_PI)-0.5;
@@ -569,11 +620,14 @@ static int roman_boy(ModeInfo *mi, double umin, double umax,
double vmin, double vmax, int numu, int numv)
{
int polys = 0;
- static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
- static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
- static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
- static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
- float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[3][3];
+ static const GLfloat mat_diff_red[] = { 1.0, 0.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_green[] = { 0.0, 1.0, 0.0, 1.0 };
+ static const GLfloat mat_diff_oneside[] = { 0.9, 0.4, 0.3, 1.0 };
+ static const GLfloat mat_diff_trans_red[] = { 1.0, 0.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
+ static const GLfloat mat_diff_trans_oneside[] = { 0.9, 0.4, 0.3, 0.7 };
+ float mat_diff_dyn[4], mat_diff_dyn_compl[4];
+ float p[3], pu[3], pv[3], pm[3], n[3], b[3], mat[3][3], matc[3][3];
int i, j, k, l, m, o, g;
double u, v, ur, vr, oz;
double xx[3], xxu[3], xxv[3];
@@ -591,6 +645,10 @@ static int roman_boy(ModeInfo *mi, double umin, double umax,
r = 1.0+d*d*(1.0/2.0+d*d*(1.0/6.0+d*d*(1.0/3.0)));
radius = 1.0/r;
oz = 0.5*r;
+
+ if (pp->change_colors)
+ rotateall(pp->rho,pp->sigma,pp->tau,matc);
+
if (pp->view == VIEW_WALK)
{
u = pp->umove;
@@ -740,18 +798,54 @@ static int roman_boy(ModeInfo *mi, double umin, double umax,
mult_rotmat(r2,r1,mat);
}
- if (pp->colors == COLORS_TWOSIDED)
+ if (!pp->change_colors)
{
- glColor3fv(mat_diff_red);
- if (pp->display_mode == DISP_TRANSPARENT)
+ if (pp->colors == COLORS_ONESIDED)
{
- glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
- glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ glColor3fv(mat_diff_oneside);
+ if (pp->display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_trans_oneside);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
+ mat_diff_oneside);
+ }
}
- else
+ else if (pp->colors == COLORS_TWOSIDED)
{
- glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
- glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ glColor3fv(mat_diff_red);
+ if (pp->display_mode == DISP_TRANSPARENT)
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
+ }
+ else
+ {
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
+ }
+ }
+ }
+ else /* pp->change_colors */
+ {
+ color(pp,0.0,matc,mat_diff_dyn);
+ if (pp->colors == COLORS_ONESIDED)
+ {
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ }
+ else if (pp->colors == COLORS_TWOSIDED)
+ {
+ mat_diff_dyn_compl[0] = 1.0f-mat_diff_dyn[0];
+ mat_diff_dyn_compl[1] = 1.0f-mat_diff_dyn[1];
+ mat_diff_dyn_compl[2] = 1.0f-mat_diff_dyn[2];
+ mat_diff_dyn_compl[3] = mat_diff_dyn[3];
+ glColor3fv(mat_diff_dyn);
+ glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
+ glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn_compl);
}
}
glBindTexture(GL_TEXTURE_2D,pp->tex_name);
@@ -772,6 +866,14 @@ static int roman_boy(ModeInfo *mi, double umin, double umax,
o = i*(numu+1)+j;
u = ur*j/numu+umin;
v = vr*i/numv+vmin;
+ if (pp->change_colors)
+ {
+ /* Compute the colors dynamically. */
+ if (pp->colors == COLORS_DIRECTION)
+ color(pp,2.0*M_PI-fmod(2.0*u,2.0*M_PI),matc,&pp->col[4*o]);
+ else if (pp->colors == COLORS_DISTANCE)
+ color(pp,v*(5.0/6.0),matc,&pp->col[4*o]);
+ }
if (g & 1)
v = 0.5*M_PI-0.25*v;
else
@@ -872,6 +974,14 @@ static int roman_boy(ModeInfo *mi, double umin, double umax,
o = i*(numu+1)+j;
u = -ur*j/numu+umin;
v = vr*i/numv+vmin;
+ if (pp->change_colors)
+ {
+ /* Compute the colors dynamically. */
+ if (pp->colors == COLORS_DIRECTION)
+ color(pp,2.0*M_PI-fmod(2.0*u,2.0*M_PI),matc,&pp->col[4*o]);
+ else if (pp->colors == COLORS_DISTANCE)
+ color(pp,v*(5.0/6.0),matc,&pp->col[4*o]);
+ }
if (g & 1)
v = 0.5*M_PI-0.25*v;
else
@@ -981,7 +1091,7 @@ static int roman_boy(ModeInfo *mi, double umin, double umax,
m = j;
o = l*(numu+1)+m;
glTexCoord2fv(&pp->tex[2*o]);
- if (pp->colors != COLORS_TWOSIDED)
+ if (pp->colors != COLORS_ONESIDED && pp->colors != COLORS_TWOSIDED)
{
glColor3fv(&pp->col[4*o]);
glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
@@ -1013,7 +1123,7 @@ static int roman_boy(ModeInfo *mi, double umin, double umax,
m = (j+k);
o = l*(numu+1)+m;
glTexCoord2fv(&pp->tex[2*o]);
- if (pp->colors != COLORS_TWOSIDED)
+ if (pp->colors != COLORS_ONESIDED && pp->colors != COLORS_TWOSIDED)
{
glColor3fv(&pp->col[4*o]);
glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
@@ -1046,7 +1156,7 @@ static void gen_texture(ModeInfo *mi)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
- glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,TEX_DIMENSION,TEX_DIMENSION,0,
+ glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,TEX_DIMENSION,TEX_DIMENSION,0,
GL_LUMINANCE,GL_UNSIGNED_BYTE,texture);
}
@@ -1096,6 +1206,10 @@ static void init(ModeInfo *mi)
pp->dd = init_deform*0.001;
pp->defdir = -1;
+ pp->rho = frand(360.0);
+ pp->sigma = frand(360.0);
+ pp->tau = frand(360.0);
+
pp->offset3d[0] = 0.0;
pp->offset3d[1] = 0.0;
pp->offset3d[2] = -1.8;
@@ -1241,6 +1355,18 @@ static void display_romanboy(ModeInfo *mi)
if (pp->umove < 0.0)
pp->umove += 2.0*M_PI;
}
+ if (pp->change_colors)
+ {
+ pp->rho += DRHO;
+ if (pp->rho >= 360.0)
+ pp->rho -= 360.0;
+ pp->sigma += DSIGMA;
+ if (pp->sigma >= 360.0)
+ pp->sigma -= 360.0;
+ pp->tau += DTAU;
+ if (pp->tau >= 360.0)
+ pp->tau -= 360.0;
+ }
}
glMatrixMode(GL_PROJECTION);
@@ -1269,16 +1395,10 @@ static void display_romanboy(ModeInfo *mi)
ENTRYPOINT void reshape_romanboy(ModeInfo *mi, int width, int height)
{
romanboystruct *pp = &romanboy[MI_SCREEN(mi)];
- int y = 0;
-
- if (width > height * 5) { /* tiny window: show middle */
- height = width;
- y = -height/2;
- }
pp->WindW = (GLint)width;
pp->WindH = (GLint)height;
- glViewport(0,y,width,height);
+ glViewport(0,0,width,height);
pp->aspect = (GLfloat)width/(GLfloat)height;
}
@@ -1402,6 +1522,10 @@ ENTRYPOINT void init_romanboy(ModeInfo *mi)
{
pp->colors = random() % NUM_COLORS;
}
+ else if (!strcasecmp(color_mode,"one-sided"))
+ {
+ pp->colors = COLORS_ONESIDED;
+ }
else if (!strcasecmp(color_mode,"two-sided"))
{
pp->colors = COLORS_TWOSIDED;
@@ -1419,6 +1543,8 @@ ENTRYPOINT void init_romanboy(ModeInfo *mi)
pp->colors = random() % NUM_COLORS;
}
+ pp->change_colors = change_colors;
+
/* Set the view mode. */
if (!strcasecmp(view_mode,"random"))
{
diff --git a/hacks/glx/romanboy.man b/hacks/glx/romanboy.man
index 2957a0e..e4e947b 100644
--- a/hacks/glx/romanboy.man
+++ b/hacks/glx/romanboy.man
@@ -20,9 +20,11 @@ smoothly deforms between the Roman surface and the Boy surface.
[\-distance-bands]
[\-direction-bands]
[\-colors \fIcolor-scheme\fP]
+[\-onesided-colors]
[\-twosided-colors]
[\-distance-colors]
[\-direction-colors]
+[\-change-colors]
[\-view-mode \fIview-mode\fP]
[\-walk]
[\-turn]
@@ -59,26 +61,27 @@ non-orientable).
The real projective plane is a model for the projective geometry in 2d
space. One point can be singled out as the origin. A line can be
singled out as the line at infinity, i.e., a line that lies at an
-infinite distance to the origin. The line at infinity is
-topologically a circle. Points on the line at infinity are also used
-to model directions in projective geometry. The origin can be
-visualized in different manners. When using distance colors, the
-origin is the point that is displayed as fully saturated red, which is
-easier to see as the center of the reddish area on the projective
-plane. Alternatively, when using distance bands, the origin is the
-center of the only band that projects to a disk. When using direction
-bands, the origin is the point where all direction bands collapse to a
-point. Finally, when orientation markers are being displayed, the
-origin the the point where all orientation markers are compressed to a
-point. The line at infinity can also be visualized in different ways.
-When using distance colors, the line at infinity is the line that is
-displayed as fully saturated magenta. When two-sided colors are used,
-the line at infinity lies at the points where the red and green
-"sides" of the projective plane meet (of course, the real projective
-plane only has one side, so this is a design choice of the
-visualization). Alternatively, when orientation markers are being
-displayed, the line at infinity is the place where the orientation
-markers change their orientation.
+infinite distance to the origin. The line at infinity, like all lines
+in the projective plane, is topologically a circle. Points on the
+line at infinity are also used to model directions in projective
+geometry. The origin can be visualized in different manners. When
+using distance colors (and using static colors), the origin is the
+point that is displayed as fully saturated red, which is easier to see
+as the center of the reddish area on the projective plane.
+Alternatively, when using distance bands, the origin is the center of
+the only band that projects to a disk. When using direction bands,
+the origin is the point where all direction bands collapse to a point.
+Finally, when orientation markers are being displayed, the origin the
+the point where all orientation markers are compressed to a point.
+The line at infinity can also be visualized in different ways. When
+using distance colors (and using static colors), the line at infinity
+is the line that is displayed as fully saturated magenta. When
+two-sided (and static) colors are used, the line at infinity lies at
+the points where the red and green "sides" of the projective plane
+meet (of course, the real projective plane only has one side, so this
+is a design choice of the visualization). Alternatively, when
+orientation markers are being displayed, the line at infinity is the
+place where the orientation markers change their orientation.
.PP
Note that when the projective plane is displayed with bands, the
orientation markers are placed in the middle of the bands. For
@@ -114,23 +117,27 @@ origin) is a Moebius strip, which also shows that the projective plane
is non-orientable.
.PP
Finally, the colors with with the projective plane is drawn can be set
-to two-sided, distance, or direction. In two-sided mode, the
-projective plane is drawn with red on one "side" and green on the
-"other side". As described above, the projective plane only has one
-side, so the color jumps from red to green along the line at infinity.
-This mode enables you to see that the projective plane is
-non-orientable. In distance mode, the projective plane is displayed
-with fully saturated colors that depend on the distance of the points
-on the projective plane to the origin. The origin is displayed in
-red, the line at infinity is displayed in magenta. If the projective
-plane is displayed as distance bands, each band will be displayed with
-a different color. In direction mode, the projective plane is
-displayed with fully saturated colors that depend on the angle of the
-points on the projective plane with respect to the origin. Angles in
-opposite directions to the origin (e.g., 15 and 205 degrees) are
-displayed in the same color since they are projectively equivalent.
-If the projective plane is displayed as direction bands, each band
-will be displayed with a different color.
+to one-sided, two-sided, distance, or direction. In one-sided mode,
+the projective plane is drawn with the same color on both "sides." In
+two-sided mode (using static colors), the projective plane is drawn
+with red on one "side" and green on the "other side." As described
+above, the projective plane only has one side, so the color jumps from
+red to green along the line at infinity. This mode enables you to see
+that the projective plane is non-orientable. If changing colors are
+used in two-sided mode, changing complementary colors are used on the
+respective "sides." In distance mode, the projective plane is
+displayed with fully saturated colors that depend on the distance of
+the points on the projective plane to the origin. If static colors
+are used, the origin is displayed in red, while the line at infinity
+is displayed in magenta. If the projective plane is displayed as
+distance bands, each band will be displayed with a different color.
+In direction mode, the projective plane is displayed with fully
+saturated colors that depend on the angle of the points on the
+projective plane with respect to the origin. Angles in opposite
+directions to the origin (e.g., 15 and 205 degrees) are displayed in
+the same color since they are projectively equivalent. If the
+projective plane is displayed as direction bands, each band will be
+displayed with a different color.
.PP
The rotation speed for each of the three coordinate axes around which
the projective plane rotates can be chosen.
@@ -230,18 +237,23 @@ to color the projective plane.
.B \-colors random
Display the projective plane with a random color scheme (default).
.TP 8
+.B \-colors onesided \fP(Shortcut: \fB\-onesided-colors\fP)
+Display the projective plane with a single color.
+.TP 8
.B \-colors twosided \fP(Shortcut: \fB\-twosided-colors\fP)
-Display the projective plane with two colors: red on one "side" and
-green on the "other side." Note that the line at infinity lies at the
+Display the projective plane with two colors: one color one "side" and
+the complementary color on the "other side." For static colors, the
+colors are red and green. Note that the line at infinity lies at the
points where the red and green "sides" of the projective plane meet,
i.e., where the orientation of the projective plane reverses.
.TP 8
.B \-colors distance \fP(Shortcut: \fB\-distance-colors\fP)
Display the projective plane with fully saturated colors that depend
on the distance of the points on the projective plane to the origin.
-The origin is displayed in red, the line at infinity is displayed in
-magenta. If the projective plane is displayed as distance bands, each
-band will be displayed with a different color.
+For static colors, the origin is displayed in red, while the line at
+infinity is displayed in magenta. If the projective plane is
+displayed as distance bands, each band will be displayed with a
+different color.
.TP 8
.B \-colors direction \fP(Shortcut: \fB\-direction-colors\fP)
Display the projective plane with fully saturated colors that depend
@@ -251,6 +263,16 @@ degrees) are displayed in the same color since they are projectively
equivalent. If the projective plane is displayed as direction bands,
each band will be displayed with a different color.
.PP
+The following options determine whether the colors with which the
+projective plane is displayed are static or are changing dynamically.
+.TP 8
+.B \-change-colors
+Change the colors with which the projective plane is displayed
+dynamically.
+.TP 8
+.B \-no-change-colors
+Use static colors to display the projective plane (default).
+.PP
The following three options are mutually exclusive. They determine
how to view the projective plane.
.TP 8
@@ -378,7 +400,7 @@ stored in the RESOURCE_MANAGER property.
.BR X (1),
.BR xscreensaver (1)
.SH COPYRIGHT
-Copyright \(co 2013-2014 by Carsten Steger. Permission to use, copy,
+Copyright \(co 2013-2020 by Carsten Steger. Permission to use, copy,
modify, distribute, and sell this software and its documentation for
any purpose is hereby granted without fee, provided that the above
copyright notice appear in all copies and that both that copyright
@@ -387,4 +409,4 @@ No representations are made about the suitability of this software for
any purpose. It is provided "as is" without express or implied
warranty.
.SH AUTHOR
-Carsten Steger <carsten@mirsanmir.org>, 03-oct-2014.
+Carsten Steger <carsten@mirsanmir.org>, 06-jan-2020.
diff --git a/hacks/glx/sonar-icmp.c b/hacks/glx/sonar-icmp.c
index 961dcf5..658074b 100644
--- a/hacks/glx/sonar-icmp.c
+++ b/hacks/glx/sonar-icmp.c
@@ -1,4 +1,4 @@
-/* sonar, Copyright (c) 1998-2019 Jamie Zawinski and Stephen Martin
+/* sonar, Copyright (c) 1998-2020 Jamie Zawinski and Stephen Martin
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@@ -939,6 +939,7 @@ send_ping (ping_data *pd, const sonar_bogie *b)
struct ICMP *icmph;
const char *token = "org.jwz.xscreensaver.sonar";
char *host_id;
+ struct timeval tval;
unsigned long pcktsiz = (sizeof(struct ICMP) + sizeof(struct timeval) +
sizeof(socklen_t) + pb->addrlen +
@@ -956,12 +957,16 @@ send_ping (ping_data *pd, const sonar_bogie *b)
ICMP_CHECKSUM(icmph) = 0;
ICMP_ID(icmph) = pd->pid;
ICMP_SEQ(icmph) = pd->seq++;
+ /* struct timeval needs alignment, so we first use aligned buffer for
+ gettimeofday() and later copy the result to packet buffer
+ */
# ifdef GETTIMEOFDAY_TWO_ARGS
- gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
+ gettimeofday((struct timeval *) &tval,
(struct timezone *) 0);
# else
- gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]);
+ gettimeofday((struct timeval *) &tval);
# endif
+ memcpy(&packet[sizeof(struct ICMP)], &tval, sizeof tval);
/* We store the sockaddr of the host we're pinging in the packet, and parse
that out of the return packet later (see get_ping() for why).
@@ -1068,7 +1073,7 @@ get_ping (sonar_sensor_data *ssd)
int result;
u_char packet[1024];
struct timeval now;
- struct timeval *then;
+ struct timeval then;
struct ip *ip;
int iphdrlen;
struct ICMP *icmph;
@@ -1132,7 +1137,10 @@ get_ping (sonar_sensor_data *ssd)
ip = (struct ip *) packet;
iphdrlen = IP_HDRLEN(ip) << 2;
icmph = (struct ICMP *) &packet[iphdrlen];
- then = (struct timeval *) &packet[iphdrlen + sizeof(struct ICMP)];
+ /* struct timeval data in packet is not aligned, move the data to
+ the aligned buffer
+ */
+ memcpy(&then, &packet[iphdrlen + sizeof(struct ICMP)], sizeof then);
/* Ignore anything but ICMP Replies */
@@ -1221,7 +1229,7 @@ get_ping (sonar_sensor_data *ssd)
bl = new;
{
- double msec = delta(then, &now) / 1000.0;
+ double msec = delta(&then, &now) / 1000.0;
if (pd->times_p)
{
@@ -1694,7 +1702,7 @@ sonar_init_ping (Display *dpy, char **error_ret, char **desc_ret,
fprintf (stderr, "%s: unable to open icmp socket\n", progname);
/* Disavow privs */
- setuid(getuid());
+ if (setuid(getuid()) == -1) abort();
pd->pid = getpid() & 0xFFFF;
pd->seq = 0;
diff --git a/hacks/glx/sonar.c b/hacks/glx/sonar.c
index ddbb79f..e4e0b86 100644
--- a/hacks/glx/sonar.c
+++ b/hacks/glx/sonar.c
@@ -1,4 +1,4 @@
-/* sonar, Copyright (c) 1998-2018 Jamie Zawinski and Stephen Martin
+/* sonar, Copyright (c) 1998-2020 Jamie Zawinski and Stephen Martin
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@@ -935,7 +935,11 @@ init_sensor (ModeInfo *mi)
ping_arg, ping_timeout, resolve_p, times_p,
debug_p);
else
- setuid(getuid()); /* Disavow privs if not pinging. */
+ { /* Disavow privs if not pinging. */
+ if (setuid(getuid()) == -1) abort();
+ }
+
+ setuid(getuid());
sp->start_time = double_time (); /* for error message timing */
diff --git a/hacks/glx/starwars.c b/hacks/glx/starwars.c
index bccd568..0f1457a 100644
--- a/hacks/glx/starwars.c
+++ b/hacks/glx/starwars.c
@@ -69,6 +69,7 @@
#define DEF_LINES "125"
#define DEF_STEPS "35"
#define DEF_SPIN "0.03"
+#define DEF_SATURATION "0.3"
#define DEF_SIZE "-1"
#define DEF_COLUMNS "-1"
#define DEF_LINE_WRAP "True"
@@ -130,6 +131,7 @@ static sws_configuration *scs = NULL;
static int max_lines;
static int scroll_steps;
static float star_spin;
+static float star_saturation;
static float font_size;
static int target_columns;
static int wrap_p;
@@ -145,9 +147,11 @@ static XrmOptionDescRec opts[] = {
{"-lines", ".lines", XrmoptionSepArg, 0 },
{"-steps", ".steps", XrmoptionSepArg, 0 },
{"-spin", ".spin", XrmoptionSepArg, 0 },
+ {"-saturation", ".saturation",XrmoptionSepArg, 0 },
{"-size", ".size", XrmoptionSepArg, 0 },
{"-columns", ".columns", XrmoptionSepArg, 0 },
/*{"-font", ".font", XrmoptionSepArg, 0 },*/
+ {"-program", ".program", XrmoptionSepArg, 0 },
{"-fade", ".fade", XrmoptionNoArg, "True" },
{"-no-fade", ".fade", XrmoptionNoArg, "False" },
{"-textures", ".textures", XrmoptionNoArg, "True" },
@@ -170,6 +174,7 @@ static argtype vars[] = {
{&max_lines, "lines", "Integer", DEF_LINES, t_Int},
{&scroll_steps, "steps", "Integer", DEF_STEPS, t_Int},
{&star_spin, "spin", "Float", DEF_SPIN, t_Float},
+ {&star_saturation,"saturation","Float", DEF_SATURATION,t_Float},
{&font_size, "size", "Float", DEF_SIZE, t_Float},
{&target_columns, "columns", "Integer", DEF_COLUMNS, t_Int},
{&wrap_p, "lineWrap", "Boolean", DEF_LINE_WRAP, t_Bool},
@@ -587,9 +592,10 @@ init_stars (ModeInfo *mi, int width, int height)
glBegin (GL_POINTS);
for (i = 0; i < nstars / steps; i++)
{
- glColor3f (0.6 + frand(0.3),
- 0.6 + frand(0.3),
- 0.6 + frand(0.3));
+ GLfloat brightness = 0.9 - star_saturation;
+ glColor3f (brightness + frand(star_saturation),
+ brightness + frand(star_saturation),
+ brightness + frand(star_saturation));
glVertex2f (2 * size * (0.5 - frand(1.0)),
2 * size * (0.5 - frand(1.0)));
}