/* glsnake.c - OpenGL imitation of Rubik's Snake * * (c) 2001-2005 Jamie Wilkinson * (c) 2001-2003 Andrew Bennetts * (c) 2001-2003 Peter Aylett * * 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 * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif /* 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 #else # ifdef HAVE_JWXYZ # define HAVE_GETTIMEOFDAY # else # include # include # endif #endif # ifdef HAVE_ANDROID # include #endif #ifdef HAVE_JWZGLES # include "jwzgles.h" #endif /* HAVE_JWZGLES */ #ifdef STANDALONE # include "xlockmoreI.h" #endif #include #include #include #include /* 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 # include typedef struct timeval snaketime; # define GETSECS(t) ((t).tv_sec) # define GETMSECS(t) ((t).tv_usec/1000) # else /* !GETTIMEOFDAY_TWO_ARGS */ # include # include 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 # include typedef struct timeb snaketime; # define GETSECS(t) ((long)(t).time) # define GETMSECS(t) ((t).millitm/1000) # endif /* HAVE_FTIME */ #endif /* HAVE_GETTIMEOFDAY */ #include #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 0 #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_ANGVEL "1.0" #define DEF_STATICTIME "5000" #define DEF_ALTCOLOUR "False" #define DEF_TITLES "False" #define DEF_INTERACTIVE "False" #define DEF_ZOOM "25.0" #define DEF_WIREFRAME "False" #define DEF_TRANSPARENT "True" #endif /* static variables */ 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 #define glsnake_init init_glsnake #define glsnake_display draw_glsnake #define glsnake_reshape reshape_glsnake #define free_glsnake 0 #define release_glsnake 0 #define glsnake_handle_event xlockmore_no_events /* xscreensaver defaults */ #define DEFAULTS "*delay: 30000 \n" \ "*count: 30 \n" \ "*showFPS: False \n" \ "*suppressRotationAnimation: True\n" \ "*labelfont: -*-helvetica-medium-r-normal-*-*-180-*-*-*-*-*-*\n" \ #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) #include "xlockmore.h" #include "texfont.h" static XrmOptionDescRec opts[] = { { "-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, "True" }, { "-no-altcolour", ".altcolour", XrmoptionNoArg, "False" }, { "-titles", ".titles", XrmoptionNoArg, "True" }, { "-no-titles", ".titles", XrmoptionNoArg, "False" }, { "-zoom", ".zoom", XrmoptionSepArg, DEF_ZOOM }, { "-wireframe", ".wireframe", XrmoptionNoArg, "true" }, { "-no-wireframe", ".wireframe", XrmoptionNoArg, "false" }, { "-transparent", ".transparent", XrmoptionNoArg, "true" }, { "-no-transparent", ".transparent", XrmoptionNoArg, "false" }, }; static argtype vars[] = { {&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}, }; ENTRYPOINT ModeSpecOpt glsnake_opts = {countof(opts), opts, countof(vars), vars, NULL}; #endif struct model_s { const char * name; float node[NODE_COUNT]; }; struct glsnake_cfg { #ifndef HAVE_GLUT GLXContext * glx_context; texture_font_data *font_data; #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]; /* 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; int node_polys; /* is the window fullscreen? */ int fullscreen; }; #define COLOUR_CYCLIC 0 #define COLOUR_ACYCLIC 1 #define COLOUR_INVALID 2 #define COLOUR_AUTHENTIC 3 #define COLOUR_ORIGLOGO 4 static const 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 } } }; static const 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 } }, { "PictureComingSoon", { 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 } }, }; static 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 #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 */ static const 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 }}; static const 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 }}; static const 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 }}; /* static const 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 */ static void calc_rotation(); static inline void ui_mousedrag(); #endif static const GLfloat white_light[] = { 1.0, 1.0, 1.0, 1.0 }; static const GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 }; static const GLfloat mat_specular[] = { 0.1, 0.1, 0.1, 1.0 }; static const GLfloat mat_shininess[] = { 20.0 }; static 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);*/ } } static 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 */ } ENTRYPOINT void glsnake_reshape( #ifndef HAVE_GLUT ModeInfo * mi, #endif int w, int h); static void start_morph(struct glsnake_cfg *, unsigned int model_index, int immediate); /* wot initialises it */ ENTRYPOINT void glsnake_init( #ifndef HAVE_GLUT ModeInfo * mi #endif ) { #ifndef HAVE_GLUT struct glsnake_cfg * bp; /* set up the conf struct and glx contexts */ MI_INIT(mi, glc); 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, bp->prev_model, 1); /* set up a font for the labels */ #ifndef HAVE_GLUT if (titles) bp->font_data = load_texture_font (mi->dpy, "labelfont"); #endif /* build a solid display list */ bp->node_solid = glGenLists(1); glNewList(bp->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]); bp->node_polys++; glNormal3fv(solid_prism_n[1]); glVertex3fv(solid_prism_v[6]); glVertex3fv(solid_prism_v[7]); glVertex3fv(solid_prism_v[8]); bp->node_polys++; glNormal3fv(solid_prism_n[2]); glVertex3fv(solid_prism_v[12]); glVertex3fv(solid_prism_v[13]); glVertex3fv(solid_prism_v[14]); bp->node_polys++; glNormal3fv(solid_prism_n[3]); glVertex3fv(solid_prism_v[3]); glVertex3fv(solid_prism_v[4]); glVertex3fv(solid_prism_v[5]); bp->node_polys++; glNormal3fv(solid_prism_n[4]); glVertex3fv(solid_prism_v[9]); glVertex3fv(solid_prism_v[11]); glVertex3fv(solid_prism_v[10]); bp->node_polys++; glNormal3fv(solid_prism_n[5]); glVertex3fv(solid_prism_v[16]); glVertex3fv(solid_prism_v[15]); glVertex3fv(solid_prism_v[17]); bp->node_polys++; 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]); bp->node_polys++; 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]); bp->node_polys++; 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]); bp->node_polys++; 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]); bp->node_polys++; 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]); bp->node_polys++; 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]); bp->node_polys++; 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]); bp->node_polys++; 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]); bp->node_polys++; 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]); bp->node_polys++; 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]); bp->node_polys++; glNormal3fv(solid_prism_n[19]); glVertex3fv(solid_prism_v[3]); glVertex3fv(solid_prism_v[15]); glVertex3fv(solid_prism_v[9]); bp->node_polys++; 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]); bp->node_polys++; 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]); bp->node_polys++; 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]); bp->node_polys++; glEnd(); glEndList(); /* build wire display list */ bp->node_wire = glGenLists(1); glNewList(bp->node_wire, GL_COMPILE); glBegin(GL_LINE_STRIP); glVertex3fv(wire_prism_v[0]); glVertex3fv(wire_prism_v[1]); bp->node_polys++; glVertex3fv(wire_prism_v[2]); glVertex3fv(wire_prism_v[0]); bp->node_polys++; glVertex3fv(wire_prism_v[3]); glVertex3fv(wire_prism_v[4]); bp->node_polys++; glVertex3fv(wire_prism_v[5]); glVertex3fv(wire_prism_v[3]); bp->node_polys++; glEnd(); glBegin(GL_LINES); glVertex3fv(wire_prism_v[1]); glVertex3fv(wire_prism_v[4]); bp->node_polys++; glVertex3fv(wire_prism_v[2]); glVertex3fv(wire_prism_v[5]); bp->node_polys++; glEnd(); glEndList(); #ifdef HAVE_GLUT /* initialise the rotation */ calc_rotation(); #endif } static void draw_title( #ifndef HAVE_GLUT ModeInfo * mi #endif ) { #ifndef HAVE_GLUT struct glsnake_cfg * bp = &glc[MI_SCREEN(mi)]; #endif /* 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 glOrtho((GLdouble) 0., (GLdouble) bp->width, (GLdouble) 0., (GLdouble) bp->height, -1, 1); #else glOrtho((GLdouble) 0., (GLdouble) mi->xgwa.width, (GLdouble) 0., (GLdouble) mi->xgwa.height, -1, 1); #endif glColor4f(1.0, 1.0, 1.0, 1.0); { char interactstr[] = "interactive"; const char * s; #ifdef HAVE_GLUT int w; #endif if (interactive) s = interactstr; else s = model[bp->next_model].name; #ifdef HAVE_GLUT { unsigned int i = 0; w = glutBitmapLength(GLUT_BITMAP_HELVETICA_12, (const unsigned char *) s); glRasterPos2f((GLfloat) (bp->width - w - 3), 4.0); while (s[i] != '\0') glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, s[i++]); } #else print_texture_label (mi->dpy, bp->font_data, mi->xgwa.width, mi->xgwa.height, 1, s); #endif } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); /* glPopAttrib();*/ } /* apply the matrix to the origin and stick it in vec */ static 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; } /* wot gets called when the winder is resized */ ENTRYPOINT void glsnake_reshape( #ifndef HAVE_GLUT ModeInfo * mi, #endif int width, int height) { double h = (GLfloat) height / (GLfloat) width; int y = 0; if (width > height * 5) { /* tiny window: show middle */ height = width; y = -height/2; h = height / (GLfloat) width; } glViewport(0, y, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); /* jwz: 0.05 was too close (left black rectangles) */ gluPerspective(zoom, 1/h, 1.0, 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 bp->width = width; bp->height = height; #endif } /* Returns the new dst_dir for the given src_dir and dst_dir */ static 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)); } /* 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 */ static void calc_snake_metrics(struct glsnake_cfg *bp) { 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); bp->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[bp->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[bp->next_model].node[i] == (int) (RIGHT)) dstDir = -dstDir; break; default: /* Prevent spurious "might be used * uninitialised" warnings when compiling * with -O2 */ dstDir = 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 bp->is_legal = 0; prevSrcDir = srcDir; prevDstDir = dstDir; } /* determine if the snake is cyclic */ bp->is_cyclic = (dstDir == Y_MASK && x == 12 && y == 11 && z == 12); /* determine last_turn */ bp->last_turn = -1; if (bp->is_cyclic) switch (srcDir) { case -Z_MASK: bp->last_turn = ZERO; break; case Z_MASK: bp->last_turn = PIN; break; case X_MASK: bp->last_turn = LEFT; break; case -X_MASK: bp->last_turn = RIGHT; break; } } /* work out how far through the current morph we are */ static float morph_percent(struct glsnake_cfg *bp) { 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[bp->prev_model].node[i] - model[bp->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(bp->node[i] - model[bp->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; } static void morph_colour(struct glsnake_cfg *bp) { float percent, compct; /* complement of percentage */ percent = morph_percent(bp); compct = 1.0 - percent; bp->colour[0][0] = colour[bp->prev_colour][0][0] * compct + colour[bp->next_colour][0][0] * percent; bp->colour[0][1] = colour[bp->prev_colour][0][1] * compct + colour[bp->next_colour][0][1] * percent; bp->colour[0][2] = colour[bp->prev_colour][0][2] * compct + colour[bp->next_colour][0][2] * percent; bp->colour[0][3] = colour[bp->prev_colour][0][3] * compct + colour[bp->next_colour][0][3] * percent; bp->colour[1][0] = colour[bp->prev_colour][1][0] * compct + colour[bp->next_colour][1][0] * percent; bp->colour[1][1] = colour[bp->prev_colour][1][1] * compct + colour[bp->next_colour][1][1] * percent; bp->colour[1][2] = colour[bp->prev_colour][1][2] * compct + colour[bp->next_colour][1][2] * percent; bp->colour[1][3] = colour[bp->prev_colour][1][3] * compct + colour[bp->next_colour][1][3] * percent; } /* Start morph process to this model */ static void start_morph(struct glsnake_cfg *bp, 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 < NODE_COUNT; i++) bp->node[i] = model[model_index].node[i]; } bp->prev_model = bp->next_model; bp->next_model = model_index; bp->prev_colour = bp->next_colour; calc_snake_metrics(bp); if (!bp->is_legal) bp->next_colour = COLOUR_INVALID; else if (altcolour) bp->next_colour = COLOUR_AUTHENTIC; else if (bp->is_cyclic) bp->next_colour = COLOUR_CYCLIC; else bp->next_colour = COLOUR_ACYCLIC; if (immediate) { bp->colour[0][0] = colour[bp->next_colour][0][0]; bp->colour[0][1] = colour[bp->next_colour][0][1]; bp->colour[0][2] = colour[bp->next_colour][0][2]; bp->colour[0][3] = colour[bp->next_colour][0][3]; bp->colour[1][0] = colour[bp->next_colour][1][0]; bp->colour[1][1] = colour[bp->next_colour][1][1]; bp->colour[1][2] = colour[bp->next_colour][1][2]; bp->colour[1][3] = colour[bp->next_colour][1][3]; } bp->morphing = 1; morph_colour(bp); } #if 0 /* Returns morph progress */ static 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 (bp->new_morph) bp->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 = bp->node[i]; float destAngle = model[bp->next_model].node[i]; if (curAngle != destAngle) { still_morphing = 1; if (fabs(curAngle-destAngle) <= iter_angle_max) bp->node[i] = destAngle; else if (fmod(curAngle-destAngle+360,360) > 180) bp->node[i] = fmod(curAngle + iter_angle_max, 360); else bp->node[i] = fmod(curAngle+360 - iter_angle_max, 360); largest_diff = MAX(largest_diff, fabs(destAngle-bp->node[i])); largest_progress = MAX(largest_diff, fabs(bp->node[i] - model[bp->prev_model].node[i])); } } return MIN(largest_diff / largest_progress, 1.0); } #endif #ifdef HAVE_GLUT static void glsnake_idle(); static restore_idle(int v __attribute__((__unused__))) { glutIdleFunc(glsnake_idle); } #endif static 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 } static 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 (bp->paused) { /* Avoid busy waiting when nothing is changing */ quick_sleep(); #ifdef HAVE_GLUT glutSwapBuffers(); glutPostRedisplay(); #endif return; } /* Well, ftime gives time with millisecond resolution. * (or worse, perhaps... who knows what the OS will do) * So if no discernable amount of time has passed: * a) There's no point updating the screen, because * it would be the same * b) The code will divide by zero */ gettime(¤t_time); iter_msec = (long) GETMSECS(current_time) - GETMSECS(bp->last_iteration) + ((long) GETSECS(current_time) - GETSECS(bp->last_iteration)) * 1000L; if (iter_msec) { /* save the current time */ memcpy(&bp->last_iteration, ¤t_time, sizeof(snaketime)); /* work out if we have to switch models */ morf_msec = GETMSECS(bp->last_iteration) - GETMSECS(bp->last_morph) + ((long) (GETSECS(bp->last_iteration)-GETSECS(bp->last_morph)) * 1000L); if ((morf_msec > statictime) && !interactive && !bp->morphing) { /*printf("starting morph\n");*/ memcpy(&bp->last_morph, &(bp->last_iteration), sizeof(bp->last_morph)); start_morph(bp, RAND(models), 0); } if (interactive && !bp->morphing) { quick_sleep(); return; } /* if (!bp->dragging && !bp->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);*/ } /* 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 = bp->node[i]; float dest_angle = model[bp->next_model].node[i]; if (cur_angle != dest_angle) { still_morphing = 1; if (fabs(cur_angle - dest_angle) <= iter_angle_max) bp->node[i] = dest_angle; else if (fmod(cur_angle - dest_angle + 360, 360) > 180) bp->node[i] = fmod(cur_angle + iter_angle_max, 360); else bp->node[i] = fmod(cur_angle + 360 - iter_angle_max, 360); } } if (!still_morphing) bp->morphing = 0; /* colour cycling */ morph_colour(bp); #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(); } } /* wot draws it */ ENTRYPOINT 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 int i; float ang; float positions[NODE_COUNT][4]; /* origin points for each node */ float com[4]; /* it's the CENTRE of MASS */ #ifndef HAVE_GLUT if (!bp->glx_context) return; glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context)); #endif gl_init(mi); /* 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 = bp->node[i]; /*printf("ang = %f\n", ang);*/ 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 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); # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */ { GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi); int o = (int) current_device_rotation(); if (o != 0 && o != 180 && o != -180) glScalef (1/h, 1/h, 1/h); } # endif /* now draw each node along the snake -- this is quite ugly :p */ mi->polygon_count = 0; for (i = 0; i < NODE_COUNT; i++) { /* choose a colour for this node */ if ((i == bp->selected || i == bp->selected+1) && interactive) /* yellow */ glColor4f(1.0, 1.0, 0.0, 1.0); else { /*glColor4fv(bp->colour[(i+1)%2]);*/ glMaterialfv(GL_FRONT, GL_AMBIENT, bp->colour[(i+1)%2]); glMaterialfv(GL_FRONT, GL_DIFFUSE, bp->colour[(i+1)%2]); /*glMaterialfv(GL_FRONT, GL_SPECULAR, bp->colour[(i+1)%2]);*/ } /* draw the node */ if (wireframe) glCallList(bp->node_wire); else glCallList(bp->node_solid); mi->polygon_count += bp->node_polys; /* now work out where to draw the next one */ /* Interpolate between models */ ang = bp->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 #ifndef HAVE_GLUT glsnake_idle(bp); if (mi->fps_p) do_fps(mi); #endif glFlush(); #ifdef HAVE_GLUT glutSwapBuffers(); #else glXSwapBuffers(dpy, window); #endif } #ifdef HAVE_GLUT /* anything that needs to be cleaned up goes here */ static void unmain() { glutDestroyWindow(bp->window); free(bp); } static void ui_init(int *, char **); int main(int argc, char ** argv) { bp = malloc(sizeof(struct glsnake_cfg)); memset(bp, 0, sizeof(struct glsnake_cfg)); bp->width = 640; bp->height = 480; ui_init(&argc, argv); gettime(&bp->last_iteration); memcpy(&bp->last_morph, &bp->last_iteration, sizeof(snaketime)); srand((unsigned int)GETSECS(bp->last_iteration)); bp->prev_colour = bp->next_colour = COLOUR_ACYCLIC; bp->next_model = RAND(models); bp->prev_model = 0; start_morph(bp->prev_model, 1); glsnake_init(); atexit(unmain); glutSwapBuffers(); glutMainLoop(); return 0; } #endif /* * GLUT FUNCTIONS */ #ifdef HAVE_GLUT /* trackball quaternions */ static float cumquat[4] = {0.0,0.0,0.0,0.0}, oldquat[4] = {0.0,0.0,0.0,0.1}; /* rotation matrix */ static float rotation[16]; /* mouse drag vectors: start and end */ static float mouse_start[3], mouse_end[3]; /* dragging boolean */ static int dragging = 0; /* this function calculates the rotation matrix based on the quaternions * generated from the mouse drag vectors */ static 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; } static inline void ui_mousedrag() { glMultMatrixf(rotation); } static void ui_keyboard(unsigned char c, int x__attribute__((__unused__)), int y __attribute__((__unused__))) { int i; 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 */ bp->next_model++; bp->next_model %= models; start_morph(bp->next_model, 0); /* Reset last_morph time */ gettime(&bp->last_morph); break; case ',': /* previous model */ bp->next_model = (bp->next_model + (int)models - 1) % (int)models; start_morph(bp->next_model, 0); /* Reset bp->last_morph time */ gettime(&bp->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(&bp->last_iteration); gettime(&bp->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 { glDisable(GL_BLEND); } break; case 'p': if (bp->paused) { /* unpausing, reset last_iteration and last_morph time */ gettime(&bp->last_iteration); gettime(&bp->last_morph); } bp->paused = 1 - bp->paused; break; case 'd': /* dump the current model so we can add it! */ printf("# %s\nnoname:\t", model[bp->next_model].name); { int i; for (i = 0; i < NODE_COUNT; i++) { if (bp->node[i] == ZERO) printf("Z"); else if (bp->node[i] == LEFT) printf("L"); else if (bp->node[i] == PIN) printf("P"); else if (bp->node[i] == RIGHT) printf("R"); /* else printf("%f", node[i].curAngle); */ if (i < NODE_COUNT - 1) printf(" "); } } printf("\n"); break; case 'f': bp->fullscreen = 1 - bp->fullscreen; if (bp->fullscreen) { bp->old_width = bp->width; bp->old_height = bp->height; glutFullScreen(); } else { glutReshapeWindow(bp->old_width, bp->old_height); glutPositionWindow(50,50); } break; case 't': titles = 1 - titles; if (interactive || bp->paused) glutPostRedisplay(); break; case 'c': altcolour = 1 - altcolour; break; case 'z': zoom += 1.0; glsnake_reshape(bp->width, bp->height); break; case 'Z': zoom -= 1.0; glsnake_reshape(bp->width, bp->height); break; default: break; } } static void ui_special(int key, int x__attribute__((__unused__)), int y __attribute__((__unused__))) { float *destAngle = &(model[bp->next_model].node[bp->selected]); int unknown_key = 0; if (interactive) { switch (key) { case GLUT_KEY_UP: bp->selected = (bp->selected + (NODE_COUNT - 2)) % (NODE_COUNT - 1); break; case GLUT_KEY_DOWN: bp->selected = (bp->selected + 1) % (NODE_COUNT - 1); break; case GLUT_KEY_LEFT: *destAngle = fmod(*destAngle+(LEFT), 360); bp->morphing = bp->new_morph = 1; break; case GLUT_KEY_RIGHT: *destAngle = fmod(*destAngle+(RIGHT), 360); bp->morphing = bp->new_morph = 1; break; case GLUT_KEY_HOME: start_morph(STRAIGHT_MODEL, 0); break; default: unknown_key = 1; break; } } calc_snake_metrics(); if (!unknown_key) glutPostRedisplay(); } static 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 - (bp->width / 2.0)) / (bp->width / 2.0); mouse_start[1] = M_SQRT1_2 * ((bp->height / 2.0) - y) / (bp->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(); } static 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 - (bp->width/ 2.0)) / (bp->width / 2.0); mouse_end[1] = M_SQRT1_2 * ((bp->height / 2.0) - y) / (bp->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); } /* 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]; calc_rotation(); } glutPostRedisplay(); } static void ui_init(int * argc, char ** argv) { glutInit(argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(bp->width, bp->height); bp->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 */ XSCREENSAVER_MODULE ("GLSnake", glsnake)