-/* glsnake, Copyright (c) 2001,2002
- * Jamie Wilkinson, Andrew Bennetts, Peter Aylett
- * jaq@spacepants.org, andrew@puzzling.org, peter@ylett.com
- *
- * ported to xscreensaver 15th Jan 2002 by Jamie Wilkinson
- * http://spacepants.org/src/glsnake/
- *
+/* glsnake.c - OpenGL imitation of Rubik's Snake
+ *
+ * (c) 2001-2005 Jamie Wilkinson <jaq@spacepants.org>
+ * (c) 2001-2003 Andrew Bennetts <andrew@puzzling.org>
+ * (c) 2001-2003 Peter Aylett <peter@ylett.com>
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <X11/Intrinsic.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
-extern XtAppContext app;
+/* HAVE_GLUT defined if we're building a standalone glsnake,
+ * and not defined if we're building as an xscreensaver hack */
+#ifdef HAVE_GLUT
+# include <GL/glut.h>
+#else
+# include <GL/gl.h>
+# include <GL/glu.h>
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* angles */
+#define ZERO 0.0
+#define LEFT 90.0
+#define PIN 180.0
+#define RIGHT 270.0
+
+#ifdef HAVE_GETTIMEOFDAY
+# ifdef GETTIMEOFDAY_TWO_ARGS
+
+# include <sys/time.h>
+# include <time.h>
+ typedef struct timeval snaketime;
+# define GETSECS(t) ((t).tv_sec)
+# define GETMSECS(t) ((t).tv_usec/1000)
+
+# else /* !GETTIMEOFDAY_TWO_ARGS */
+
+# include <sys/time.h>
+# include <time.h>
+ typedef struct timeval snaketime;
+# define GETSECS(t) ((t).tv_sec)
+# define GETMSECS(t) ((t).tv_usec/1000)
+
+# endif /* GETTIMEOFDAY_TWO_ARGS */
+
+#else /* !HAVE_GETTIMEOFDAY */
+# ifdef HAVE_FTIME
-#define PROGCLASS "glsnake"
-#define HACK_INIT glsnake_init
-#define HACK_DRAW glsnake_draw
-#define HACK_RESHAPE glsnake_reshape
-#define sws_opts xlockmore_opts
+# include <sys/timeb.h>
+ typedef struct timeb snaketime;
+# define GETSECS(t) ((long)(t).time)
+# define GETMSECS(t) ((t).millitm/1000)
-#define DEF_SPEED "0.05"
+# endif /* HAVE_FTIME */
+#endif /* HAVE_GETTIMEOFDAY */
+
+#include <math.h>
+
+#ifndef M_SQRT1_2 /* Win32 doesn't have this constant */
+#define M_SQRT1_2 0.70710678118654752440084436210485
+#endif
+
+#define NODE_COUNT 24
+
+#ifdef HAVE_GLUT
+#define DEF_YANGVEL 0.10
+#define DEF_ZANGVEL 0.14
+#define DEF_EXPLODE 0.03
+#define DEF_ANGVEL 1.0
+#define DEF_STATICTIME 5000
+#define DEF_ALTCOLOUR 0
+#define DEF_TITLES 1
+#define DEF_INTERACTIVE 0
+#define DEF_ZOOM 25.0
+#define DEF_WIREFRAME 0
+#define DEF_TRANSPARENT 1
+#else
+/* xscreensaver options doobies prefer strings */
+#define DEF_YANGVEL "0.10"
+#define DEF_ZANGVEL "0.14"
#define DEF_EXPLODE "0.03"
-#define DEF_VELOCITY "1.0"
-#define DEF_ACCEL "0.1"
+#define DEF_ANGVEL "1.0"
#define DEF_STATICTIME "5000"
-#define DEF_YSPIN "0.10"
-#define DEF_ZSPIN "0.14"
-#define DEF_SCARYCOLOUR "False"
-#define DEF_LABELS "True"
-
-#define DEFAULTS "*delay: 30000 \n" \
- "*count: 30 \n" \
- "*showFPS: False \n" \
- "*wireframe: False \n" \
- "*speed: " DEF_SPEED " \n" \
- "*explode: " DEF_EXPLODE " \n" \
- "*velocity: " DEF_VELOCITY " \n" \
-/* "*accel: " DEF_ACCEL " \n" */ \
- "*statictime: " DEF_STATICTIME " \n" \
- "*yspin: " DEF_YSPIN " \n" \
- "*zspin: " DEF_ZSPIN " \n" \
- "*scarycolour:" DEF_SCARYCOLOUR " \n" \
- "*labels: " DEF_LABELS " \n" \
- "*labelfont: -*-helvetica-medium-r-*-*-*-120-*\n" \
+#define DEF_ALTCOLOUR "False"
+#define DEF_TITLES "True"
+#define DEF_INTERACTIVE "False"
+#define DEF_ZOOM "25.0"
+#define DEF_WIREFRAME "False"
+#define DEF_TRANSPARENT "True"
+#endif
+
+/* static variables */
+#ifndef HAVE_GLUT
+# include <X11/Intrinsic.h>
+#else
+/* xscreensaver boolean type */
+# define Bool int
+#endif
+
+static GLfloat explode;
+static long statictime;
+static GLfloat yspin = 60.0;
+static GLfloat zspin = -45.0;
+static GLfloat yangvel;
+static GLfloat zangvel;
+static Bool altcolour;
+static Bool titles;
+static Bool interactive;
+static Bool wireframe;
+static Bool transparent;
+static GLfloat zoom;
+static GLfloat angvel;
+
+#ifndef HAVE_GLUT
+/* xscreensaver setup */
+extern XtAppContext app;
+
+#define PROGCLASS "glsnake"
+#define HACK_INIT glsnake_init
+#define HACK_DRAW glsnake_display
+#define HACK_RESHAPE glsnake_reshape
+#define sws_opts xlockmore_opts
+
+
+/* xscreensaver defaults */
+#define DEFAULTS "*delay: 30000 \n" \
+ "*count: 30 \n" \
+ "*showFPS: False \n" \
+ "*labelfont: -*-times-bold-r-normal-*-180-*\n" \
#define countof(x) (sizeof((x))/sizeof((*x)))
#include "xlockmore.h"
-
-#ifdef USE_GL /* whole file */
-
-#include <GL/glu.h>
-#include <sys/timeb.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#define ZERO 0
-#define LEFT 1
-#define PIN 2
-#define RIGHT 3
-
-typedef struct model_s {
- char * name;
- float node[24];
-} model_t;
-
-typedef struct nodeang_s {
- float cur_angle;
- float dest_angle;
-} nodeang_t;
-
-typedef struct {
- GLXContext * glx_context;
-
- int node_list; /* name of the display list */
- int is_cyclic;
- int is_legal;
- int last_turn;
- int selected;
- struct timeb last_iteration;
- struct timeb last_morph;
- int morphing;
- nodeang_t node[24];
- GLfloat roty;
- GLfloat rotz;
- int paused;
- int dragging;
- int m_count;
- int m;
- int cur_model;
- int interactive;
- GLfloat colour_t[3];
- GLfloat colour_i[3];
- GLfloat colour[3];
- model_t * models;
-
- XFontStruct * font;
- GLuint font_list;
-} glsnake_configuration;
-
-static glsnake_configuration *glc = NULL;
-
-static GLfloat speed;
-static GLfloat explode;
-static GLfloat velocity;
-/* static GLfloat accel; */
-static long statictime;
-static GLfloat yspin;
-static GLfloat zspin;
-static Bool scarycolour;
-static Bool labels;
+#include "glxfonts.h"
static XrmOptionDescRec opts[] = {
- { "-speed", ".speed", XrmoptionSepArg, 0 },
- { "-explode", ".explode", XrmoptionSepArg, 0 },
- { "-velocity", ".velocity", XrmoptionSepArg, 0 },
-/* { "-accel", ".accel", XrmoptionSepArg, 0 }, */
- { "-statictime", ".statictime", XrmoptionSepArg, 0 },
- { "-yspin", ".yspin", XrmoptionSepArg, 0 },
- { "-zspin", ".zspin", XrmoptionSepArg, 0 },
- { "-scarycolour", ".scarycolour", XrmoptionNoArg, "True" },
- { "+scarycolour", ".scarycolour", XrmoptionNoArg, "False" },
- { "-labels", ".labels", XrmoptionNoArg, "True" },
- { "+labels", ".labels", XrmoptionNoArg, "False" },
+ { "-explode", ".explode", XrmoptionSepArg, DEF_EXPLODE },
+ { "-angvel", ".angvel", XrmoptionSepArg, DEF_ANGVEL },
+ { "-statictime", ".statictime", XrmoptionSepArg, DEF_STATICTIME },
+ { "-yangvel", ".yangvel", XrmoptionSepArg, DEF_YANGVEL },
+ { "-zangvel", ".zangvel", XrmoptionSepArg, DEF_ZANGVEL },
+ { "-altcolour", ".altcolour", XrmoptionNoArg, (caddr_t) "True" },
+ { "-no-altcolour", ".altcolour", XrmoptionNoArg, (caddr_t) "False" },
+ { "-titles", ".titles", XrmoptionNoArg, (caddr_t) "True" },
+ { "-no-titles", ".titles", XrmoptionNoArg, (caddr_t) "False" },
+ { "-zoom", ".zoom", XrmoptionSepArg, DEF_ZOOM },
+ { "-wireframe", ".wireframe", XrmoptionNoArg, (caddr_t) "true" },
+ { "-no-wireframe", ".wireframe", XrmoptionNoArg, (caddr_t) "false" },
+ { "-transparent", ".transparent", XrmoptionNoArg, (caddr_t) "true" },
+ { "-no-transparent", ".transparent", XrmoptionNoArg, (caddr_t) "false" },
};
static argtype vars[] = {
- {(caddr_t *) &speed, "speed", "Speed", DEF_SPEED, t_Float},
- {(caddr_t *) &explode, "explode", "Explode", DEF_EXPLODE, t_Float},
- {(caddr_t *) &velocity, "velocity", "Velocity", DEF_VELOCITY, t_Float},
-/* {(caddr_t *) &accel, "accel", "Acceleration", DEF_ACCEL, t_Float}, */
- {(caddr_t *) &statictime, "statictime", "Static Time", DEF_STATICTIME, t_Int},
- {(caddr_t *) &yspin, "yspin", "Y Spin", DEF_YSPIN, t_Float},
- {(caddr_t *) &zspin, "zspin", "Z Spin", DEF_ZSPIN, t_Float},
-/* {(caddr_t *) &interactive, "interactive", "Interactive", DEF_INTERACTIVE, t_Bool}, */
- {(caddr_t *) &scarycolour, "scarycolour", "Scary Colour", DEF_SCARYCOLOUR, t_Bool},
- {(caddr_t *) &labels, "labels", "Labels", DEF_LABELS, t_Bool},
+ {&explode, "explode", "Explode", DEF_EXPLODE, t_Float},
+ {&angvel, "angvel", "Angular Velocity", DEF_ANGVEL, t_Float},
+ {&statictime, "statictime", "Static Time", DEF_STATICTIME, t_Int},
+ {&yangvel, "yangvel", "Angular Velocity about Y axis", DEF_YANGVEL, t_Float},
+ {&zangvel, "zangvel", "Angular Velocity about X axis", DEF_ZANGVEL, t_Float},
+ {&interactive, "interactive", "Interactive", DEF_INTERACTIVE, t_Bool},
+ {&altcolour, "altcolour", "Alternate Colour Scheme", DEF_ALTCOLOUR, t_Bool},
+ {&titles, "titles", "Titles", DEF_TITLES, t_Bool},
+ {&zoom, "zoom", "Zoom", DEF_ZOOM, t_Float},
+ {&wireframe, "wireframe", "Wireframe", DEF_WIREFRAME, t_Bool},
+ {&transparent, "transparent", "Transparent!", DEF_TRANSPARENT, t_Bool},
};
-ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
+ModeSpecOpt sws_opts = {(int)countof(opts), opts, (int)countof(vars), vars, NULL};
+#endif
-/* prism models */
-#define VOFFSET 0.045
-float solid_prism_v[][3] = {
- /* first corner, bottom left front */
- { VOFFSET, VOFFSET, 1.0 },
- { VOFFSET, 0.0, 1.0 - VOFFSET },
- { 0.0, VOFFSET, 1.0 - VOFFSET },
- /* second corner, rear */
- { VOFFSET, VOFFSET, 0.00 },
- { VOFFSET, 0.0, VOFFSET },
- { 0.0, VOFFSET, VOFFSET },
- /* third, right front */
- { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 1.0 },
- { 1.0 - VOFFSET / M_SQRT1_2, 0.0, 1.0 - VOFFSET },
- { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, 1.0 - VOFFSET },
- /* fourth, right rear */
- { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 0.0 },
- { 1.0 - VOFFSET / M_SQRT1_2, 0.0, VOFFSET },
- { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, VOFFSET },
- /* fifth, upper front */
- { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 1.0 },
- { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, 1.0 - VOFFSET },
- { 0.0, 1.0 - VOFFSET / M_SQRT1_2, 1.0 - VOFFSET},
- /* sixth, upper rear */
- { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 0.0 },
- { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, VOFFSET },
- { 0.0, 1.0 - VOFFSET / M_SQRT1_2, VOFFSET }
+struct model_s {
+ const char * name;
+ float node[NODE_COUNT];
};
-/* normals */
-float solid_prism_n[][3] = {
- /* corners */
- { -VOFFSET, -VOFFSET, VOFFSET },
- { VOFFSET, -VOFFSET, VOFFSET },
- { -VOFFSET, VOFFSET, VOFFSET },
- { -VOFFSET, -VOFFSET, -VOFFSET },
- { VOFFSET, -VOFFSET, -VOFFSET },
- { -VOFFSET, VOFFSET, -VOFFSET },
- /* edges */
- { -VOFFSET, 0.0, VOFFSET },
- { 0.0, -VOFFSET, VOFFSET },
- { VOFFSET, VOFFSET, VOFFSET },
- { -VOFFSET, 0.0, -VOFFSET },
- { 0.0, -VOFFSET, -VOFFSET },
- { VOFFSET, VOFFSET, -VOFFSET },
- { -VOFFSET, -VOFFSET, 0.0 },
- { VOFFSET, -VOFFSET, 0.0 },
- { -VOFFSET, VOFFSET, 0.0 },
- /* faces */
- { 0.0, 0.0, 1.0 },
- { 0.0, -1.0, 0.0 },
- { M_SQRT1_2, M_SQRT1_2, 0.0 },
- { -1.0, 0.0, 0.0 },
- { 0.0, 0.0, -1.0 }
-};
+struct glsnake_cfg {
+#ifndef HAVE_GLUT
+ GLXContext * glx_context;
+ XFontStruct * font;
+ GLuint font_list;
+#else
+ /* font list number */
+ int font;
+#endif
+
+ /* window id */
+ int window;
+
+ /* is a morph in progress? */
+ int morphing;
+
+ /* has the model been paused? */
+ int paused;
+
+ /* snake metrics */
+ int is_cyclic;
+ int is_legal;
+ float last_turn;
+ int debug;
+
+ /* the shape of the model */
+ float node[NODE_COUNT];
-/* vertices */
-float wire_prism_v[][3] = {
- { 0.0, 0.0, 1.0 },
- { 1.0, 0.0, 1.0 },
- { 0.0, 1.0, 1.0 },
- { 0.0, 0.0, 0.0 },
- { 1.0, 0.0, 0.0 },
- { 0.0, 1.0, 0.0 }
+ /* currently selected node for interactive mode */
+ int selected;
+
+ /* models */
+ unsigned int prev_model;
+ unsigned int next_model;
+
+ /* model morphing */
+ int new_morph;
+
+ /* colours */
+ float colour[2][4];
+ int next_colour;
+ int prev_colour;
+
+ /* timing variables */
+ snaketime last_iteration;
+ snaketime last_morph;
+
+ /* window size */
+ int width, height;
+ int old_width, old_height;
+
+ /* the id of the display lists for drawing a node */
+ GLuint node_solid, node_wire;
+
+ /* is the window fullscreen? */
+ int fullscreen;
};
-/* normals */
-float wire_prism_n[][3] = {
- { 0.0, 0.0, 1.0},
- { 0.0,-1.0, 0.0},
- { M_SQRT1_2, M_SQRT1_2, 0.0},
- {-1.0, 0.0, 0.0},
- { 0.0, 0.0,-1.0}
+#define COLOUR_CYCLIC 0
+#define COLOUR_ACYCLIC 1
+#define COLOUR_INVALID 2
+#define COLOUR_AUTHENTIC 3
+#define COLOUR_ORIGLOGO 4
+
+float colour[][2][4] = {
+ /* cyclic - green */
+ { { 0.4, 0.8, 0.2, 0.6 },
+ { 1.0, 1.0, 1.0, 0.6 } },
+ /* acyclic - blue */
+ { { 0.3, 0.1, 0.9, 0.6 },
+ { 1.0, 1.0, 1.0, 0.6 } },
+ /* invalid - grey */
+ { { 0.3, 0.1, 0.9, 0.6 },
+ { 1.0, 1.0, 1.0, 0.6 } },
+ /* authentic - purple and green */
+ { { 0.38, 0.0, 0.55, 0.7 },
+ { 0.0, 0.5, 0.34, 0.7 } },
+ /* old "authentic" colours from the logo */
+ { { 171/255.0, 0, 1.0, 1.0 },
+ { 46/255.0, 205/255.0, 227/255.0, 1.0 } }
};
-/* default models */
-#define Z 0.0
-#define L 90.0
-#define P 180.0
-#define R 270.0
-
-static model_t default_models[] = {
- { "Ball",
- { R, R, L, L, R, L, R, R, L, R, L, L, R, R, L, L, R, L, R, R, L, R, L }
- },
- { "Snow",
- { R, R, R, R, L, L, L, L, R, R, R, R, L, L, L, L, R, R, R, R, L, L, L }
- },
- { "Propellor",
- { Z, Z, Z, R, L, R, Z, L, Z, Z, Z, R, L, R, Z, L, Z, Z, Z, R, L, R, Z }
- },
- { "Flamingo",
- { Z, P, Z, Z, Z, Z, Z, P, R, R, P, R, L, P, L, R, P, R, R, Z, Z, Z, P }
- },
- { "Cat",
- { Z, P, P, Z, P, P, Z, L, Z, P, P, Z, P, P, Z, P, P, Z, Z, Z, Z, Z, Z }
- },
- { "Rooster",
- { Z, Z, P, P, Z, L, Z, L, R, P, R, Z, P, P, Z, R, P, R, L, Z, L, Z, P }
- }
+struct model_s model[] = {
+#define STRAIGHT_MODEL 0
+ { "straight",
+ { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO,
+ ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO,
+ ZERO, ZERO, ZERO }
+ },
+ /* the models in the Rubik's snake manual */
+ { "ball",
+ { RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT,
+ RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT,
+ RIGHT, LEFT, RIGHT, LEFT, ZERO }
+ },
+#define START_MODEL 2
+ { "snow",
+ { RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT,
+ RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT,
+ RIGHT, LEFT, LEFT, LEFT, ZERO }
+ },
+ { "propellor",
+ { ZERO, ZERO, ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT, ZERO, ZERO,
+ ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT, ZERO, ZERO, ZERO, RIGHT,
+ LEFT, RIGHT, ZERO, LEFT }
+ },
+ { "flamingo",
+ { ZERO, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, RIGHT, PIN,
+ RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, RIGHT, ZERO, ZERO,
+ ZERO, PIN, ZERO }
+ },
+ { "cat",
+ { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN,
+ ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO,
+ ZERO, ZERO }
+ },
+ { "rooster",
+ { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, LEFT, RIGHT, PIN, RIGHT,
+ ZERO, PIN, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, ZERO, LEFT, ZERO,
+ PIN, ZERO }
+ },
+ /* These models were taken from Andrew and Peter's original snake.c
+ * as well as some newer ones made up by Jamie, Andrew and Peter. */
+ { "half balls",
+ { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT,
+ LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT,
+ RIGHT, LEFT, LEFT, LEFT, ZERO }
+ },
+ { "zigzag1",
+ { RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT,
+ LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT,
+ RIGHT, RIGHT, LEFT, LEFT, ZERO }
+ },
+ { "zigzag2",
+ { PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN,
+ ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN,
+ ZERO }
+ },
+ { "zigzag3",
+ { PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN,
+ LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN,
+ ZERO }
+ },
+ { "caterpillar",
+ { RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT,
+ LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN,
+ LEFT, LEFT, ZERO }
+ },
+ { "bow",
+ { RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT,
+ LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT,
+ RIGHT, RIGHT, LEFT, LEFT, ZERO }
+ },
+ { "turtle",
+ { ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT,
+ LEFT, RIGHT, LEFT, LEFT, PIN, LEFT, LEFT, LEFT, RIGHT, LEFT,
+ RIGHT, RIGHT, RIGHT, ZERO }
+ },
+ { "basket",
+ { RIGHT, PIN, ZERO, ZERO, PIN, LEFT, ZERO, LEFT, LEFT, ZERO,
+ LEFT, PIN, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, ZERO,
+ PIN, LEFT, ZERO }
+ },
+ { "thing",
+ { PIN, RIGHT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT,
+ LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT,
+ RIGHT, LEFT, LEFT, ZERO }
+ },
+ { "hexagon",
+ { ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO,
+ ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO,
+ LEFT, ZERO, ZERO, RIGHT }
+ },
+ { "tri1",
+ { ZERO, ZERO, LEFT, RIGHT, ZERO, LEFT, ZERO, RIGHT, ZERO, ZERO,
+ LEFT, RIGHT, ZERO, LEFT, ZERO, RIGHT, ZERO, ZERO, LEFT, RIGHT,
+ ZERO, LEFT, ZERO, RIGHT }
+ },
+ { "triangle",
+ { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO,
+ ZERO, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, ZERO, ZERO,
+ ZERO, ZERO, LEFT, RIGHT }
+ },
+ { "flower",
+ { ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT,
+ RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN,
+ RIGHT, RIGHT, PIN }
+ },
+ { "crucifix",
+ { ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN,
+ PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN }
+ },
+ { "kayak",
+ { PIN, RIGHT, LEFT, PIN, LEFT, PIN, ZERO, ZERO, RIGHT, PIN, LEFT,
+ ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO,
+ PIN, RIGHT }
+ },
+ { "bird",
+ { ZERO, ZERO, ZERO, ZERO, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT,
+ ZERO, RIGHT, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT,
+ LEFT, ZERO, PIN, ZERO }
+ },
+ { "seal",
+ { RIGHT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO,
+ LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, LEFT, PIN, RIGHT,
+ RIGHT, LEFT, ZERO }
+ },
+ { "dog",
+ { ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN,
+ ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO }
+ },
+ { "frog",
+ { RIGHT, RIGHT, LEFT, LEFT, RIGHT, PIN, RIGHT, PIN, LEFT, PIN,
+ RIGHT, ZERO, LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT,
+ RIGHT, LEFT, LEFT, ZERO }
+ },
+ { "quavers",
+ { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO, ZERO, ZERO,
+ RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, ZERO, LEFT, LEFT,
+ RIGHT, LEFT, RIGHT, RIGHT, ZERO }
+ },
+ { "fly",
+ { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO, PIN, ZERO, ZERO,
+ LEFT, PIN, RIGHT, ZERO, ZERO, PIN, ZERO, LEFT, LEFT, RIGHT, LEFT,
+ RIGHT, RIGHT, ZERO }
+ },
+ { "puppy",
+ { ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO,
+ RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT,
+ LEFT, ZERO, ZERO }
+ },
+ { "stars",
+ { LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT,
+ ZERO, ZERO, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN,
+ RIGHT, LEFT, ZERO }
+ },
+ { "mountains",
+ { RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN,
+ LEFT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT,
+ PIN, LEFT, PIN }
+ },
+ { "quad1",
+ { RIGHT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN,
+ LEFT, PIN, RIGHT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT,
+ LEFT, PIN, LEFT, PIN }
+ },
+ { "quad2",
+ { ZERO, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN,
+ ZERO, PIN, ZERO, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT,
+ PIN, ZERO, PIN }
+ },
+ { "glasses",
+ { ZERO, PIN, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, PIN,
+ ZERO, PIN, ZERO, PIN, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO,
+ PIN, ZERO, PIN }
+ },
+ { "em",
+ { ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN,
+ ZERO, PIN, ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO,
+ PIN, ZERO, PIN }
+ },
+ { "quad3",
+ { ZERO, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT,
+ ZERO, PIN, ZERO, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO,
+ LEFT, ZERO, PIN }
+ },
+ { "vee",
+ { ZERO, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO,
+ ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO,
+ ZERO, ZERO, PIN }
+ },
+ { "square",
+ { ZERO, ZERO, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, ZERO,
+ ZERO, PIN, ZERO, ZERO, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO,
+ ZERO, ZERO, PIN }
+ },
+ { "eagle",
+ { RIGHT, ZERO, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, ZERO,
+ LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT,
+ ZERO, ZERO, LEFT, PIN }
+ },
+ { "volcano",
+ { RIGHT, ZERO, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT,
+ ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, RIGHT, RIGHT, PIN, LEFT,
+ LEFT, RIGHT, ZERO, LEFT, PIN }
+ },
+ { "saddle",
+ { RIGHT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, ZERO,
+ LEFT, PIN, RIGHT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT,
+ ZERO, LEFT, PIN }
+ },
+ { "c3d",
+ { ZERO, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT, ZERO,
+ ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT,
+ ZERO, ZERO, PIN }
+ },
+ { "block",
+ { ZERO, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN,
+ RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT,
+ PIN, RIGHT }
+ },
+ { "duck",
+ { LEFT, PIN, LEFT, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, LEFT,
+ PIN, RIGHT, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, LEFT, PIN,
+ LEFT, ZERO }
+ },
+ { "prayer",
+ { RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO, ZERO,
+ ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, RIGHT, RIGHT, LEFT,
+ RIGHT, LEFT, LEFT, LEFT, PIN }
+ },
+ { "giraffe",
+ { ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, RIGHT,
+ RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT,
+ PIN, LEFT, LEFT, LEFT }
+ },
+ { "tie fighter",
+ { PIN, LEFT, RIGHT, LEFT, LEFT, PIN, RIGHT, ZERO, RIGHT, LEFT,
+ ZERO, PIN, LEFT, LEFT, RIGHT, RIGHT, RIGHT, PIN, LEFT, ZERO,
+ LEFT, RIGHT, ZERO, ZERO }
+ },
+ { "Strong Arms",
+ { PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, RIGHT,
+ RIGHT, PIN, RIGHT, RIGHT, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO,
+ ZERO, PIN, PIN, ZERO }
+ },
+
+ /* the following modesl were created during the slug/compsoc codefest
+ * febrray 2003 */
+ { "cool looking gegl",
+ { PIN, PIN, ZERO, ZERO, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT,
+ ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, RIGHT, PIN, ZERO,
+ ZERO, ZERO }
+ },
+ { "knuckledusters",
+ { ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN,
+ PIN, ZERO, RIGHT, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO,
+ RIGHT, ZERO }
+ },
+ { "k's turd",
+ { RIGHT, RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT,
+ RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN,
+ RIGHT, LEFT, RIGHT, PIN, ZERO }
+ },
+ { "lightsabre",
+ { ZERO, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO,
+ ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO,
+ ZERO, ZERO }
+ },
+ { "not a stairway",
+ { LEFT, ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO,
+ RIGHT, LEFT, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT,
+ RIGHT, ZERO, LEFT, ZERO }
+ },
+ { "not very good (but accurate) gegl",
+ { ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO,
+ PIN, PIN, ZERO, RIGHT, LEFT, ZERO, PIN, ZERO, PIN, PIN, ZERO,
+ PIN, ZERO }
+ },
+ { "box",
+ { ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, ZERO,
+ ZERO, PIN, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO,
+ ZERO, ZERO, ZERO }
+ },
+ { "kissy box",
+ { PIN, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, ZERO,
+ ZERO, PIN, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO,
+ ZERO, PIN, ZERO }
+ },
+ { "erect penis", /* thanks benno */
+ { PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, PIN,
+ PIN, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO,
+ ZERO, ZERO }
+ },
+ { "flaccid penis",
+ { PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, PIN,
+ PIN, ZERO, ZERO, ZERO, RIGHT, PIN, ZERO, ZERO, ZERO, ZERO, ZERO,
+ ZERO, ZERO }
+ },
+ { "vagina",
+ { RIGHT, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO,
+ LEFT, ZERO, ZERO, ZERO, LEFT, ZERO, LEFT, PIN, LEFT, PIN, RIGHT,
+ PIN, RIGHT, ZERO }
+ },
+ { "mask",
+ { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO, PIN,
+ ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO,
+ ZERO, ZERO, ZERO }
+ },
+ { "poles or columns or something",
+ { LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO,
+ ZERO, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO,
+ ZERO, LEFT, ZERO }
+ },
+ { "crooked v",
+ { ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO,
+ ZERO, LEFT, ZERO, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO,
+ ZERO, ZERO, ZERO }
+ },
+ { "dog leg",
+ { ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO,
+ LEFT, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO,
+ ZERO, ZERO }
+ },
+ { "scrubby",
+ { ZERO, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO,
+ LEFT, RIGHT, ZERO, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO,
+ LEFT, PIN, ZERO, ZERO }
+ },
+ { "voltron's eyes",
+ { ZERO, ZERO, PIN, RIGHT, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO,
+ LEFT, PIN, ZERO, ZERO, PIN, ZERO, LEFT, ZERO, RIGHT, LEFT, ZERO,
+ RIGHT, ZERO, ZERO }
+ },
+ { "flying toaster",
+ { PIN, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO,
+ RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO,
+ PIN, ZERO }
+ },
+ { "dubbya",
+ { PIN, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO,
+ ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO,
+ PIN, ZERO }
+ },
+ { "tap handle",
+ { PIN, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO,
+ LEFT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO,
+ PIN, ZERO }
+ },
+ { "wingnut",
+ { PIN, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO,
+ PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO,
+ PIN, ZERO }
+ },
+ { "tight twist",
+ { RIGHT, ZERO, ZERO, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT,
+ RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT,
+ ZERO, ZERO, RIGHT, ZERO }
+ },
+ { "double helix",
+ { RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, RIGHT,
+ ZERO, RIGHT, ZERO, RIGHT, LEFT, RIGHT, PIN, ZERO, RIGHT, ZERO,
+ RIGHT, ZERO, RIGHT, ZERO, ZERO }
+ },
+
+ /* These models come from the website at
+ * http://www.geocities.com/stigeide/snake */
+#if 0
+ { "Abstract",
+ { RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, PIN, ZERO, RIGHT, LEFT, RIGHT, ZERO, ZERO }
+ },
+#endif
+ { "toadstool",
+ { LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, ZERO }
+ },
+ { "AlanH2",
+ { LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, ZERO }
+ },
+ { "AlanH3",
+ { LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, ZERO }
+ },
+ { "AlanH4",
+ { ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT, RIGHT, PIN, ZERO, ZERO, ZERO }
+ },
+ { "Alien",
+ { RIGHT, LEFT, RIGHT, PIN, ZERO, ZERO, PIN, RIGHT, LEFT, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, ZERO, PIN, PIN, ZERO }
+ },
+ { "Angel",
+ { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, ZERO }
+ },
+ { "AnotherFigure",
+ { LEFT, PIN, RIGHT, ZERO, ZERO, PIN, RIGHT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, PIN, ZERO, ZERO }
+ },
+ { "Ball",
+ { LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT , ZERO }
+ },
+ { "Basket",
+ { ZERO, RIGHT, RIGHT, ZERO, RIGHT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, ZERO, LEFT , ZERO }
+ },
+ { "Beetle",
+ { PIN, LEFT, RIGHT, ZERO, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO, LEFT, RIGHT, PIN, RIGHT , ZERO }
+ },
+ { "bone",
+ { PIN, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, PIN , ZERO }
+ },
+ { "Bow",
+ { LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT , ZERO }
+ },
+ { "bra",
+ { RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT , ZERO }
+ },
+ { "bronchosaurus",
+ { ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, PIN , ZERO }
+ },
+ { "Cactus",
+ { PIN, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, ZERO, ZERO , ZERO }
+ },
+ { "Camel",
+ { RIGHT, ZERO, PIN, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, ZERO, ZERO, LEFT , ZERO }
+ },
+ { "Candlestick",
+ { LEFT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, RIGHT , ZERO }
+ },
+ { "Cat",
+ { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO , ZERO }
+ },
+ { "Cave",
+ { RIGHT, ZERO, ZERO, PIN, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, LEFT, PIN, RIGHT, RIGHT, LEFT, PIN, ZERO, ZERO , ZERO }
+ },
+ { "Chains",
+ { PIN, ZERO, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO , ZERO }
+ },
+ { "Chair",
+ { RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, LEFT, RIGHT, LEFT, LEFT , ZERO }
+ },
+ { "Chick",
+ { RIGHT, RIGHT, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, LEFT, LEFT , ZERO }
+ },
+ { "Clockwise",
+ { RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT , ZERO }
+ },
+ { "cobra",
+ { ZERO, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, LEFT, ZERO, LEFT, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT , ZERO }
+ },
+#if 0
+ { "Cobra2",
+ { LEFT, ZERO, PIN, ZERO, PIN, LEFT, ZERO, PIN, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, PIN, ZERO, RIGHT, PIN, ZERO, PIN, ZERO, RIGHT , ZERO }
+ },
+#endif
+ { "Cobra3",
+ { ZERO, LEFT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, LEFT , ZERO }
+ },
+ { "Compact1",
+ { ZERO, ZERO, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, PIN , ZERO }
+ },
+ { "Compact2",
+ { LEFT, PIN, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO , ZERO }
+ },
+ { "Compact3",
+ { ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN , ZERO }
+ },
+ { "Compact4",
+ { PIN, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO , ZERO }
+ },
+ { "Compact5",
+ { LEFT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT , ZERO }
+ },
+ { "Contact",
+ { PIN, ZERO, ZERO, PIN, LEFT, LEFT, PIN, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO, PIN, RIGHT, PIN , ZERO }
+ },
+ { "Contact2",
+ { RIGHT, PIN, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, PIN, LEFT , ZERO }
+ },
+ { "Cook",
+ { ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, RIGHT, LEFT, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, PIN, LEFT, RIGHT, ZERO, RIGHT, ZERO, PIN , ZERO }
+ },
+ { "Counterclockwise",
+ { LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT , ZERO }
+ },
+ { "Cradle",
+ { LEFT, LEFT, ZERO, PIN, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, PIN, ZERO, RIGHT, RIGHT, LEFT, LEFT, ZERO, ZERO, RIGHT , ZERO }
+ },
+ { "Crankshaft",
+ { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO, PIN, RIGHT , ZERO }
+ },
+ { "Cross",
+ { ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN , ZERO }
+ },
+ { "Cross2",
+ { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO , ZERO }
+ },
+ { "Cross3",
+ { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO, ZERO }
+ },
+ { "CrossVersion1",
+ { PIN, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, ZERO }
+ },
+ { "CrossVersion2",
+ { RIGHT, LEFT, PIN, LEFT, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, LEFT, LEFT, PIN, LEFT, RIGHT, ZERO }
+ },
+ { "Crown",
+ { LEFT, ZERO, PIN, ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO, PIN, ZERO, RIGHT, LEFT, ZERO, PIN, ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO, PIN, ZERO, ZERO }
+ },
+ { "DNAStrand",
+ { RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, ZERO }
+ },
+ { "Diamond",
+ { ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO }
+ },
+ { "Dog",
+ { RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO, LEFT, RIGHT, ZERO }
+ },
+ { "DogFace",
+ { ZERO, ZERO, PIN, PIN, ZERO, LEFT, LEFT, RIGHT, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, RIGHT, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO }
+ },
+ { "DoublePeak",
+ { ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, LEFT, ZERO, PIN, ZERO, RIGHT, RIGHT, LEFT, PIN, LEFT, RIGHT, ZERO }
+ },
+ { "DoubleRoof",
+ { ZERO, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO }
+ },
+ { "txoboggan",
+ { ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO }
+ },
+ { "Doubled",
+ { LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, ZERO, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, ZERO }
+ },
+ { "Doubled1",
+ { LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, ZERO, RIGHT, ZERO, RIGHT, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, ZERO }
+ },
+ { "Doubled2",
+ { LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, RIGHT, ZERO, RIGHT, LEFT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, ZERO }
+ },
+ { "DumblingSpoon",
+ { PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, ZERO }
+ },
+ { "Embrace",
+ { PIN, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, ZERO }
+ },
+ { "EndlessBelt",
+ { ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, PIN, RIGHT, LEFT, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO }
+ },
+ { "Entrance",
+ { LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, ZERO }
+ },
+ { "Esthetic",
+ { LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, ZERO }
+ },
+ { "Explosion",
+ { RIGHT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, ZERO }
+ },
+ { "F-ZeroXCar",
+ { RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, ZERO }
+ },
+ { "Face",
+ { ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO }
+ },
+ { "Fantasy",
+ { LEFT, LEFT, RIGHT, PIN, ZERO, RIGHT, ZERO, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, ZERO, LEFT, ZERO, PIN, LEFT, RIGHT, RIGHT, RIGHT, PIN, ZERO }
+ },
+ { "Fantasy1",
+ { PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO }
+ },
+ { "FaserGun",
+ { ZERO, ZERO, LEFT, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, RIGHT, ZERO, PIN, ZERO }
+ },
+ { "FelixW",
+ { ZERO, RIGHT, ZERO, PIN, LEFT, ZERO, LEFT, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, RIGHT, ZERO, RIGHT, PIN, ZERO, LEFT, ZERO, ZERO }
+ },
+ { "Flamingo",
+ { ZERO, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN, LEFT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, ZERO, ZERO, ZERO, PIN, ZERO }
+ },
+ { "FlatOnTheTop",
+ { ZERO, PIN, PIN, ZERO, PIN, RIGHT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO }
+ },
+ { "Fly",
+ { ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO }
+ },
+ { "Fountain",
+ { LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, PIN, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, PIN, RIGHT, PIN, ZERO }
+ },
+ { "Frog",
+ { LEFT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, RIGHT, LEFT, RIGHT, RIGHT, ZERO }
+ },
+ { "Frog2",
+ { LEFT, ZERO, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, RIGHT, ZERO, RIGHT, ZERO }
+ },
+ { "Furby",
+ { PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, PIN, ZERO, ZERO, PIN, ZERO }
+ },
+ { "Gate",
+ { ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, ZERO }
+ },
+ { "Ghost",
+ { LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO }
+ },
+ { "Globus",
+ { RIGHT, LEFT, ZERO, PIN, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, PIN, ZERO, RIGHT, LEFT, ZERO, ZERO }
+ },
+ { "Grotto",
+ { PIN, PIN, ZERO, LEFT, RIGHT, LEFT, ZERO, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, ZERO, RIGHT, LEFT, RIGHT, ZERO }
+ },
+ { "H",
+ { PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, LEFT, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO }
+ },
+ { "HeadOfDevil",
+ { PIN, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, ZERO, ZERO, ZERO }
+ },
+ { "Heart",
+ { RIGHT, ZERO, ZERO, ZERO, PIN, LEFT, PIN, LEFT, RIGHT, RIGHT, ZERO, PIN, ZERO, LEFT, LEFT, RIGHT, PIN, RIGHT, PIN, ZERO, ZERO, ZERO, LEFT, ZERO }
+ },
+ { "Heart2",
+ { ZERO, PIN, ZERO, ZERO, LEFT, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO }
+ },
+ { "Hexagon",
+ { ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, ZERO }
+ },
+ { "HoleInTheMiddle1",
+ { ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, ZERO }
+ },
+ { "HoleInTheMiddle2",
+ { ZERO, LEFT, RIGHT, ZERO, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, ZERO }
+ },
+ { "HouseBoat",
+ { RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, ZERO }
+ },
+ { "HouseByHouse",
+ { LEFT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, ZERO }
+ },
+ { "Infinity",
+ { LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, ZERO }
+ },
+ { "Integral",
+ { RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO }
+ },
+ { "Iron",
+ { ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, ZERO, RIGHT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, ZERO, RIGHT, ZERO }
+ },
+ { "just squares",
+ { RIGHT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, LEFT, PIN, RIGHT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, LEFT, ZERO }
+ },
+ { "Kink",
+ { ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO }
+ },
+ { "Knot",
+ { LEFT, LEFT, PIN, LEFT, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, RIGHT, PIN, RIGHT, RIGHT, LEFT, ZERO }
+ },
+ { "Leaf",
+ { ZERO, PIN, PIN, ZERO, ZERO, LEFT, ZERO, LEFT, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO }
+ },
+ { "LeftAsRight",
+ { RIGHT, PIN, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, RIGHT, LEFT, RIGHT, PIN, LEFT, ZERO }
+ },
+ { "Long-necked",
+ { PIN, ZERO, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, PIN, PIN, ZERO, ZERO }
+ },
+ { "lunar module",
+ { PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, ZERO }
+ },
+ { "magnifying glass",
+ { ZERO, ZERO, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, ZERO, ZERO, ZERO }
+ },
+ { "Mask",
+ { ZERO, ZERO, ZERO, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, ZERO }
+ },
+ { "Microscope",
+ { PIN, PIN, ZERO, ZERO, PIN, ZERO, RIGHT, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, LEFT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO }
+ },
+ { "Mirror",
+ { PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT, RIGHT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, ZERO }
+ },
+ { "MissPiggy",
+ { ZERO, LEFT, LEFT, PIN, RIGHT, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, LEFT, PIN, RIGHT, RIGHT, ZERO, RIGHT, ZERO }
+ },
+ { "Mole",
+ { ZERO, RIGHT, ZERO, RIGHT, LEFT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, LEFT, ZERO, RIGHT, RIGHT, PIN, LEFT, ZERO }
+ },
+ { "Monk",
+ { LEFT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO }
+ },
+ { "Mountain",
+ { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, LEFT, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO }
+ },
+ { "mountains",
+ { ZERO, PIN, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, PIN, ZERO, ZERO }
+ },
+ { "MouseWithoutTail",
+ { ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO }
+ },
+ { "mushroom",
+ { PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN, ZERO }
+ },
+ { "necklace",
+ { ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO }
+ },
+ { "NestledAgainst",
+ { LEFT, ZERO, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, ZERO }
+ },
+ { "NoClue",
+ { ZERO, RIGHT, PIN, LEFT, LEFT, LEFT, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, RIGHT, RIGHT, RIGHT, PIN, LEFT, ZERO, ZERO }
+ },
+ { "Noname",
+ { LEFT, PIN, RIGHT, PIN, RIGHT, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, ZERO }
+ },
+ { "Obelisk",
+ { PIN, ZERO, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, ZERO, ZERO, ZERO, ZERO }
+ },
+ { "Ostrich",
+ { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN, ZERO }
+ },
+ { "Ostrich2",
+ { PIN, PIN, ZERO, PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO }
+ },
+ { "pair of glasses",
+ { ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, LEFT, ZERO, PIN, ZERO, RIGHT, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO }
+ },
+ { "Parrot",
+ { ZERO, ZERO, ZERO, ZERO, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, ZERO, PIN, ZERO }
+ },
+ { "Penis",
+ { PIN, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, PIN, PIN, ZERO }
+ },
+ { "PictureCommingSoon",
+ { LEFT, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, ZERO, RIGHT, RIGHT, ZERO }
+ },
+ { "Pitti",
+ { LEFT, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, RIGHT, ZERO }
+ },
+ { "Plait",
+ { LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, ZERO }
+ },
+ { "Platform",
+ { RIGHT, PIN, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO }
+ },
+ { "PodRacer",
+ { ZERO, PIN, ZERO, PIN, RIGHT, PIN, ZERO, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, ZERO, PIN, LEFT, ZERO }
+ },
+#if 0
+ { "Pokemon",
+ { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO }
+ },
+#endif
+ { "Prawn",
+ { RIGHT, PIN, ZERO, PIN, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT, PIN, ZERO, PIN, LEFT, ZERO }
+ },
+ { "Propeller",
+ { ZERO, ZERO, ZERO, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, RIGHT, ZERO, LEFT, RIGHT, ZERO }
+ },
+ { "Pyramid",
+ { ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, RIGHT, LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, LEFT, ZERO }
+ },
+ { "QuarterbackTiltedAndReadyToHut",
+ { PIN, ZERO, RIGHT, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, LEFT, ZERO, PIN, ZERO }
+ },
+ { "Ra",
+ { PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO }
+ },
+ { "Rattlesnake",
+ { LEFT, ZERO, LEFT, ZERO, LEFT, ZERO, LEFT, LEFT, ZERO, LEFT, ZERO, LEFT, ZERO, LEFT, RIGHT, ZERO, PIN, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, ZERO }
+ },
+ { "Revelation",
+ { ZERO, ZERO, ZERO, PIN, ZERO, ZERO, PIN, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN, ZERO }
+ },
+ { "Revolution1",
+ { LEFT, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, ZERO }
+ },
+ { "Ribbon",
+ { RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO }
+ },
+ { "Rocket",
+ { RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, RIGHT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, ZERO, LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, ZERO }
+ },
+ { "Roofed",
+ { ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, ZERO, PIN, ZERO, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, ZERO, PIN, ZERO, RIGHT, ZERO }
+ },
+ { "Roofs",
+ { PIN, PIN, RIGHT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, LEFT, PIN, PIN, ZERO }
+ },
+ { "RowHouses",
+ { RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO }
+ },
+ { "Sculpture",
+ { RIGHT, LEFT, PIN, ZERO, ZERO, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO }
+ },
+ { "Seal",
+ { LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, LEFT, ZERO }
+ },
+ { "Seal2",
+ { RIGHT, PIN, ZERO, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, ZERO }
+ },
+ { "Sheep",
+ { RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, LEFT, ZERO }
+ },
+ { "Shelter",
+ { LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO }
+ },
+ { "Ship",
+ { PIN, RIGHT, LEFT, LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, LEFT, ZERO, PIN, PIN, ZERO }
+ },
+ { "Shpongle",
+ { LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, ZERO }
+ },
+ { "Slide",
+ { LEFT, RIGHT, LEFT, RIGHT, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, RIGHT, LEFT, ZERO }
+ },
+ { "SmallShip",
+ { ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, LEFT, RIGHT, ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, LEFT, ZERO }
+ },
+ { "SnakeReadyToStrike",
+ { LEFT, ZERO, LEFT, ZERO, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, LEFT, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO }
+ },
+ { "Snakes14",
+ { RIGHT, RIGHT, PIN, ZERO, RIGHT, LEFT, RIGHT, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, ZERO, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO }
+ },
+ { "Snakes15",
+ { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO, PIN, RIGHT, ZERO }
+ },
+ { "Snakes18",
+ { PIN, PIN, LEFT, PIN, LEFT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO }
+ },
+ { "Snowflake",
+ { LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, ZERO }
+ },
+ { "Snowman",
+ { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO }
+ },
+ { "Source",
+ { PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, ZERO }
+ },
+ { "Spaceship",
+ { PIN, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, PIN, ZERO }
+ },
+ { "Spaceship2",
+ { PIN, PIN, LEFT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, LEFT, LEFT, PIN, PIN, ZERO }
+ },
+ { "Speedboat",
+ { LEFT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, LEFT, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, ZERO }
+ },
+ { "Speedboat2",
+ { PIN, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, PIN, ZERO, RIGHT, PIN, LEFT, ZERO }
+ },
+ { "Spider",
+ { RIGHT, RIGHT, ZERO, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, RIGHT, LEFT, RIGHT, ZERO, ZERO, LEFT, ZERO }
+ },
+ { "Spitzbergen",
+ { PIN, LEFT, ZERO, RIGHT, RIGHT, LEFT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, PIN, RIGHT, LEFT, LEFT, ZERO, ZERO }
+ },
+ { "Square",
+ { ZERO, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, ZERO, ZERO }
+ },
+ { "SquareHole",
+ { PIN, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, PIN, ZERO }
+ },
+ { "Stage",
+ { RIGHT, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, ZERO }
+ },
+ { "Stairs",
+ { ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, ZERO }
+ },
+ { "Stairs2",
+ { ZERO, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO }
+ },
+ { "Straight",
+ { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO }
+ },
+ { "Swan",
+ { ZERO, PIN, ZERO, PIN, LEFT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, RIGHT, ZERO }
+ },
+ { "Swan2",
+ { PIN, ZERO, PIN, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO }
+ },
+ { "Swan3",
+ { PIN, PIN, ZERO, ZERO, ZERO, RIGHT, ZERO, RIGHT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, ZERO, RIGHT, ZERO }
+ },
+ { "Symbol",
+ { RIGHT, RIGHT, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, RIGHT, ZERO }
+ },
+ { "Symmetry",
+ { RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, LEFT, RIGHT, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT, ZERO }
+ },
+ { "Symmetry2",
+ { ZERO, PIN, LEFT, LEFT, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, PIN, LEFT, ZERO }
+ },
+ { "TableFireworks",
+ { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, RIGHT, LEFT, ZERO, RIGHT, PIN, ZERO }
+ },
+ { "Tapering",
+ { ZERO, ZERO, RIGHT, LEFT, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, ZERO, ZERO, ZERO }
+ },
+ { "TaperingTurned",
+ { ZERO, ZERO, RIGHT, LEFT, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, ZERO, ZERO, ZERO }
+ },
+ { "TeaLightStick",
+ { RIGHT, ZERO, PIN, PIN, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, ZERO }
+ },
+ { "thighmaster",
+ { RIGHT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO }
+ },
+ { "Terraces",
+ { RIGHT, LEFT, ZERO, RIGHT, LEFT, PIN, LEFT, LEFT, PIN, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, ZERO }
+ },
+ { "Terrier",
+ { PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO }
+ },
+ { "Three-Legged",
+ { RIGHT, ZERO, LEFT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, RIGHT, ZERO, PIN, ZERO, LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, RIGHT, ZERO, LEFT, ZERO }
+ },
+ { "ThreePeaks",
+ { RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, ZERO }
+ },
+ { "ToTheFront",
+ { ZERO, PIN, RIGHT, LEFT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, LEFT, LEFT, PIN, ZERO, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, ZERO }
+ },
+ { "Top",
+ { PIN, LEFT, LEFT, PIN, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, RIGHT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO }
+ },
+ { "Transport",
+ { PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO }
+ },
+ { "Triangle",
+ { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO }
+ },
+ { "Tripple",
+ { PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, ZERO }
+ },
+#if 0
+ { "Turtle",
+ { RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO }
+ },
+#endif
+ { "Twins",
+ { ZERO, PIN, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, ZERO, ZERO, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, PIN, ZERO, ZERO, ZERO }
+ },
+ { "TwoSlants",
+ { ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, PIN, ZERO }
+ },
+ { "TwoWings",
+ { PIN, LEFT, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, ZERO, ZERO }
+ },
+ { "UFO",
+ { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO }
+ },
+ { "USS Enterprise",
+ { LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, ZERO, ZERO }
+ },
+ { "UpAndDown",
+ { ZERO, PIN, ZERO, PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, ZERO }
+ },
+ { "Upright",
+ { ZERO, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO, ZERO }
+ },
+ { "Upside-down",
+ { PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, RIGHT, LEFT, LEFT, PIN, RIGHT, RIGHT, LEFT, LEFT, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, ZERO }
+ },
+ { "Valley",
+ { ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, ZERO }
+ },
+ { "Viaduct",
+ { PIN, RIGHT, PIN, LEFT, PIN, ZERO, ZERO, PIN, RIGHT, ZERO, RIGHT, RIGHT, ZERO, RIGHT, PIN, ZERO, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, ZERO }
+ },
+ { "View",
+ { ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, ZERO }
+ },
+ { "Waterfall",
+ { LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, ZERO }
+ },
+ { "windwheel",
+ { PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO }
+ },
+ { "Window",
+ { PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO }
+ },
+ { "WindowToTheWorld",
+ { PIN, LEFT, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO }
+ },
+ { "Windshield",
+ { PIN, PIN, ZERO, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, ZERO, PIN, PIN, ZERO, PIN, ZERO }
+ },
+ { "WingNut",
+ { ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, RIGHT, RIGHT, PIN, ZERO, ZERO, ZERO, ZERO, ZERO }
+ },
+ { "Wings2",
+ { RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, ZERO }
+ },
+ { "WithoutName",
+ { PIN, RIGHT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, ZERO }
+ },
+ { "Wolf",
+ { ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, ZERO }
+ },
+ { "X",
+ { LEFT, ZERO, ZERO, PIN, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, RIGHT, PIN, ZERO, ZERO, ZERO }
+ },
};
-/* add a model to the model list */
-model_t * add_model(model_t * models, char * name, int * rotations, int * count) {
- int i;
-
- (*count)++;
- models = realloc(models, sizeof(model_t) * (*count));
- models[(*count)-1].name = strdup(name);
-#ifdef DEBUG
- fprintf(stderr, "resized models to %d bytes for model %s\n", sizeof(model_t) * (*count), models[(*count)-1].name);
+size_t models = sizeof(model) / sizeof(struct model_s);
+
+#define VOFFSET 0.045
+
+#define X_MASK 1
+#define Y_MASK 2
+#define Z_MASK 4
+
+/* the connecting string that holds the snake together */
+#define MAGICAL_RED_STRING 0
+
+#define GETSCALAR(vec,mask) ((vec)==(mask) ? 1 : ((vec)==-(mask) ? -1 : 0 ))
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif
- for (i = 0; i < 24; i++) {
- models[(*count)-1].node[i] = rotations[i] * 90.0;
- }
- return models;
+#ifndef MIN
+# define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#define RAND(n) ((random() & 0x7fffffff) % ((long) (n)))
+#define RANDSIGN() ((random() & 1) ? 1 : -1)
+
+/* the triangular prism what makes up the basic unit */
+float solid_prism_v[][3] = {
+ /* first corner, bottom left front */
+ { VOFFSET, VOFFSET, 1.0 },
+ { VOFFSET, 0.00, 1.0 - VOFFSET },
+ { 0.00, VOFFSET, 1.0 - VOFFSET },
+ /* second corner, rear */
+ { VOFFSET, VOFFSET, 0.00 },
+ { VOFFSET, 0.00, VOFFSET },
+ { 0.00, VOFFSET, VOFFSET },
+ /* third, right front */
+ { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 1.0 },
+ { 1.0 - VOFFSET / M_SQRT1_2, 0.0, 1.0 - VOFFSET },
+ { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, 1.0 - VOFFSET },
+ /* fourth, right rear */
+ { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 0.0 },
+ { 1.0 - VOFFSET / M_SQRT1_2, 0.0, VOFFSET },
+ { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, VOFFSET },
+ /* fifth, upper front */
+ { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 1.0 },
+ { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, 1.0 - VOFFSET },
+ { 0.0, 1.0 - VOFFSET / M_SQRT1_2, 1.0 - VOFFSET},
+ /* sixth, upper rear */
+ { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 0.0 },
+ { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, VOFFSET },
+ { 0.0, 1.0 - VOFFSET / M_SQRT1_2, VOFFSET }};
+
+float solid_prism_n[][3] = {/* corners */
+ { -VOFFSET, -VOFFSET, VOFFSET },
+ { VOFFSET, -VOFFSET, VOFFSET },
+ { -VOFFSET, VOFFSET, VOFFSET },
+ { -VOFFSET, -VOFFSET, -VOFFSET },
+ { VOFFSET, -VOFFSET, -VOFFSET },
+ { -VOFFSET, VOFFSET, -VOFFSET },
+ /* edges */
+ { -VOFFSET, 0.0, VOFFSET },
+ { 0.0, -VOFFSET, VOFFSET },
+ { VOFFSET, VOFFSET, VOFFSET },
+ { -VOFFSET, 0.0, -VOFFSET },
+ { 0.0, -VOFFSET, -VOFFSET },
+ { VOFFSET, VOFFSET, -VOFFSET },
+ { -VOFFSET, -VOFFSET, 0.0 },
+ { VOFFSET, -VOFFSET, 0.0 },
+ { -VOFFSET, VOFFSET, 0.0 },
+ /* faces */
+ { 0.0, 0.0, 1.0 },
+ { 0.0, -1.0, 0.0 },
+ { M_SQRT1_2, M_SQRT1_2, 0.0 },
+ { -1.0, 0.0, 0.0 },
+ { 0.0, 0.0, -1.0 }};
+
+float wire_prism_v[][3] = {{ 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 0.0 },
+ { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 }};
+
+float wire_prism_n[][3] = {{ 0.0, 0.0, 1.0},
+ { 0.0,-1.0, 0.0},
+ { M_SQRT1_2, M_SQRT1_2, 0.0},
+ {-1.0, 0.0, 0.0},
+ { 0.0, 0.0,-1.0}};
+
+static struct glsnake_cfg * glc = NULL;
+#ifdef HAVE_GLUT
+# define bp glc
+#endif
+
+typedef float (*morphFunc)(long);
+
+#ifdef HAVE_GLUT
+/* forward definitions for GLUT functions */
+void calc_rotation();
+inline void ui_mousedrag();
+#endif
+
+GLfloat white_light[] = { 1.0, 1.0, 1.0, 1.0 };
+GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
+GLfloat mat_specular[] = { 0.1, 0.1, 0.1, 1.0 };
+GLfloat mat_shininess[] = { 20.0 };
+
+void gl_init(
+#ifndef HAVE_GLUT
+ ModeInfo * mi
+#endif
+ ) {
+ float light_pos[][3] = {{0.0, 10.0, 20.0}, {0.0, 20.0, -1.0}};
+ float light_dir[][3] = {{0.0, -10.0,-20.0}, {0.0,-20.0, 1.0}};
+
+ glEnable(GL_DEPTH_TEST);
+ glShadeModel(GL_SMOOTH);
+ glCullFace(GL_BACK);
+ /*glEnable(GL_CULL_FACE);*/
+ glEnable(GL_NORMALIZE);
+ if (transparent) {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ }
+
+ if (!wireframe) {
+ /*glColor4f(1.0, 1.0, 1.0, 1.0);*/
+ glLightfv(GL_LIGHT0, GL_POSITION, light_pos[0]);
+ glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_dir[0]);
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
+ /*glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);*/
+#if 1
+ glLightfv(GL_LIGHT1, GL_POSITION, light_pos[1]);
+ glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_dir[1]);
+ glLightfv(GL_LIGHT1, GL_DIFFUSE, white_light);
+ glLightfv(GL_LIGHT1, GL_SPECULAR, white_light);
+#endif
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
+ glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
+ glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_LIGHT1);
+ /*glEnable(GL_COLOR_MATERIAL);*/
+ }
}
-/* filename is the name of the file to load
- * models is the pointer to where the models will be kept
- * returns a new pointer to models
- * count is number of models read
- */
-model_t * load_modelfile(char * filename, model_t * models, int * count) {
- int c;
- FILE * f;
- char buffy[256];
- int rotations[24];
- int name = 1;
- int rots = 0;
-
- f = fopen(filename, "r");
- if (f == NULL) {
- int error_msg_len = strlen(filename) + 33 + 1;
- char * error_msg = (char *) malloc(sizeof(char) * error_msg_len);
- sprintf(error_msg, "Unable to open model data file \"%s\"", filename);
- perror(error_msg);
- free(error_msg);
- return models;
- }
-
- while ((c = getc(f)) != EOF) {
- switch (c) {
- /* ignore comments */
- case '#':
- while (c != '\n')
- c = getc(f);
- break;
- case ':':
- buffy[name-1] = '\0';
- name = 0;
- break;
- case '\n':
- if (rots > 0) {
-#ifdef DEBUG
- /* print out the model we just read in */
- int i;
- printf("%s: ", buffy);
- for (i = 0; i < rots; i++) {
- switch (rotations[i]) {
- case LEFT:
- printf("L");
- break;
- case RIGHT:
- printf("R");
- break;
- case PIN:
- printf("P");
- break;
- case ZERO:
- printf("Z");
- break;
- }
- }
- printf("\n");
+void gettime(snaketime *t)
+{
+#ifdef HAVE_GETTIMEOFDAY
+#ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+ gettimeofday(t, &tzp);
+#else /* !GETTIMEOFDAY_TWO_ARGS */
+ gettimeofday(t);
+#endif /* !GETTIMEOFDAY_TWO_ARGS */
+#else /* !HAVE_GETTIMEOFDAY */
+#ifdef HAVE_FTIME
+ ftime(t);
+#endif /* HAVE_FTIME */
+#endif /* !HAVE_GETTIMEOFDAY */
+}
+
+void start_morph(unsigned int model_index, int immediate);
+
+/* wot initialises it */
+void glsnake_init(
+#ifndef HAVE_GLUT
+ModeInfo * mi
#endif
- models = add_model(models, buffy, rotations, count);
- }
- name = 1;
- rots = 0;
- break;
- default:
- if (name) {
- buffy[name-1] = c;
- name++;
- if (name > 255)
- fprintf(stderr, "buffy overflow warning\n");
- } else {
- switch (c) {
- case '0':
- case 'Z':
- rotations[rots] = ZERO;
- rots++;
- break;
- case '1':
- case 'L':
- rotations[rots] = LEFT;
- rots++;
- break;
- case '2':
- case 'P':
- rotations[rots] = PIN;
- rots++;
- break;
- case '3':
- case 'R':
- rotations[rots] = RIGHT;
- rots++;
- break;
- default:
- break;
- }
- }
- break;
- }
+) {
+#ifndef HAVE_GLUT
+ struct glsnake_cfg * bp;
+
+ /* set up the conf struct and glx contexts */
+ if (!glc) {
+ glc = (struct glsnake_cfg *) calloc(MI_NUM_SCREENS(mi), sizeof(struct glsnake_cfg));
+ if (!glc) {
+ fprintf(stderr, "%s: out of memory\n", progname);
+ exit(1);
}
- return models;
+ }
+ bp = &glc[MI_SCREEN(mi)];
+
+ if ((bp->glx_context = init_GL(mi)) != NULL) {
+ gl_init(mi);
+ glsnake_reshape(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+ }
+#else
+ gl_init();
+#endif
+
+ /* initialise conf struct */
+ memset(&bp->node, 0, sizeof(float) * NODE_COUNT);
+
+ bp->selected = 11;
+ bp->is_cyclic = 0;
+ bp->is_legal = 1;
+ bp->last_turn = -1;
+ bp->morphing = 0;
+ bp->paused = 0;
+ bp->new_morph = 0;
+
+ gettime(&bp->last_iteration);
+ memcpy(&bp->last_morph, &bp->last_iteration, sizeof(bp->last_morph));
+
+ bp->prev_colour = bp->next_colour = COLOUR_ACYCLIC;
+ bp->next_model = RAND(models);
+ bp->prev_model = START_MODEL;
+ start_morph(bp->prev_model, 1);
+
+ /* set up a font for the labels */
+#ifndef HAVE_GLUT
+ if (titles)
+ load_font(mi->dpy, "labelfont", &bp->font, &bp->font_list);
+#endif
+
+ /* build a solid display list */
+ glc->node_solid = glGenLists(1);
+ glNewList(glc->node_solid, GL_COMPILE);
+ /* corners */
+ glBegin(GL_TRIANGLES);
+ glNormal3fv(solid_prism_n[0]);
+ glVertex3fv(solid_prism_v[0]);
+ glVertex3fv(solid_prism_v[2]);
+ glVertex3fv(solid_prism_v[1]);
+
+ glNormal3fv(solid_prism_n[1]);
+ glVertex3fv(solid_prism_v[6]);
+ glVertex3fv(solid_prism_v[7]);
+ glVertex3fv(solid_prism_v[8]);
+
+ glNormal3fv(solid_prism_n[2]);
+ glVertex3fv(solid_prism_v[12]);
+ glVertex3fv(solid_prism_v[13]);
+ glVertex3fv(solid_prism_v[14]);
+
+ glNormal3fv(solid_prism_n[3]);
+ glVertex3fv(solid_prism_v[3]);
+ glVertex3fv(solid_prism_v[4]);
+ glVertex3fv(solid_prism_v[5]);
+
+ glNormal3fv(solid_prism_n[4]);
+ glVertex3fv(solid_prism_v[9]);
+ glVertex3fv(solid_prism_v[11]);
+ glVertex3fv(solid_prism_v[10]);
+
+ glNormal3fv(solid_prism_n[5]);
+ glVertex3fv(solid_prism_v[16]);
+ glVertex3fv(solid_prism_v[15]);
+ glVertex3fv(solid_prism_v[17]);
+ glEnd();
+ /* edges */
+ glBegin(GL_QUADS);
+ glNormal3fv(solid_prism_n[6]);
+ glVertex3fv(solid_prism_v[0]);
+ glVertex3fv(solid_prism_v[12]);
+ glVertex3fv(solid_prism_v[14]);
+ glVertex3fv(solid_prism_v[2]);
+
+ glNormal3fv(solid_prism_n[7]);
+ glVertex3fv(solid_prism_v[0]);
+ glVertex3fv(solid_prism_v[1]);
+ glVertex3fv(solid_prism_v[7]);
+ glVertex3fv(solid_prism_v[6]);
+
+ glNormal3fv(solid_prism_n[8]);
+ glVertex3fv(solid_prism_v[6]);
+ glVertex3fv(solid_prism_v[8]);
+ glVertex3fv(solid_prism_v[13]);
+ glVertex3fv(solid_prism_v[12]);
+
+ glNormal3fv(solid_prism_n[9]);
+ glVertex3fv(solid_prism_v[3]);
+ glVertex3fv(solid_prism_v[5]);
+ glVertex3fv(solid_prism_v[17]);
+ glVertex3fv(solid_prism_v[15]);
+
+ glNormal3fv(solid_prism_n[10]);
+ glVertex3fv(solid_prism_v[3]);
+ glVertex3fv(solid_prism_v[9]);
+ glVertex3fv(solid_prism_v[10]);
+ glVertex3fv(solid_prism_v[4]);
+
+ glNormal3fv(solid_prism_n[11]);
+ glVertex3fv(solid_prism_v[15]);
+ glVertex3fv(solid_prism_v[16]);
+ glVertex3fv(solid_prism_v[11]);
+ glVertex3fv(solid_prism_v[9]);
+
+ glNormal3fv(solid_prism_n[12]);
+ glVertex3fv(solid_prism_v[1]);
+ glVertex3fv(solid_prism_v[2]);
+ glVertex3fv(solid_prism_v[5]);
+ glVertex3fv(solid_prism_v[4]);
+
+ glNormal3fv(solid_prism_n[13]);
+ glVertex3fv(solid_prism_v[8]);
+ glVertex3fv(solid_prism_v[7]);
+ glVertex3fv(solid_prism_v[10]);
+ glVertex3fv(solid_prism_v[11]);
+
+ glNormal3fv(solid_prism_n[14]);
+ glVertex3fv(solid_prism_v[13]);
+ glVertex3fv(solid_prism_v[16]);
+ glVertex3fv(solid_prism_v[17]);
+ glVertex3fv(solid_prism_v[14]);
+ glEnd();
+
+ /* faces */
+ glBegin(GL_TRIANGLES);
+ glNormal3fv(solid_prism_n[15]);
+ glVertex3fv(solid_prism_v[0]);
+ glVertex3fv(solid_prism_v[6]);
+ glVertex3fv(solid_prism_v[12]);
+
+ glNormal3fv(solid_prism_n[19]);
+ glVertex3fv(solid_prism_v[3]);
+ glVertex3fv(solid_prism_v[15]);
+ glVertex3fv(solid_prism_v[9]);
+ glEnd();
+
+ glBegin(GL_QUADS);
+ glNormal3fv(solid_prism_n[16]);
+ glVertex3fv(solid_prism_v[1]);
+ glVertex3fv(solid_prism_v[4]);
+ glVertex3fv(solid_prism_v[10]);
+ glVertex3fv(solid_prism_v[7]);
+
+ glNormal3fv(solid_prism_n[17]);
+ glVertex3fv(solid_prism_v[8]);
+ glVertex3fv(solid_prism_v[11]);
+ glVertex3fv(solid_prism_v[16]);
+ glVertex3fv(solid_prism_v[13]);
+
+ glNormal3fv(solid_prism_n[18]);
+ glVertex3fv(solid_prism_v[2]);
+ glVertex3fv(solid_prism_v[14]);
+ glVertex3fv(solid_prism_v[17]);
+ glVertex3fv(solid_prism_v[5]);
+ glEnd();
+ glEndList();
+
+ /* build wire display list */
+ glc->node_wire = glGenLists(1);
+ glNewList(glc->node_wire, GL_COMPILE);
+ glBegin(GL_LINE_STRIP);
+ glVertex3fv(wire_prism_v[0]);
+ glVertex3fv(wire_prism_v[1]);
+ glVertex3fv(wire_prism_v[2]);
+ glVertex3fv(wire_prism_v[0]);
+ glVertex3fv(wire_prism_v[3]);
+ glVertex3fv(wire_prism_v[4]);
+ glVertex3fv(wire_prism_v[5]);
+ glVertex3fv(wire_prism_v[3]);
+ glEnd();
+ glBegin(GL_LINES);
+ glVertex3fv(wire_prism_v[1]);
+ glVertex3fv(wire_prism_v[4]);
+ glVertex3fv(wire_prism_v[2]);
+ glVertex3fv(wire_prism_v[5]);
+ glEnd();
+ glEndList();
+
+#ifdef HAVE_GLUT
+ /* initialise the rotation */
+ calc_rotation();
+#endif
}
-model_t * load_models(char * dirpath, model_t * models, int * count) {
- char name[1024];
- struct dirent * dp;
- DIR * dfd;
+void draw_title(
+#ifndef HAVE_GLUT
+ ModeInfo * mi
+#endif
+ ) {
+#ifndef HAVE_GLUT
+ struct glsnake_cfg * bp = &glc[MI_SCREEN(mi)];
+#endif
- if ((dfd = opendir(dirpath)) == NULL) {
- if (strstr(dirpath, "data") == NULL)
-/* fprintf(stderr, "load_models: can't read %s/\n", dirpath); */
- return models;
- }
- while ((dp = readdir(dfd)) != NULL) {
- if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
- continue;
- if (strlen(dirpath) + strlen(dp->d_name) + 2 > sizeof(name))
- fprintf(stderr, "load_models: name %s/%s too long\n", dirpath, dp->d_name);
- else {
- sprintf(name, "%s/%s", dirpath, dp->d_name);
- if (strcmp(&name[(int) strlen(name) - 7], "glsnake") == 0) {
-#ifdef DEBUG
- fprintf(stderr, "load_models: opening %s\n", name);
+ /* draw some text */
+ glPushAttrib((GLbitfield) GL_TRANSFORM_BIT | GL_ENABLE_BIT);
+ glDisable(GL_LIGHTING);
+ glDisable(GL_DEPTH_TEST);
+ if (transparent) {
+ glDisable(GL_BLEND);
+ }
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+#ifdef HAVE_GLUT
+ gluOrtho2D((GLdouble) 0., (GLdouble) glc->width, (GLdouble) 0., (GLdouble) glc->height);
+#else
+ gluOrtho2D((GLdouble) 0., (GLdouble) mi->xgwa.width, (GLdouble) 0., (GLdouble) mi->xgwa.height);
+#endif
+ glColor4f(1.0, 1.0, 1.0, 1.0);
+ {
+ char interactstr[] = "interactive";
+ const char * s;
+#ifdef HAVE_GLUT
+ int w;
#endif
- models = load_modelfile(name, models, count);
- }
- }
+
+ if (interactive)
+ s = interactstr;
+ else
+ s = model[glc->next_model].name;
+
+#ifdef HAVE_GLUT
+ {
+ unsigned int i = 0;
+
+ w = glutBitmapLength(GLUT_BITMAP_HELVETICA_12, (const unsigned char *) s);
+ glRasterPos2f((GLfloat) (glc->width - w - 3), 4.0);
+ while (s[i] != '\0')
+ glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, s[i++]);
}
- closedir(dfd);
- return models;
+#else
+ print_gl_string(mi->dpy, bp->font, bp->font_list,
+ mi->xgwa.width, mi->xgwa.height,
+ 10.0, (float) mi->xgwa.height - 10.0,
+ s);
+#endif
+ }
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glPopAttrib();
}
-/* snake metrics */
-#define X_MASK 1
-#define Y_MASK 2
-#define Z_MASK 4
-#define GETSCALAR(vec,mask) ((vec)==(mask) ? 1 : ((vec)==-(mask) ? -1 : 0 ))
+/* apply the matrix to the origin and stick it in vec */
+void matmult_origin(float rotmat[16], float vec[4]) {
+#if 1
+ vec[0] = 0.5 * rotmat[0] + 0.5 * rotmat[4] + 0.5 * rotmat [8] + 1 * rotmat[12];
+ vec[1] = 0.5 * rotmat[1] + 0.5 * rotmat[5] + 0.5 * rotmat [9] + 1 * rotmat[13];
+ vec[2] = 0.5 * rotmat[2] + 0.5 * rotmat[6] + 0.5 * rotmat[10] + 1 * rotmat[14];
+ vec[3] = 0.5 * rotmat[3] + 0.5 * rotmat[7] + 0.5 * rotmat[11] + 1 * rotmat[15];
+#else
+ vec[0] = 0 * rotmat [0] + 0 * rotmat [1] + 0 * rotmat [2] + 1 * rotmat [3];
+ vec[1] = 0 * rotmat [4] + 0 * rotmat [5] + 0 * rotmat [6] + 1 * rotmat [7];
+ vec[2] = 0 * rotmat [8] + 0 * rotmat [9] + 0 * rotmat[10] + 1 * rotmat[11];
+ vec[3] = 0 * rotmat[12] + 0 * rotmat[13] + 0 * rotmat[14] + 1 * rotmat[15];
+#endif
+ vec[0] /= vec[3];
+ vec[1] /= vec[3];
+ vec[2] /= vec[3];
+ vec[3] = 1.0;
+}
-int cross_product(int src_dir, int dest_dir) {
- return X_MASK * (GETSCALAR(src_dir, Y_MASK) * GETSCALAR(dest_dir, Z_MASK) -
- GETSCALAR(src_dir, Z_MASK) * GETSCALAR(dest_dir, Y_MASK)) +
- Y_MASK * (GETSCALAR(src_dir, Z_MASK) * GETSCALAR(dest_dir, X_MASK) -
- GETSCALAR(src_dir, X_MASK) * GETSCALAR(dest_dir, Z_MASK)) +
- Z_MASK * (GETSCALAR(src_dir, X_MASK) * GETSCALAR(dest_dir, Y_MASK) -
- GETSCALAR(src_dir, Y_MASK) * GETSCALAR(dest_dir, X_MASK));
+/* wot gets called when the winder is resized */
+void glsnake_reshape(
+#ifndef HAVE_GLUT
+ ModeInfo * mi,
+#endif
+ int w, int h) {
+ glViewport(0, 0, (GLint) w, (GLint) h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective(zoom, (GLdouble) w / (GLdouble) h, 0.05, 100.0);
+ gluLookAt(0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
+ glMatrixMode(GL_MODELVIEW);
+ /*gluLookAt(0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);*/
+ glLoadIdentity();
+#ifdef HAVE_GLUT
+ glc->width = w;
+ glc->height = h;
+#endif
}
-void calc_snake_metrics(glsnake_configuration * bp) {
- int src_dir, dest_dir;
- int i, x, y, z;
- int prev_src_dir = -Y_MASK;
- int prev_dest_dir = Z_MASK;
- int grid[25][25][25];
-
- /* zero the grid */
- memset(&grid, 0, sizeof(int) * 25*25*25);
-
- bp->is_legal = 1;
- x = y = z = 12;
-
- /* trace path of snake and keep record for is_legal */
- for (i = 0; i < 23; i++) {
- /* establish new state variables */
- src_dir = -prev_dest_dir;
- x += GETSCALAR(prev_dest_dir, X_MASK);
- y += GETSCALAR(prev_dest_dir, Y_MASK);
- z += GETSCALAR(prev_dest_dir, Z_MASK);
-
- switch ((int) bp->node[i].dest_angle) {
- case (int) (ZERO * 90.0):
- dest_dir = -prev_src_dir;
- break;
- case (int) (PIN * 90.0):
- dest_dir = prev_src_dir;
- break;
- case (int) (RIGHT * 90.):
- case (int) (LEFT * 90.0):
- dest_dir = cross_product(prev_src_dir, prev_dest_dir);
- if (bp->node[i].dest_angle == (int) (RIGHT * 90.0))
- dest_dir = -dest_dir;
- break;
- default:
- /* prevent spurious "might be used uninitialised" warnings */
- dest_dir = 0;
- break;
- }
-
- if (grid[x][y][z] == 0)
- grid[x][y][z] = src_dir + dest_dir;
- else if (grid[x][y][z] + src_dir + dest_dir == 0)
- grid[x][y][z] = 8;
- else
- bp->is_legal = 0;
+/* Returns the new dst_dir for the given src_dir and dst_dir */
+int cross_product(int src_dir, int dst_dir) {
+ return X_MASK*(GETSCALAR(src_dir,Y_MASK) * GETSCALAR(dst_dir,Z_MASK) -
+ GETSCALAR(src_dir,Z_MASK) * GETSCALAR(dst_dir,Y_MASK))+
+ Y_MASK*(GETSCALAR(src_dir,Z_MASK) * GETSCALAR(dst_dir,X_MASK) -
+ GETSCALAR(src_dir,X_MASK) * GETSCALAR(dst_dir,Z_MASK))+
+ Z_MASK*(GETSCALAR(src_dir,X_MASK) * GETSCALAR(dst_dir,Y_MASK) -
+ GETSCALAR(src_dir,Y_MASK) * GETSCALAR(dst_dir,X_MASK));
+}
- prev_src_dir = src_dir;
- prev_dest_dir = dest_dir;
+/* calculate orthogonal snake metrics
+ * is_legal = true if model does not pass through itself
+ * is_cyclic = true if last node connects back to first node
+ * last_turn = for cyclic snakes, specifes what the 24th turn would be
+ */
+void calc_snake_metrics(void) {
+ int srcDir, dstDir;
+ int i, x, y, z;
+ int prevSrcDir = -Y_MASK;
+ int prevDstDir = Z_MASK;
+ int grid[25][25][25];
+
+ /* zero the grid */
+ memset(&grid, 0, sizeof(int) * 25*25*25);
+
+ glc->is_legal = 1;
+ x = y = z = 12;
+
+ /* trace path of snake - and keep record for is_legal */
+ for (i = 0; i < NODE_COUNT - 1; i++) {
+ /*int ang_card;*/ /* cardinal direction of node angle */
+ /* establish new state vars */
+ srcDir = -prevDstDir;
+ x += GETSCALAR(prevDstDir, X_MASK);
+ y += GETSCALAR(prevDstDir, Y_MASK);
+ z += GETSCALAR(prevDstDir, Z_MASK);
+
+ switch ((int) model[glc->next_model].node[i]) {
+ case (int) (ZERO):
+ dstDir = -prevSrcDir;
+ break;
+ case (int) (PIN):
+ dstDir = prevSrcDir;
+ break;
+ case (int) (RIGHT):
+ case (int) (LEFT):
+ dstDir = cross_product(prevSrcDir, prevDstDir);
+ if (model[glc->next_model].node[i] == (int) (RIGHT))
+ dstDir = -dstDir;
+ break;
+ default:
+ /* Prevent spurious "might be used
+ * uninitialised" warnings when compiling
+ * with -O2 */
+ dstDir = 0;
+ break;
}
-
- /* determine if the snake is cyclic */
- bp->is_cyclic = (dest_dir == Y_MASK && x == 12 && y == 11 && x == 12);
-
- /* determine last turn */
- bp->last_turn = -1;
- if (bp->is_cyclic) {
- switch (src_dir) {
- case -Z_MASK:
- bp->last_turn = ZERO * 90.0;
- break;
- case Z_MASK:
- bp->last_turn = PIN * 90.0;
- break;
- case X_MASK:
- bp->last_turn = LEFT * 90.0;
- break;
- case -X_MASK:
- bp->last_turn = RIGHT * 90.0;
- break;
- }
+
+ if (grid[x][y][z] == 0)
+ grid[x][y][z] = srcDir + dstDir;
+ else if (grid[x][y][z] + srcDir + dstDir == 0)
+ grid[x][y][z] = 8;
+ else
+ glc->is_legal = 0;
+
+ prevSrcDir = srcDir;
+ prevDstDir = dstDir;
+ }
+
+ /* determine if the snake is cyclic */
+ glc->is_cyclic = (dstDir == Y_MASK && x == 12 && y == 11 && z == 12);
+
+ /* determine last_turn */
+ glc->last_turn = -1;
+ if (glc->is_cyclic)
+ switch (srcDir) {
+ case -Z_MASK: glc->last_turn = ZERO; break;
+ case Z_MASK: glc->last_turn = PIN; break;
+ case X_MASK: glc->last_turn = LEFT; break;
+ case -X_MASK: glc->last_turn = RIGHT; break;
}
}
-void set_colours(glsnake_configuration * bp, int immediate) {
- /* set target colour */
- if (!bp->is_legal) {
- bp->colour_t[0] = 0.5;
- bp->colour_t[1] = 0.5;
- bp->colour_t[2] = 0.5;
- } else if (bp->is_cyclic) {
- bp->colour_t[0] = 0.4;
- bp->colour_t[1] = 0.8;
- bp->colour_t[2] = 0.2;
- } else {
- bp->colour_t[0] = 0.3;
- bp->colour_t[1] = 0.1;
- bp->colour_t[2] = 0.9;
- }
- if (immediate) {
- bp->colour_i[0] = bp->colour_t[0] - bp->colour[0];
- bp->colour_i[1] = bp->colour_t[1] - bp->colour[1];
- bp->colour_i[2] = bp->colour_t[2] - bp->colour[2];
- } else {
- /* instead of 50.0, I should actually work out how many times this gets
- * called during a morph */
- bp->colour_i[0] = (bp->colour_t[0] - bp->colour[0]) / 50.0;
- bp->colour_i[1] = (bp->colour_t[1] - bp->colour[1]) / 50.0;
- bp->colour_i[2] = (bp->colour_t[2] - bp->colour[2]) / 50.0;
+/* work out how far through the current morph we are */
+float morph_percent(void) {
+ float retval;
+ int i;
+
+ /* extend this function later with a case statement for each of the
+ * morph schemes */
+
+ /* when morphing all nodes at once, the longest morph will be the node
+ * that needs to rotate 180 degrees. For each node, work out how far it
+ * has to go, and store the maximum rotation and current largest angular
+ * difference, returning the angular difference over the maximum. */
+ {
+ float rot_max = 0.0, ang_diff_max = 0.0;
+
+ for (i = 0; i < NODE_COUNT - 1; i++) {
+ float rot, ang_diff;
+
+ /* work out the maximum rotation this node has to go through
+ * from the previous to the next model, taking into account that
+ * the snake always morphs through the smaller angle */
+ rot = fabs(model[glc->prev_model].node[i] -
+ model[glc->next_model].node[i]);
+ if (rot > 180.0) rot = 180.0 - rot;
+ /* work out the difference between the current position and the
+ * target */
+ ang_diff = fabs(glc->node[i] -
+ model[glc->next_model].node[i]);
+ if (ang_diff > 180.0) ang_diff = 180.0 - ang_diff;
+ /* if it's the biggest so far, record it */
+ if (rot > rot_max) rot_max = rot;
+ if (ang_diff > ang_diff_max) ang_diff_max = ang_diff;
}
+
+ /* ang_diff / rot approaches 0, we want the complement */
+ retval = 1.0 - (ang_diff_max / rot_max);
+ /* protect against naan */
+
+/* Apparently some systems (Solaris) don't have isinf() */
+#undef isinf
+#define isinf(x) (((x) > 999999999999.9) || ((x) < -999999999999.9))
+
+ if (isnan(retval) || isinf(retval)) retval = 1.0;
+ }
+ /*printf("morph_pct = %f\n", retval);*/
+ return retval;
+}
+
+void morph_colour(void) {
+ float percent, compct; /* complement of percentage */
+
+ percent = morph_percent();
+ compct = 1.0 - percent;
+
+ glc->colour[0][0] = colour[glc->prev_colour][0][0] * compct + colour[glc->next_colour][0][0] * percent;
+ glc->colour[0][1] = colour[glc->prev_colour][0][1] * compct + colour[glc->next_colour][0][1] * percent;
+ glc->colour[0][2] = colour[glc->prev_colour][0][2] * compct + colour[glc->next_colour][0][2] * percent;
+ glc->colour[0][3] = colour[glc->prev_colour][0][3] * compct + colour[glc->next_colour][0][3] * percent;
+
+ glc->colour[1][0] = colour[glc->prev_colour][1][0] * compct + colour[glc->next_colour][1][0] * percent;
+ glc->colour[1][1] = colour[glc->prev_colour][1][1] * compct + colour[glc->next_colour][1][1] * percent;
+ glc->colour[1][2] = colour[glc->prev_colour][1][2] * compct + colour[glc->next_colour][1][2] * percent;
+ glc->colour[1][3] = colour[glc->prev_colour][1][3] * compct + colour[glc->next_colour][1][3] * percent;
}
-void start_morph(int model_index, int immediate, glsnake_configuration * bp) {
+/* Start morph process to this model */
+void start_morph(unsigned int model_index, int immediate) {
+ /* if immediate, don't bother morphing, go straight to the next model */
+ if (immediate) {
int i;
- for (i = 0; i < 23; i++) {
- bp->node[i].dest_angle = bp->models[model_index].node[i];
- if (immediate)
- bp->node[i].cur_angle = bp->models[model_index].node[i];
+ for (i = 0; i < NODE_COUNT; i++)
+ glc->node[i] = model[model_index].node[i];
+ }
+
+ glc->prev_model = glc->next_model;
+ glc->next_model = model_index;
+ glc->prev_colour = glc->next_colour;
+
+ calc_snake_metrics();
+ if (!glc->is_legal)
+ glc->next_colour = COLOUR_INVALID;
+ else if (altcolour)
+ glc->next_colour = COLOUR_AUTHENTIC;
+ else if (glc->is_cyclic)
+ glc->next_colour = COLOUR_CYCLIC;
+ else
+ glc->next_colour = COLOUR_ACYCLIC;
+
+ if (immediate) {
+ glc->colour[0][0] = colour[glc->next_colour][0][0];
+ glc->colour[0][1] = colour[glc->next_colour][0][1];
+ glc->colour[0][2] = colour[glc->next_colour][0][2];
+ glc->colour[0][3] = colour[glc->next_colour][0][3];
+ glc->colour[1][0] = colour[glc->next_colour][1][0];
+ glc->colour[1][1] = colour[glc->next_colour][1][1];
+ glc->colour[1][2] = colour[glc->next_colour][1][2];
+ glc->colour[1][3] = colour[glc->next_colour][1][3];
+ }
+ glc->morphing = 1;
+
+ morph_colour();
+}
+
+/* Returns morph progress */
+float morph(long iter_msec) {
+ /* work out the maximum angle for this iteration */
+ int still_morphing;
+ float iter_angle_max, largest_diff, largest_progress;
+ int i;
+
+ if (glc->new_morph)
+ glc->new_morph = 0;
+
+ iter_angle_max = 90.0 * (angvel/1000.0) * iter_msec;
+
+ still_morphing = 0;
+ largest_diff = largest_progress = 0.0;
+ for (i = 0; i < NODE_COUNT; i++) {
+ float curAngle = glc->node[i];
+ float destAngle = model[glc->next_model].node[i];
+ if (curAngle != destAngle) {
+ still_morphing = 1;
+ if (fabs(curAngle-destAngle) <= iter_angle_max)
+ glc->node[i] = destAngle;
+ else if (fmod(curAngle-destAngle+360,360) > 180)
+ glc->node[i] = fmod(curAngle + iter_angle_max, 360);
+ else
+ glc->node[i] = fmod(curAngle+360 - iter_angle_max, 360);
+ largest_diff = MAX(largest_diff, fabs(destAngle-glc->node[i]));
+ largest_progress = MAX(largest_diff, fabs(glc->node[i] - model[glc->prev_model].node[i]));
}
+ }
- calc_snake_metrics(bp);
- set_colours(bp, 0);
- bp->cur_model = model_index;
- bp->morphing = 1;
+ return MIN(largest_diff / largest_progress, 1.0);
}
-/* convex hull */
+#ifdef HAVE_GLUT
+void glsnake_idle();
+
+void restore_idle(int v __attribute__((__unused__)))
+{
+ glutIdleFunc(glsnake_idle);
+}
+#endif
-/* model labels */
-void draw_label(ModeInfo * mi) {
- glsnake_configuration *bp = &glc[MI_SCREEN(mi)];
+void quick_sleep(void)
+{
+#ifdef HAVE_GLUT
+ /* By using glutTimerFunc we can keep responding to
+ * mouse and keyboard events, unlike using something like
+ * usleep. */
+ glutIdleFunc(NULL);
+ glutTimerFunc(1, restore_idle, 0);
+#else
+ usleep(1);
+#endif
+}
+
+void glsnake_idle(
+#ifndef HAVE_GLUT
+ struct glsnake_cfg * bp
+#endif
+ ) {
+ /* time since last iteration */
+ long iter_msec;
+ /* time since the beginning of last morph */
+ long morf_msec;
+ float iter_angle_max;
+ snaketime current_time;
+ /* morphFunc transition; */
+ int still_morphing;
+ int i;
+
+ /* Do nothing to the model if we are paused */
+ if (glc->paused) {
+ /* Avoid busy waiting when nothing is changing */
+ quick_sleep();
+#ifdef HAVE_GLUT
+ glutSwapBuffers();
+ glutPostRedisplay();
+#endif
+ return;
+ }
+
+ /* <spiv> Well, ftime gives time with millisecond resolution.
+ * <spiv> (or worse, perhaps... who knows what the OS will do)
+ * <spiv> So if no discernable amount of time has passed:
+ * <spiv> a) There's no point updating the screen, because
+ * it would be the same
+ * <spiv> b) The code will divide by zero
+ */
+ gettime(¤t_time);
+
+ iter_msec = (long) GETMSECS(current_time) - GETMSECS(glc->last_iteration) +
+ ((long) GETSECS(current_time) - GETSECS(glc->last_iteration)) * 1000L;
+
+ if (iter_msec) {
+ /* save the current time */
+ memcpy(&glc->last_iteration, ¤t_time, sizeof(snaketime));
- glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT);
- glDisable(GL_LIGHTING);
- glDisable(GL_DEPTH_TEST);
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
- gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
- glColor3f(1.0, 1.0, 0.0);
- {
- char * s;
- int i, /* w, */ l;
+ /* work out if we have to switch models */
+ morf_msec = GETMSECS(glc->last_iteration) - GETMSECS(glc->last_morph) +
+ ((long) (GETSECS(glc->last_iteration)-GETSECS(glc->last_morph)) * 1000L);
+
+ if ((morf_msec > statictime) && !interactive && !glc->morphing) {
+ /*printf("starting morph\n");*/
+ memcpy(&glc->last_morph, &(glc->last_iteration), sizeof(glc->last_morph));
+ start_morph(RAND(models), 0);
+ }
+
+ if (interactive && !glc->morphing) {
+ quick_sleep();
+ return;
+ }
+
+ /* if (!glc->dragging && !glc->interactive) { */
+ if (!interactive) {
+
+ yspin += 360/((1000/yangvel)/iter_msec);
+ zspin += 360/((1000/zangvel)/iter_msec);
+ /*
+ yspin += 360 * (yangvel/1000.0) * iter_msec;
+ zspin += 360 * (zangvel/1000.0) * iter_msec;
+ */
+
+ /*printf("yspin: %f, zspin: %f\n", yspin, zspin);*/
- if (bp->interactive)
- s = "interactive";
- else
- s = bp->models[bp->cur_model].name;
+ }
- l = strlen(s);
- /*
- w = 0;
- for (i = 0; i < l; i++) {
- w += (bp->font->per_char
- ? bp->font->per_char[((int)s[i]) - bp->font->min_char_or_byte2].rbearing
- : bp->font->min_bounds.rbearing);
- }
- */
-
- glRasterPos2f(10, mi->xgwa.height - 10 - (bp->font->ascent + bp->font->descent));
- /* mi->xgwa.width - w, bp->font->descent + bp->font->ascent); */
-
- /* fprintf(stderr, "afaf.width = %d, w = %d\n", mi->xgwa.width, w); */
-
- for (i = 0; i < l; i++)
- glCallList(bp->font_list + (int)s[i]);
+ /* work out the maximum angle we could turn this node in this
+ * timeslice, iter_msec milliseconds long */
+ iter_angle_max = 90.0 * (angvel/1000.0) * iter_msec;
+
+ still_morphing = 0;
+ for (i = 0; i < NODE_COUNT; i++) {
+ float cur_angle = glc->node[i];
+ float dest_angle = model[glc->next_model].node[i];
+ if (cur_angle != dest_angle) {
+ still_morphing = 1;
+ if (fabs(cur_angle - dest_angle) <= iter_angle_max)
+ glc->node[i] = dest_angle;
+ else if (fmod(cur_angle - dest_angle + 360, 360) > 180)
+ glc->node[i] = fmod(cur_angle + iter_angle_max, 360);
+ else
+ glc->node[i] = fmod(cur_angle + 360 - iter_angle_max, 360);
+ }
}
- glPopMatrix();
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glPopAttrib();
+
+ if (!still_morphing)
+ glc->morphing = 0;
+
+ /* colour cycling */
+ morph_colour();
+
+#ifdef HAVE_GLUT
+ glutSwapBuffers();
+ glutPostRedisplay();
+#endif
+ } else {
+ /* We are going too fast, so we may as well let the
+ * cpu relax a little by sleeping for a millisecond. */
+ quick_sleep();
+ }
}
-/* load the fonts -- this function borrowed from molecule.c */
-static void load_font(ModeInfo * mi, char * res, XFontStruct ** fontp, GLuint * dlistp) {
- const char * font = get_string_resource(res, "Font");
- XFontStruct * f;
- Font id;
- int first, last;
+/* wot draws it */
+void glsnake_display(
+#ifndef HAVE_GLUT
+ ModeInfo * mi
+#endif
+ ) {
+#ifndef HAVE_GLUT
+ struct glsnake_cfg * bp = &glc[MI_SCREEN(mi)];
+ Display * dpy = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+#endif
- if (!font)
- font = "-*-helvetica-medium-r-*-*-*-120-*";
+ int i;
+ float ang;
+ float positions[NODE_COUNT][4]; /* origin points for each node */
+ float com[4]; /* it's the CENTRE of MASS */
- f = XLoadQueryFont(mi->dpy, font);
- if (!f)
- f = XLoadQueryFont(mi->dpy, "fixed");
+#ifndef HAVE_GLUT
+ if (!bp->glx_context)
+ return;
+#endif
+
+ /* clear the buffer */
+ glClear((GLbitfield) GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ /* go into the modelview stack */
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ /* get the centre of each node, by moving through the snake and
+ * performing the rotations, then grabbing the matrix at each point
+ * and applying it to the origin */
+ glPushMatrix();
+
+#ifdef HAVE_GLUT
+ /* apply the mouse drag rotation */
+ ui_mousedrag();
+#endif
+
+ /* apply the continuous rotation */
+ glRotatef(yspin, 0.0, 1.0, 0.0);
+ glRotatef(zspin, 0.0, 0.0, 1.0);
+
+ com[0] = 0.0;
+ com[1] = 0.0;
+ com[2] = 0.0;
+ com[3] = 0.0;
+ for (i = 0; i < NODE_COUNT; i++) {
+ float rotmat[16];
+
+ ang = glc->node[i];
- id = f->fid;
- first = f->min_char_or_byte2;
- last = f->max_char_or_byte2;
+ /*printf("ang = %f\n", ang);*/
- clear_gl_error();
- *dlistp = glGenLists((GLuint) last + 1);
- check_gl_error("glGenLists");
- glXUseXFont(id, first, last - first + 1, *dlistp + first);
- check_gl_error("glXUseXFont");
+ glTranslatef(0.5, 0.5, 0.5); /* move to center */
+ glRotatef(90.0, 0.0, 0.0, -1.0); /* reorient */
+ glTranslatef(1.0 + explode, 0.0, 0.0); /* move to new pos. */
+ glRotatef(180.0 + ang, 1.0, 0.0, 0.0); /* pivot to new angle */
+ glTranslatef(-0.5, -0.5, -0.5); /* return from center */
+
+ glGetFloatv(GL_MODELVIEW_MATRIX, rotmat);
+
+ matmult_origin(rotmat, positions[i]);
+
+ /*printf("positions %f %f %f %f\n", positions[i][0], positions[i][1], positions[i][2], positions[i][3]);*/
+
+ com[0] += positions[i][0];
+ com[1] += positions[i][1];
+ com[2] += positions[i][2];
+ com[3] += positions[i][3];
+ }
+ glPopMatrix();
+ com[0] /= NODE_COUNT;
+ com[1] /= NODE_COUNT;
+ com[2] /= NODE_COUNT;
+ com[3] /= NODE_COUNT;
+
+ com[0] /= com[3];
+ com[1] /= com[3];
+ com[2] /= com[3];
+
+ /*printf("com: %f, %f, %f, %f\n", com[0], com[1], com[2], com[3]);*/
+
+#if MAGICAL_RED_STRING
+ glPushMatrix();
+ glTranslatef(-com[0], -com[1], -com[2]);
+
+ glDisable(GL_LIGHTING);
+ glColor4f(1.0, 0.0, 0.0, 1.0);
+ glBegin(GL_LINE_STRIP);
+ for (i = 0; i < NODE_COUNT - 1; i++) {
+ glVertex3fv(positions[i]);
+ }
+ glEnd();
+ glEnable(GL_LIGHTING);
+ /*glTranslatef(com[0], com[1], com[2]);*/
+ glPopMatrix();
+#endif
- *fontp = f;
-}
+ glPushMatrix();
+ glTranslatef(-com[0], -com[1], -com[2]);
+
+#ifdef HAVE_GLUT
+ /* apply the mouse drag rotation */
+ ui_mousedrag();
+#endif
+
+ /* apply the continuous rotation */
+ glRotatef(yspin, 0.0, 1.0, 0.0);
+ glRotatef(zspin, 0.0, 0.0, 1.0);
+
+ /* now draw each node along the snake -- this is quite ugly :p */
+ for (i = 0; i < NODE_COUNT; i++) {
+ /* choose a colour for this node */
+ if ((i == glc->selected || i == glc->selected+1) && interactive)
+ /* yellow */
+ glColor4f(1.0, 1.0, 0.0, 1.0);
+ else {
+ /*glColor4fv(glc->colour[(i+1)%2]);*/
+ glMaterialfv(GL_FRONT, GL_AMBIENT, glc->colour[(i+1)%2]);
+ glMaterialfv(GL_FRONT, GL_DIFFUSE, glc->colour[(i+1)%2]);
+ /*glMaterialfv(GL_FRONT, GL_SPECULAR, glc->colour[(i+1)%2]);*/
+ }
+ /* draw the node */
+ if (wireframe)
+ glCallList(glc->node_wire);
+ else
+ glCallList(glc->node_solid);
+ /* now work out where to draw the next one */
+
+ /* Interpolate between models */
+ ang = glc->node[i];
+
+ glTranslatef(0.5, 0.5, 0.5); /* move to center */
+ glRotatef(90.0, 0.0, 0.0, -1.0); /* reorient */
+ glTranslatef(1.0 + explode, 0.0, 0.0); /* move to new pos. */
+ glRotatef(180.0 + ang, 1.0, 0.0, 0.0); /* pivot to new angle */
+ glTranslatef(-0.5, -0.5, -0.5); /* return from center */
+ }
+
+ glPopMatrix();
+
+ if (titles)
+#ifdef HAVE_GLUT
+ draw_title();
+#else
+ draw_title(mi);
+#endif
-/* window management */
-void glsnake_reshape(ModeInfo *mi, int w, int h) {
- glViewport (0, 0, (GLint) w, (GLint) h);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- gluPerspective(25.0, w/(GLfloat)h, 1.0, 100.0 );
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
+#ifndef HAVE_GLUT
+ glsnake_idle(bp);
+#endif
+
+ glFlush();
+#ifdef HAVE_GLUT
+ glutSwapBuffers();
+#else
+ glXSwapBuffers(dpy, window);
+#endif
}
-static void gl_init(ModeInfo *mi) {
- /* glsnake_configuration *bp = &glc[MI_SCREEN(mi)]; */
- int wire = MI_IS_WIREFRAME(mi);
- float light_pos[][3] = {{0.0,0.0,20.0},{0.0,20.0,0.0}};
- float light_dir[][3] = {{0.0,0.0,-20.0},{0.0,-20.0,0.0}};
-
- glClearColor(0.0, 0.0, 0.0, 0.0);
- glEnable(GL_DEPTH_TEST);
- glShadeModel(GL_SMOOTH);
- glCullFace(GL_BACK);
- glEnable(GL_CULL_FACE);
- glEnable(GL_NORMALIZE);
-
- if (!wire) {
- glColor3f(1.0, 1.0, 1.0);
- glLightfv(GL_LIGHT0, GL_POSITION, light_pos[0]);
- glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_dir[0]);
- glLightfv(GL_LIGHT1, GL_POSITION, light_pos[1]);
- glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_dir[1]);
- glEnable(GL_LIGHTING);
- glEnable(GL_LIGHT0);
- glEnable(GL_LIGHT1);
- glEnable(GL_COLOR_MATERIAL);
- }
+#ifdef HAVE_GLUT
+/* anything that needs to be cleaned up goes here */
+void unmain() {
+ glutDestroyWindow(glc->window);
+ free(glc);
}
-/* lifted from lament.c */
-#define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
-#define RANDSIGN() ((random() & 1) ? 1 : -1)
+void ui_init(int *, char **);
-void glsnake_init(ModeInfo *mi) {
- glsnake_configuration * bp;
- int wire = MI_IS_WIREFRAME(mi);
+int main(int argc, char ** argv) {
+ glc = malloc(sizeof(struct glsnake_cfg));
+ memset(glc, 0, sizeof(struct glsnake_cfg));
- if (!glc) {
- glc = (glsnake_configuration *) calloc(MI_NUM_SCREENS(mi), sizeof(glsnake_configuration));
- if (!glc) {
- fprintf(stderr, "%s: out of memory\n", progname);
- exit(1);
- }
- bp = &glc[MI_SCREEN(mi)];
- }
+ glc->width = 640;
+ glc->height = 480;
+
+ ui_init(&argc, argv);
- bp = &glc[MI_SCREEN(mi)];
+ gettime(&glc->last_iteration);
+ memcpy(&glc->last_morph, &glc->last_iteration, sizeof(snaketime));
+ srand((unsigned int)GETSECS(glc->last_iteration));
- if ((bp->glx_context = init_GL(mi)) != NULL) {
- gl_init(mi);
- glsnake_reshape(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
- }
+ glc->prev_colour = glc->next_colour = COLOUR_ACYCLIC;
+ glc->next_model = RAND(models);
+ glc->prev_model = 0;
+ start_morph(glc->prev_model, 1);
- /* initialise config variables */
- memset(&bp->node, 0, sizeof(nodeang_t) * 24);
- bp->m_count = sizeof(default_models) / sizeof(model_t); /* overwrite this in a bit */
- bp->selected = 11;
- bp->is_cyclic = 0;
- bp->is_legal = 1;
- bp->last_turn = -1;
- bp->roty = 0.0;
- bp->rotz = 0.0;
- bp->morphing = 0;
- bp->paused = 0;
- bp->dragging = 0;
- bp->interactive = 0;
-
- ftime(&(bp->last_iteration));
- memcpy(&(bp->last_morph), &(bp->last_iteration), sizeof(struct timeb));
- /* srand((unsigned int) bp->last_iteration.time); */
-
- /* load the model files */
- /* first copy the defaults to bp->m_count */
- bp->models = (model_t *) malloc(sizeof(model_t) * bp->m_count);
- memcpy(bp->models, default_models, bp->m_count * sizeof(model_t));
- /* then add on models from the Debian model file location */
- bp->models = load_models("/usr/share/glsnake", bp->models, &(bp->m_count));
-
- bp->m = bp->cur_model = RAND(bp->m_count);
- start_morph(bp->cur_model, 1, bp);
-
- calc_snake_metrics(bp);
- set_colours(bp, 1);
-
- /* set up a font for the labels */
- if (labels)
- load_font(mi, "labelfont", &bp->font, &bp->font_list);
-
- bp->node_list = glGenLists(1);
- glNewList(bp->node_list, GL_COMPILE);
- if (!wire) {
- /* corners */
- glBegin(GL_TRIANGLES);
- glNormal3fv(solid_prism_n[0]);
- glVertex3fv(solid_prism_v[0]);
- glVertex3fv(solid_prism_v[2]);
- glVertex3fv(solid_prism_v[1]);
+ glsnake_init();
- glNormal3fv(solid_prism_n[1]);
- glVertex3fv(solid_prism_v[6]);
- glVertex3fv(solid_prism_v[7]);
- glVertex3fv(solid_prism_v[8]);
-
- glNormal3fv(solid_prism_n[2]);
- glVertex3fv(solid_prism_v[12]);
- glVertex3fv(solid_prism_v[13]);
- glVertex3fv(solid_prism_v[14]);
+ atexit(unmain);
+ glutSwapBuffers();
+ glutMainLoop();
- glNormal3fv(solid_prism_n[3]);
- glVertex3fv(solid_prism_v[3]);
- glVertex3fv(solid_prism_v[4]);
- glVertex3fv(solid_prism_v[5]);
-
- glNormal3fv(solid_prism_n[4]);
- glVertex3fv(solid_prism_v[9]);
- glVertex3fv(solid_prism_v[11]);
- glVertex3fv(solid_prism_v[10]);
-
- glNormal3fv(solid_prism_n[5]);
- glVertex3fv(solid_prism_v[16]);
- glVertex3fv(solid_prism_v[15]);
- glVertex3fv(solid_prism_v[17]);
- glEnd();
-
- /* edges */
- glBegin(GL_QUADS);
- glNormal3fv(solid_prism_n[6]);
- glVertex3fv(solid_prism_v[0]);
- glVertex3fv(solid_prism_v[12]);
- glVertex3fv(solid_prism_v[14]);
- glVertex3fv(solid_prism_v[2]);
-
- glNormal3fv(solid_prism_n[7]);
- glVertex3fv(solid_prism_v[0]);
- glVertex3fv(solid_prism_v[1]);
- glVertex3fv(solid_prism_v[7]);
- glVertex3fv(solid_prism_v[6]);
-
- glNormal3fv(solid_prism_n[8]);
- glVertex3fv(solid_prism_v[6]);
- glVertex3fv(solid_prism_v[8]);
- glVertex3fv(solid_prism_v[13]);
- glVertex3fv(solid_prism_v[12]);
-
- glNormal3fv(solid_prism_n[9]);
- glVertex3fv(solid_prism_v[3]);
- glVertex3fv(solid_prism_v[5]);
- glVertex3fv(solid_prism_v[17]);
- glVertex3fv(solid_prism_v[15]);
-
- glNormal3fv(solid_prism_n[10]);
- glVertex3fv(solid_prism_v[3]);
- glVertex3fv(solid_prism_v[9]);
- glVertex3fv(solid_prism_v[10]);
- glVertex3fv(solid_prism_v[4]);
-
- glNormal3fv(solid_prism_n[11]);
- glVertex3fv(solid_prism_v[15]);
- glVertex3fv(solid_prism_v[16]);
- glVertex3fv(solid_prism_v[11]);
- glVertex3fv(solid_prism_v[9]);
-
- glNormal3fv(solid_prism_n[12]);
- glVertex3fv(solid_prism_v[1]);
- glVertex3fv(solid_prism_v[2]);
- glVertex3fv(solid_prism_v[5]);
- glVertex3fv(solid_prism_v[4]);
-
- glNormal3fv(solid_prism_n[13]);
- glVertex3fv(solid_prism_v[8]);
- glVertex3fv(solid_prism_v[7]);
- glVertex3fv(solid_prism_v[10]);
- glVertex3fv(solid_prism_v[11]);
-
- glNormal3fv(solid_prism_n[14]);
- glVertex3fv(solid_prism_v[13]);
- glVertex3fv(solid_prism_v[16]);
- glVertex3fv(solid_prism_v[17]);
- glVertex3fv(solid_prism_v[14]);
- glEnd();
-
- /* faces */
- glBegin(GL_TRIANGLES);
- glNormal3fv(solid_prism_n[15]);
- glVertex3fv(solid_prism_v[0]);
- glVertex3fv(solid_prism_v[6]);
- glVertex3fv(solid_prism_v[12]);
-
- glNormal3fv(solid_prism_n[19]);
- glVertex3fv(solid_prism_v[3]);
- glVertex3fv(solid_prism_v[15]);
- glVertex3fv(solid_prism_v[9]);
- glEnd();
-
- glBegin(GL_QUADS);
- glNormal3fv(solid_prism_n[16]);
- glVertex3fv(solid_prism_v[1]);
- glVertex3fv(solid_prism_v[4]);
- glVertex3fv(solid_prism_v[10]);
- glVertex3fv(solid_prism_v[7]);
+ return 0;
+}
+#endif
+
+/*
+ * GLUT FUNCTIONS
+ */
+
+#ifdef HAVE_GLUT
+
+/* trackball quaternions */
+float cumquat[4] = {0.0,0.0,0.0,0.0}, oldquat[4] = {0.0,0.0,0.0,0.1};
+
+/* rotation matrix */
+float rotation[16];
+
+/* mouse drag vectors: start and end */
+float mouse_start[3], mouse_end[3];
+
+/* dragging boolean */
+int dragging = 0;
+
+/* this function calculates the rotation matrix based on the quaternions
+ * generated from the mouse drag vectors */
+void calc_rotation() {
+ double Nq, s;
+ double xs, ys, zs, wx, wy, wz, xx, xy, xz, yy, yz, zz;
+
+ /* this bit ripped from Shoemake's quaternion notes from SIGGRAPH */
+ Nq = cumquat[0] * cumquat[0] + cumquat[1] * cumquat[1] +
+ cumquat[2] * cumquat[2] + cumquat[3] * cumquat[3];
+ s = (Nq > 0.0) ? (2.0 / Nq) : 0.0;
+ xs = cumquat[0] * s; ys = cumquat[1] * s; zs = cumquat[2] * s;
+ wx = cumquat[3] * xs; wy = cumquat[3] * ys; wz = cumquat[3] * zs;
+ xx = cumquat[0] * xs; xy = cumquat[0] * ys; xz = cumquat[0] * zs;
+ yy = cumquat[1] * ys; yz = cumquat[1] * zs; zz = cumquat[2] * zs;
+
+ rotation[0] = 1.0 - (yy + zz);
+ rotation[1] = xy + wz;
+ rotation[2] = xz - wy;
+ rotation[4] = xy - wz;
+ rotation[5] = 1.0 - (xx + zz);
+ rotation[6] = yz + wx;
+ rotation[8] = xz + wy;
+ rotation[9] = yz - wx;
+ rotation[10] = 1.0 - (xx + yy);
+ rotation[3] = rotation[7] = rotation[11] = 0.0;
+ rotation[12] = rotation[13] = rotation[14] = 0.0;
+ rotation[15] = 1.0;
+}
+
+inline void ui_mousedrag() {
+ glMultMatrixf(rotation);
+}
+
+void ui_keyboard(unsigned char c, int x __attribute__((__unused__)), int y __attribute__((__unused__))) {
+ switch (c) {
+ case 27: /* ESC */
+ case 'q':
+ exit(0);
+ break;
+ case 'e':
+ explode += DEF_EXPLODE;
+ glutPostRedisplay();
+ break;
+ case 'E':
+ explode -= DEF_EXPLODE;
+ if (explode < 0.0) explode = 0.0;
+ glutPostRedisplay();
+ break;
+ case '.':
+ /* next model */
+ glc->next_model++;
+ glc->next_model %= models;
+ start_morph(glc->next_model, 0);
- glNormal3fv(solid_prism_n[17]);
- glVertex3fv(solid_prism_v[8]);
- glVertex3fv(solid_prism_v[11]);
- glVertex3fv(solid_prism_v[16]);
- glVertex3fv(solid_prism_v[13]);
+ /* Reset last_morph time */
+ gettime(&glc->last_morph);
+ break;
+ case ',':
+ /* previous model */
+ glc->next_model = (glc->next_model + (int)models - 1) % (int)models;
+ start_morph(glc->next_model, 0);
- glNormal3fv(solid_prism_n[18]);
- glVertex3fv(solid_prism_v[2]);
- glVertex3fv(solid_prism_v[14]);
- glVertex3fv(solid_prism_v[17]);
- glVertex3fv(solid_prism_v[5]);
- glEnd();
+ /* Reset glc->last_morph time */
+ gettime(&glc->last_morph);
+ break;
+ case '+':
+ angvel += DEF_ANGVEL;
+ break;
+ case '-':
+ if (angvel > DEF_ANGVEL)
+ angvel -= DEF_ANGVEL;
+ break;
+ case 'i':
+ if (interactive) {
+ /* Reset last_iteration and last_morph time */
+ gettime(&glc->last_iteration);
+ gettime(&glc->last_morph);
+ }
+ interactive = 1 - interactive;
+ glutPostRedisplay();
+ break;
+ case 'w':
+ wireframe = 1 - wireframe;
+ if (wireframe)
+ glDisable(GL_LIGHTING);
+ else
+ glEnable(GL_LIGHTING);
+ glutPostRedisplay();
+ break;
+ case 'a':
+ transparent = 1 - transparent;
+ if (transparent) {
+ glEnable(GL_BLEND);
} else {
- /* build wire display list */
- glBegin(GL_LINE_STRIP);
- glVertex3fv(wire_prism_v[0]);
- glVertex3fv(wire_prism_v[1]);
- glVertex3fv(wire_prism_v[2]);
- glVertex3fv(wire_prism_v[0]);
- glVertex3fv(wire_prism_v[3]);
- glVertex3fv(wire_prism_v[4]);
- glVertex3fv(wire_prism_v[5]);
- glVertex3fv(wire_prism_v[3]);
- glEnd();
-
- glBegin(GL_LINES);
- glVertex3fv(wire_prism_v[1]);
- glVertex3fv(wire_prism_v[4]);
- glVertex3fv(wire_prism_v[2]);
- glVertex3fv(wire_prism_v[5]);
- glEnd();
+ glDisable(GL_BLEND);
}
- glEndList();
-}
-
-/* "jwz? no way man, he's my idle" -- Jaq, 2001.
- * I forget the context :( */
-void glsnake_idol(glsnake_configuration * bp) {
- /* time since last iteration */
- long iter_msec;
- /* time since the beginning of last morph */
- long morf_msec;
- float iter_angle_max;
- int i;
- struct timeb current_time;
- int still_morphing;
-
- /* Do nothing to the model if we are paused */
- if (bp->paused) {
- /* Avoid busy waiting when nothing is changing */
- usleep(1);
- return;
+ break;
+ case 'p':
+ if (glc->paused) {
+ /* unpausing, reset last_iteration and last_morph time */
+ gettime(&glc->last_iteration);
+ gettime(&glc->last_morph);
}
- /* ftime is winDOS compatible */
- ftime(¤t_time);
-
- /* <spiv> Well, ftime gives time with millisecond resolution.
- * <Jaq> if current time is exactly equal to last iteration,
- * then don't do this block
- * <spiv> (or worse, perhaps... who knows what the OS will do)
- * <spiv> So if no discernable amount of time has passed:
- * <spiv> a) There's no point updating the screen, because
- * it would be the same
- * <spiv> b) The code will divide by zero
- */
- iter_msec = (long) current_time.millitm - bp->last_iteration.millitm +
- ((long) current_time.time - bp->last_iteration.time) * 1000L;
- if (iter_msec) {
- /* save the current time */
- memcpy(&(bp->last_iteration), ¤t_time, sizeof(struct timeb));
-
- /* work out if we have to switch models */
- morf_msec = bp->last_iteration.millitm - bp->last_morph.millitm +
- ((long) (bp->last_iteration.time - bp->last_morph.time) * 1000L);
-
- if ((morf_msec > statictime) && !bp->interactive) {
- memcpy(&(bp->last_morph), &(bp->last_iteration), sizeof(struct timeb));
- start_morph(RAND(bp->m_count), 0, bp);
- }
-
- if (bp->interactive && !bp->morphing) {
- usleep(1);
- return;
- }
-
- if (!bp->dragging && !bp->interactive) {
- bp->roty += 360/((1000/yspin)/iter_msec);
- bp->rotz += 360/((1000/zspin)/iter_msec);
- }
-
- /* work out the maximum angle for this iteration */
- iter_angle_max = 90.0 * (velocity/1000.0) * iter_msec;
-
- still_morphing = 0;
- for (i = 0; i < 24; i++) {
- float cur_angle = bp->node[i].cur_angle;
- float dest_angle = bp->node[i].dest_angle;
- if (cur_angle != dest_angle) {
- still_morphing = 1;
- if (fabs(cur_angle - dest_angle) <= iter_angle_max)
- bp->node[i].cur_angle = dest_angle;
- else if (fmod(cur_angle - dest_angle + 360, 360) > 180)
- bp->node[i].cur_angle = fmod(cur_angle + iter_angle_max, 360);
- else
- bp->node[i].cur_angle = fmod(cur_angle + 360 - iter_angle_max, 360);
- }
- }
-
- if (!still_morphing)
- bp->morphing = 0;
-
- /* colour cycling */
- if (fabs(bp->colour[0] - bp->colour_t[0]) <= fabs(bp->colour_i[0]))
- bp->colour[0] = bp->colour_t[0];
- else
- bp->colour[0] += bp->colour_i[0];
- if (fabs(bp->colour[1] - bp->colour_t[1]) <= fabs(bp->colour_i[1]))
- bp->colour[1] = bp->colour_t[1];
- else
- bp->colour[1] += bp->colour_i[1];
- if (fabs(bp->colour[2] - bp->colour_t[2]) <= fabs(bp->colour_i[2]))
- bp->colour[2] = bp->colour_t[2];
- else
- bp->colour[2] += bp->colour_i[2];
+ glc->paused = 1 - glc->paused;
+ break;
+ case 'd':
+ /* dump the current model so we can add it! */
+ printf("# %s\nnoname:\t", model[glc->next_model].name);
+ {
+ int i;
+
+ for (i = 0; i < NODE_COUNT; i++) {
+ if (glc->node[i] == ZERO)
+ printf("Z");
+ else if (glc->node[i] == LEFT)
+ printf("L");
+ else if (glc->node[i] == PIN)
+ printf("P");
+ else if (glc->node[i] == RIGHT)
+ printf("R");
+ /*
+ else
+ printf("%f", node[i].curAngle);
+ */
+ if (i < NODE_COUNT - 1)
+ printf(" ");
+ }
+ }
+ printf("\n");
+ break;
+ case 'f':
+ glc->fullscreen = 1 - glc->fullscreen;
+ if (glc->fullscreen) {
+ glc->old_width = glc->width;
+ glc->old_height = glc->height;
+ glutFullScreen();
} else {
- /* We are going too fast, so we may as well let the
- * cpu relax a little by sleeping for a millisecond. */
- usleep(1);
+ glutReshapeWindow(glc->old_width, glc->old_height);
+ glutPositionWindow(50,50);
}
+ break;
+ case 't':
+ titles = 1 - titles;
+ if (interactive || glc->paused)
+ glutPostRedisplay();
+ break;
+ case 'c':
+ altcolour = 1 - altcolour;
+ break;
+ case 'z':
+ zoom += 1.0;
+ glsnake_reshape(glc->width, glc->height);
+ break;
+ case 'Z':
+ zoom -= 1.0;
+ glsnake_reshape(glc->width, glc->height);
+ break;
+ default:
+ break;
+ }
}
-void glsnake_draw(ModeInfo *mi) {
- glsnake_configuration *bp = &glc[MI_SCREEN(mi)];
- Display *dpy = MI_DISPLAY(mi);
- Window window = MI_WINDOW(mi);
-
- int i;
- float ang;
-
- if (!bp->glx_context)
- return;
-
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- gluLookAt(0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
-
- /* rotate and translate into snake space */
- glRotatef(45.0, -5.0, 0.0, 1.0);
- glTranslatef(-0.5, 0.0, 0.5);
-
- /* rotate the 0th junction */
- glTranslatef(0.5, 0.0, 0.5);
- /* glMultMatrix(rotation); -- quaternion rotation */
- glRotatef(bp->roty, 0.0, 1.0, 0.0);
- glRotatef(bp->rotz, 0.0, 0.0, 1.0);
- glTranslated(-0.5, 0.0, -0.5);
-
- /* translate middle node to centre */
- for (i = 11; i >= 0; i--) {
- ang = bp->node[i].cur_angle;
- glTranslatef(0.5, 0.5, 0.5);
- glRotatef(180+ang, -1.0, 0.0, 0.0);
- glTranslatef(-1.0 - explode, 0.0, 0.0);
- glRotatef(90, 0.0, 0.0, 1.0);
- glTranslatef(-0.5, -0.5, -0.5);
+void ui_special(int key, int x __attribute__((__unused__)), int y __attribute__((__unused__))) {
+ float *destAngle = &(model[glc->next_model].node[glc->selected]);
+ int unknown_key = 0;
+
+ if (interactive) {
+ switch (key) {
+ case GLUT_KEY_UP:
+ glc->selected = (glc->selected + (NODE_COUNT - 2)) % (NODE_COUNT - 1);
+ break;
+ case GLUT_KEY_DOWN:
+ glc->selected = (glc->selected + 1) % (NODE_COUNT - 1);
+ break;
+ case GLUT_KEY_LEFT:
+ *destAngle = fmod(*destAngle+(LEFT), 360);
+ glc->morphing = glc->new_morph = 1;
+ break;
+ case GLUT_KEY_RIGHT:
+ *destAngle = fmod(*destAngle+(RIGHT), 360);
+ glc->morphing = glc->new_morph = 1;
+ break;
+ case GLUT_KEY_HOME:
+ start_morph(STRAIGHT_MODEL, 0);
+ break;
+ default:
+ unknown_key = 1;
+ break;
}
+ }
+ calc_snake_metrics();
- /* now draw each node along the snake */
- for (i = 0; i < 24; i++) {
- glPushMatrix();
-
- /* choose a colour for this node */
- if (bp->interactive && (i == bp->selected || i == bp->selected+1))
- glColor3f(1.0, 1.0, 0.0);
- else {
- if (i % 2) {
- if (scarycolour)
- glColor3f(0.6, 0.0, 0.9);
- else
- glColor3fv(bp->colour);
- } else {
- if (scarycolour)
- glColor3f(0.2, 0.9, 1.0);
- else
- glColor3f(1.0, 1.0, 1.0);
- }
- }
-
- /* draw the node */
- glCallList(bp->node_list);
-
- /* now work out where to draw the next one */
-
- /* interpolate between models */
- ang = bp->node[i].cur_angle;
-
- glTranslatef(0.5, 0.5, 0.5);
- glRotatef(90, 0.0, 0.0, -1.0);
- glTranslatef(1.0 + explode, 0.0, 0.0);
- glRotatef(180 + ang, 1.0, 0.0, 0.0);
- glTranslatef(-0.5, -0.5, -0.5);
- }
+ if (!unknown_key)
+ glutPostRedisplay();
+}
- /* clear up the matrix stack */
- for (i = 0; i < 24; i++)
- glPopMatrix();
+void ui_mouse(int button, int state, int x, int y) {
+ if (button==0) {
+ switch (state) {
+ case GLUT_DOWN:
+ dragging = 1;
+ mouse_start[0] = M_SQRT1_2 *
+ (x - (glc->width / 2.0)) / (glc->width / 2.0);
+ mouse_start[1] = M_SQRT1_2 *
+ ((glc->height / 2.0) - y) / (glc->height / 2.0);
+ mouse_start[2] = sqrt((double)(1-(mouse_start[0]*mouse_start[0]+mouse_start[1]*mouse_start[1])));
+ break;
+ case GLUT_UP:
+ dragging = 0;
+ oldquat[0] = cumquat[0];
+ oldquat[1] = cumquat[1];
+ oldquat[2] = cumquat[2];
+ oldquat[3] = cumquat[3];
+ break;
+ default:
+ break;
+ }
+ }
+ glutPostRedisplay();
+}
- if (labels)
- draw_label(mi);
-
- if (mi->fps_p)
- do_fps (mi);
+void ui_motion(int x, int y) {
+ double norm;
+ float q[4];
+
+ if (dragging) {
+ /* construct the motion end vector from the x,y position on the
+ * window */
+ mouse_end[0] = M_SQRT1_2 * (x - (glc->width/ 2.0)) / (glc->width / 2.0);
+ mouse_end[1] = M_SQRT1_2 * ((glc->height / 2.0) - y) / (glc->height / 2.0);
+ /* calculate the normal of the vector... */
+ norm = mouse_end[0] * mouse_end[0] + mouse_end[1] * mouse_end[1];
+ /* check if norm is outside the sphere and wraparound if necessary */
+ if (norm > 1.0) {
+ mouse_end[0] = -mouse_end[0];
+ mouse_end[1] = -mouse_end[1];
+ mouse_end[2] = sqrt(norm - 1);
+ } else {
+ /* the z value comes from projecting onto an elliptical spheroid */
+ mouse_end[2] = sqrt(1 - norm);
+ }
- glsnake_idol(bp);
+ /* now here, build a quaternion from mouse_start and mouse_end */
+ q[0] = mouse_start[1] * mouse_end[2] - mouse_start[2] * mouse_end[1];
+ q[1] = mouse_start[2] * mouse_end[0] - mouse_start[0] * mouse_end[2];
+ q[2] = mouse_start[0] * mouse_end[1] - mouse_start[1] * mouse_end[0];
+ q[3] = mouse_start[0] * mouse_end[0] + mouse_start[1] * mouse_end[1] + mouse_start[2] * mouse_end[2];
+
+ /* new rotation is the product of the new one and the old one */
+ cumquat[0] = q[3] * oldquat[0] + q[0] * oldquat[3] +
+ q[1] * oldquat[2] - q[2] * oldquat[1];
+ cumquat[1] = q[3] * oldquat[1] + q[1] * oldquat[3] +
+ q[2] * oldquat[0] - q[0] * oldquat[2];
+ cumquat[2] = q[3] * oldquat[2] + q[2] * oldquat[3] +
+ q[0] * oldquat[1] - q[1] * oldquat[0];
+ cumquat[3] = q[3] * oldquat[3] - q[0] * oldquat[0] -
+ q[1] * oldquat[1] - q[2] * oldquat[2];
- glFlush();
- glXSwapBuffers(dpy, window);
+ calc_rotation();
+ }
+ glutPostRedisplay();
}
-#endif /* USE_GL */
+void ui_init(int * argc, char ** argv) {
+ glutInit(argc, argv);
+ glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
+ glutInitWindowSize(glc->width, glc->height);
+ glc->window = glutCreateWindow("glsnake");
+
+ glutDisplayFunc(glsnake_display);
+ glutReshapeFunc(glsnake_reshape);
+ glutIdleFunc(glsnake_idle);
+ glutKeyboardFunc(ui_keyboard);
+ glutSpecialFunc(ui_special);
+ glutMouseFunc(ui_mouse);
+ glutMotionFunc(ui_motion);
+
+ yangvel = DEF_YANGVEL;
+ zangvel = DEF_ZANGVEL;
+ explode = DEF_EXPLODE;
+ angvel = DEF_ANGVEL;
+ statictime = DEF_STATICTIME;
+ altcolour = DEF_ALTCOLOUR;
+ titles = DEF_TITLES;
+ interactive = DEF_INTERACTIVE;
+ zoom = DEF_ZOOM;
+ wireframe = DEF_WIREFRAME;
+ transparent = DEF_TRANSPARENT;
+}
+#endif /* HAVE_GLUT */