X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fglx%2Fglsnake.c;h=dd533539f41e0b06c4142465aa6badb9025dd794;hp=90a28ced3f1fa847d29d53e57bd12ac0ce29b85d;hb=96a411663168b0ba5432b407a83be55f3df0c802;hpb=bbd0773f2adde4927a6196361d4061e70bf48cd9 diff --git a/hacks/glx/glsnake.c b/hacks/glx/glsnake.c index 90a28ced..dd533539 100644 --- a/hacks/glx/glsnake.c +++ b/hacks/glx/glsnake.c @@ -1,10 +1,9 @@ -/* 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-2003 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 @@ -20,42 +19,137 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include +#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 +#else +# include +# include +#endif -#define PROGCLASS "glsnake" -#define HACK_INIT glsnake_init -#define HACK_DRAW glsnake_draw -#define HACK_RESHAPE glsnake_reshape -#define sws_opts xlockmore_opts +#include +#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 +#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 DEF_SPEED "0.05" +#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_ACCEL 0.1 +#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 +#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_ANGVEL "1.0" #define DEF_ACCEL "0.1" #define DEF_STATICTIME "5000" -#define DEF_YSPIN "0.10" -#define DEF_ZSPIN "0.14" -#define DEF_SCARYCOLOUR "False" -#define DEF_LABELS "True" -#define DEF_BBOX "False" - -#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: -*-times-bold-r-normal-*-180-*\n" \ - "*bbox: " DEF_BBOX "\n" \ +#define DEF_ALTCOLOUR "False" +#define DEF_TITLES "True" +#define DEF_INTERACTIVE "False" +#define DEF_ZOOM "25.0" +#define DEF_WIREFRAME "False" +#endif + +/* static variables */ +#ifndef HAVE_GLUT +# include +#else +/* xscreensaver boolean type */ +# define Bool int +#endif + +static GLfloat explode; +static GLfloat accel; +static long statictime; +static GLfloat yspin = 0; +static GLfloat zspin = 0; +static GLfloat yangvel; +static GLfloat zangvel; +static Bool altcolour; +static Bool titles; +static Bool interactive; +static Bool wireframe; +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" \ + "*wireframe: False \n" \ + "*explode: " DEF_EXPLODE " \n" \ + "*angvel: " DEF_ANGVEL " \n" \ + "*accel: " DEF_ACCEL " \n" \ + "*statictime: " DEF_STATICTIME " \n" \ + "*yangvel: " DEF_YANGVEL " \n" \ + "*zangvel: " DEF_ZANGVEL " \n" \ + "*altcolour: " DEF_ALTCOLOUR " \n" \ + "*titles: True \n" \ + "*labelfont: -*-times-bold-r-normal-*-180-*\n" \ + "*zoom: " DEF_ZOOM " \n" \ @@ -64,688 +158,1334 @@ extern XtAppContext app; #include "xlockmore.h" -#ifdef USE_GL /* whole file */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 timeval last_iteration; - struct timeval 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; -static Bool do_bbox; - 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" }, -#if 0 - { "-bbox", ".bbox", XrmoptionNoArg, "True" }, - { "+bbox", ".bbox", XrmoptionNoArg, "False" }, -#endif + { "-explode", ".explode", XrmoptionSepArg, DEF_EXPLODE }, + { "-angvel", ".angvel", XrmoptionSepArg, DEF_ANGVEL }, + { "-accel", ".accel", XrmoptionSepArg, DEF_ACCEL }, + { "-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" }, }; 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}, - {(caddr_t *) &do_bbox, "bbox", "BBox", DEF_BBOX, t_Bool}, + {(caddr_t *) &explode, "explode", "Explode", DEF_EXPLODE, t_Float}, + {(caddr_t *) &angvel, "angvel", "Angular Velocity", DEF_ANGVEL, t_Float}, + {(caddr_t *) &accel, "accel", "Acceleration", DEF_ACCEL, t_Float}, + {(caddr_t *) &statictime, "statictime", "Static Time", DEF_STATICTIME, t_Int}, + {(caddr_t *) &yangvel, "yangvel", "Angular Velocity about Y axis", DEF_YANGVEL, t_Float}, + {(caddr_t *) &zangvel, "zangvel", "Angular Velocity about X axis", DEF_ZANGVEL, t_Float}, + {(caddr_t *) &interactive, "interactive", "Interactive", DEF_INTERACTIVE, t_Bool}, + {(caddr_t *) &altcolour, "altcolour", "Alternate Colour Scheme", DEF_ALTCOLOUR, t_Bool}, + {(caddr_t *) &titles, "titles", "Titles", DEF_TITLES, t_Bool}, + {(caddr_t *) &zoom, "zoom", "Zoom", DEF_ZOOM, t_Float}, + {(caddr_t *) &wireframe, "wireframe", "Wireframe", DEF_WIREFRAME, t_Bool}, }; ModeSpecOpt sws_opts = {countof(opts), opts, 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 { + 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; + int last_turn; + int debug; + + /* the shape of the model */ + float node[NODE_COUNT]; + + /* currently selected node for interactive mode */ + int selected; -/* 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 } + /* models */ + int prev_model; + int next_model; + + /* model morphing */ + int new_morph; + + /* colours */ + float colour[2][3]; + 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 */ + int 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 + +float colour[][2][3] = { + /* cyclic - green */ + { { 0.4, 0.8, 0.2 }, + { 1.0, 1.0, 1.0 } }, + /* acyclic - blue */ + { { 0.3, 0.1, 0.9 }, + { 1.0, 1.0, 1.0 } }, + /* invalid - grey */ + { { 0.3, 0.1, 0.9 }, + { 1.0, 1.0, 1.0 } }, + /* authentic - purple and green */ + { { 0.38, 0.0, 0.55 }, + { 0.0, 0.5, 0.34 } } }; -/* 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 } + }, + /* the models in the Rubik's snake manual */ +#define START_MODEL 1 + { "ball", + { RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, + RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, + RIGHT, LEFT, RIGHT, LEFT } + }, + { "snow", + { RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, + RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, + RIGHT, LEFT, LEFT, LEFT } + }, + { "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 } + }, + { "cat", + { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, + ZERO, PIN, PIN, ZERO, PIN, PIN, 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 } + }, + /* 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 } + }, + { "zigzag1", + { RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, + LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, + RIGHT, RIGHT, LEFT, LEFT } + }, + { "zigzag2", + { PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, + ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN } + }, + { "zigzag3", + { PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, + LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN } + }, + { "caterpillar", + { RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, + LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, + LEFT, LEFT } + }, + { "bow", + { RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, + LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, + RIGHT, RIGHT, LEFT, LEFT } + }, + { "turtle", + { ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, + LEFT, RIGHT, LEFT, LEFT, PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, + RIGHT, RIGHT, RIGHT } + }, + { "basket", + { RIGHT, PIN, ZERO, ZERO, PIN, LEFT, ZERO, LEFT, LEFT, ZERO, + LEFT, PIN, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, ZERO, + PIN, LEFT } + }, + { "thing", + { PIN, RIGHT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, + LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, + RIGHT, LEFT, LEFT } + }, + { "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 } + }, + { "seal", + { RIGHT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, + LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, LEFT, PIN, RIGHT, + RIGHT, LEFT } + }, + { "dog", + { ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, + ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN } + }, + { "frog", + { RIGHT, RIGHT, LEFT, LEFT, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, + RIGHT, ZERO, LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, + RIGHT, LEFT, LEFT } + }, + { "quavers", + { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO, ZERO, ZERO, + RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, ZERO, LEFT, LEFT, + RIGHT, LEFT, RIGHT, RIGHT } + }, + { "fly", + { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO, PIN, ZERO, ZERO, + LEFT, PIN, RIGHT, ZERO, ZERO, PIN, ZERO, LEFT, LEFT, RIGHT, LEFT, + RIGHT, RIGHT } + }, + { "puppy", + { ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, + RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, + LEFT } + }, + { "stars", + { LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, + ZERO, ZERO, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, + RIGHT, LEFT } + }, + { "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 } + }, + { "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 } + }, + { "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 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 } + }, + { "arse 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 */ + { "Abstract", + { RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, PIN, ZERO, RIGHT, LEFT, RIGHT, ZERO } + }, + { "AlanH1", + { LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN } + }, + { "AlanH2", + { LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT } + }, + { "AlanH3", + { LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN } + }, + { "AlanH4", + { ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT, RIGHT, PIN, 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 } + }, + { "Angel", + { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT } + }, + { "AnotherFigure", + { LEFT, PIN, RIGHT, ZERO, ZERO, PIN, RIGHT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, PIN, 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 } + }, + { "Basket", + { ZERO, RIGHT, RIGHT, ZERO, RIGHT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, ZERO, LEFT } + }, + { "Beetle", + { PIN, LEFT, RIGHT, ZERO, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO, LEFT, RIGHT, PIN, RIGHT } + }, + { "Bone", + { PIN, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, PIN } + }, + { "Bow", + { LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT } + }, + { "Bra", + { RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT } + }, + { "BronchoSaurian", + { ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, PIN } + }, + { "Cactus", + { PIN, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, 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 } + }, + { "Candlestick", + { LEFT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, RIGHT } + }, + { "Cat", + { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, 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 } + }, + { "Chains", + { PIN, ZERO, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, 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 } + }, + { "Chick", + { RIGHT, RIGHT, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, LEFT, LEFT } + }, + { "Clockwise", + { RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT } + }, + { "Cobra", + { ZERO, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, LEFT, ZERO, LEFT, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT } + }, + { "Cobra2", + { LEFT, ZERO, PIN, ZERO, PIN, LEFT, ZERO, PIN, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, PIN, ZERO, RIGHT, PIN, ZERO, PIN, ZERO, RIGHT } + }, + { "Cobra3", + { ZERO, LEFT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, LEFT } + }, + { "Compact1", + { ZERO, ZERO, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, PIN } + }, + { "Compact2", + { LEFT, PIN, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, 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 } + }, + { "Compact4", + { PIN, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, 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 } + }, + { "Contact", + { PIN, ZERO, ZERO, PIN, LEFT, LEFT, PIN, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO, PIN, RIGHT, PIN } + }, + { "Contact2", + { RIGHT, PIN, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, PIN, LEFT } + }, + { "Cook", + { ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, RIGHT, LEFT, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, PIN, LEFT, RIGHT, ZERO, RIGHT, ZERO, PIN } + }, + { "Counterclockwise", + { LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT } + }, + { "Cradle", + { LEFT, LEFT, ZERO, PIN, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, PIN, ZERO, RIGHT, RIGHT, LEFT, LEFT, ZERO, ZERO, RIGHT } + }, + { "Crankshaft", + { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO, PIN, RIGHT } + }, + { "Cross", + { ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN } + }, + { "Cross2", + { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, 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 } + }, + { "CrossVersion1", + { PIN, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN } + }, + { "CrossVersion2", + { RIGHT, LEFT, PIN, LEFT, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, LEFT, LEFT, PIN, LEFT, RIGHT } + }, + { "Crown", + { LEFT, ZERO, PIN, ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO, PIN, ZERO, RIGHT, LEFT, ZERO, PIN, ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO, PIN, 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 } + }, + { "Diamond", + { ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, LEFT } + }, + { "Dog", + { RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO, LEFT, RIGHT } + }, + { "DogFace", + { ZERO, ZERO, PIN, PIN, ZERO, LEFT, LEFT, RIGHT, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, RIGHT, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, PIN } + }, + { "DoublePeak", + { ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, LEFT, ZERO, PIN, ZERO, RIGHT, RIGHT, LEFT, PIN, LEFT, RIGHT } + }, + { "DoubleRoof", + { ZERO, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT } + }, + { "DoubleToboggan", + { ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO, ZERO, PIN } + }, + { "Doubled", + { LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, ZERO, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT } + }, + { "Doubled1", + { LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, ZERO, RIGHT, ZERO, RIGHT, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT } + }, + { "Doubled2", + { LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, RIGHT, ZERO, RIGHT, LEFT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT } + }, + { "DumblingSpoon", + { PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, RIGHT, 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 } + }, + { "EndlessBelt", + { ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, PIN, RIGHT, LEFT, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, ZERO, LEFT, RIGHT } + }, + { "Entrance", + { LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT } + }, + { "Esthetic", + { LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT } + }, + { "Explotion", + { RIGHT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT } + }, + { "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 } + }, + { "Face", + { ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT } + }, + { "Fantasy", + { LEFT, LEFT, RIGHT, PIN, ZERO, RIGHT, ZERO, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, ZERO, LEFT, ZERO, PIN, LEFT, RIGHT, RIGHT, RIGHT, PIN } + }, + { "Fantasy1", + { PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN } + }, + { "FaserGun", + { ZERO, ZERO, LEFT, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, RIGHT, ZERO, PIN } + }, + { "FelixW", + { ZERO, RIGHT, ZERO, PIN, LEFT, ZERO, LEFT, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, RIGHT, ZERO, RIGHT, PIN, ZERO, LEFT, 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 } + }, + { "FlatOnTheTop", + { ZERO, PIN, PIN, ZERO, PIN, RIGHT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, PIN } + }, + { "Fly", + { ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT } + }, + { "Fountain", + { LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, PIN, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, PIN, RIGHT, PIN } + }, + { "Frog", + { LEFT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, RIGHT, LEFT, RIGHT, RIGHT } + }, + { "Frog2", + { LEFT, ZERO, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, RIGHT, ZERO, RIGHT } + }, + { "Furby", + { PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, PIN, ZERO, ZERO, PIN } + }, + { "Gate", + { ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, ZERO, PIN, PIN, 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 } + }, + { "Globus", + { RIGHT, LEFT, ZERO, PIN, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, PIN, ZERO, RIGHT, LEFT, 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 } + }, + { "H", + { PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, LEFT, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, PIN, PIN, 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 } + }, + { "Heart", + { RIGHT, ZERO, ZERO, ZERO, PIN, LEFT, PIN, LEFT, RIGHT, RIGHT, ZERO, PIN, ZERO, LEFT, LEFT, RIGHT, PIN, RIGHT, PIN, ZERO, ZERO, ZERO, LEFT } + }, + { "Heart2", + { ZERO, PIN, ZERO, ZERO, LEFT, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO, RIGHT, ZERO, ZERO, PIN, 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 } + }, + { "HoleInTheMiddle1", + { ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT } + }, + { "HoleInTheMiddle2", + { ZERO, LEFT, RIGHT, ZERO, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT } + }, + { "HouseBoat", + { RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, RIGHT, PIN } + }, + { "HouseByHouse", + { LEFT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT } + }, + { "Infinity", + { LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT } + }, + { "Integral", + { RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT } + }, + { "Iron", + { ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, ZERO, RIGHT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, ZERO, RIGHT } + }, + { "JustSquares", + { RIGHT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, LEFT, PIN, RIGHT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, LEFT } + }, + { "Kink", + { ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, 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 } + }, + { "Leaf", + { ZERO, PIN, PIN, ZERO, ZERO, LEFT, ZERO, LEFT, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, 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 } + }, + { "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 } + }, + { "LunaModule", + { PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT } + }, + { "MagnifyingGlass", + { ZERO, ZERO, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, 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 } + }, + { "Microscope", + { PIN, PIN, ZERO, ZERO, PIN, ZERO, RIGHT, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, LEFT, ZERO, PIN, PIN, ZERO, PIN, PIN } + }, + { "Mirror", + { PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT, RIGHT, PIN, RIGHT, ZERO, PIN, PIN, 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 } + }, + { "Mole", + { ZERO, RIGHT, ZERO, RIGHT, LEFT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, LEFT, ZERO, RIGHT, RIGHT, PIN, LEFT } + }, + { "Monk", + { LEFT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT } + }, + { "Mountain", + { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, LEFT, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, 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 } + }, + { "MouseWithoutTail", + { ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, 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 } + }, + { "Necklace", + { ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, RIGHT, 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 } + }, + { "NoClue", + { ZERO, RIGHT, PIN, LEFT, LEFT, LEFT, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, RIGHT, RIGHT, RIGHT, PIN, LEFT, 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 } + }, + { "Obelisk", + { PIN, ZERO, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, 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 } + }, + { "Ostrich2", + { PIN, PIN, ZERO, PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO } + }, + { "PairOfGlasses", + { ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, LEFT, ZERO, PIN, ZERO, RIGHT, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, 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 } + }, + { "Penis", + { PIN, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, PIN, PIN } + }, + { "PictureCommingSoon", + { LEFT, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, ZERO, RIGHT, RIGHT } + }, + { "Pitti", + { LEFT, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, RIGHT } + }, + { "Plait", + { LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT } + }, + { "Platform", + { RIGHT, PIN, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT } + }, + { "PodRacer", + { ZERO, PIN, ZERO, PIN, RIGHT, PIN, ZERO, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, ZERO, PIN, LEFT } + }, + { "Pokemon", + { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT } + }, + { "Prawn", + { RIGHT, PIN, ZERO, PIN, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT, PIN, ZERO, PIN, LEFT } + }, + { "Propeller", + { ZERO, ZERO, ZERO, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, RIGHT, ZERO, LEFT, RIGHT } + }, + { "Pyramid", + { ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, RIGHT, LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, LEFT } + }, + { "QuarterbackTiltedAndReadyToHut", + { PIN, ZERO, RIGHT, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, LEFT, ZERO, PIN } + }, + { "Ra", + { PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT } + }, + { "Rattlesnake", + { LEFT, ZERO, LEFT, ZERO, LEFT, ZERO, LEFT, LEFT, ZERO, LEFT, ZERO, LEFT, ZERO, LEFT, RIGHT, ZERO, PIN, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT } + }, + { "Revelation", + { ZERO, ZERO, ZERO, PIN, ZERO, ZERO, PIN, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN } + }, + { "Revolution1", + { LEFT, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT } + }, + { "Ribbon", + { RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT } + }, + { "Rocket", + { RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, RIGHT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, ZERO, LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT } + }, + { "Roofed", + { ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, ZERO, PIN, ZERO, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, ZERO, PIN, ZERO, RIGHT } + }, + { "Roofs", + { PIN, PIN, RIGHT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, LEFT, PIN, PIN } + }, + { "RowHouses", + { RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT } + }, + { "Sculpture", + { RIGHT, LEFT, PIN, ZERO, ZERO, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, PIN, LEFT, RIGHT, LEFT } + }, + { "Seal", + { LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, LEFT } + }, + { "Seal2", + { RIGHT, PIN, ZERO, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, 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 } + }, + { "Shelter", + { LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT } + }, + { "Ship", + { PIN, RIGHT, LEFT, LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, LEFT, ZERO, PIN, PIN } + }, + { "Shpongle", + { LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, 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 } + }, + { "SmallShip", + { ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, LEFT, RIGHT, ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, LEFT } + }, + { "SnakeReadyToStrike", + { LEFT, ZERO, LEFT, ZERO, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, LEFT, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, LEFT } + }, + { "Snakes14", + { RIGHT, RIGHT, PIN, ZERO, RIGHT, LEFT, RIGHT, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, ZERO, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, LEFT, RIGHT } + }, + { "Snakes15", + { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO, PIN, RIGHT } + }, + { "Snakes18", + { PIN, PIN, LEFT, PIN, LEFT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, PIN } + }, + { "Snowflake", + { LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT } + }, + { "Snowman", + { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, 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 } + }, + { "Spaceship", + { PIN, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, PIN } + }, + { "Spaceship2", + { PIN, PIN, LEFT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, LEFT, LEFT, PIN, PIN } + }, + { "Speedboat", + { LEFT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, LEFT, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT } + }, + { "Speedboat2", + { PIN, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, PIN, ZERO, RIGHT, PIN, LEFT } + }, + { "Spider", + { RIGHT, RIGHT, ZERO, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, RIGHT, LEFT, RIGHT, ZERO, ZERO, LEFT } + }, + { "Spitzbergen", + { PIN, LEFT, ZERO, RIGHT, RIGHT, LEFT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, PIN, RIGHT, LEFT, LEFT, 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 } + }, + { "SquareHole", + { PIN, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, PIN } + }, + { "Stage", + { RIGHT, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, 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 } + }, + { "Stairs2", + { ZERO, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, 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 } + }, + { "Swan", + { ZERO, PIN, ZERO, PIN, LEFT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, RIGHT } + }, + { "Swan2", + { PIN, ZERO, PIN, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN, PIN } + }, + { "Swan3", + { PIN, PIN, ZERO, ZERO, ZERO, RIGHT, ZERO, RIGHT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, ZERO, RIGHT } + }, + { "Symbol", + { RIGHT, RIGHT, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, RIGHT } + }, + { "Symmetry", + { RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, LEFT, RIGHT, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT } + }, + { "Symmetry2", + { ZERO, PIN, LEFT, LEFT, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, PIN, LEFT } + }, + { "TableFireworks", + { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, RIGHT, LEFT, ZERO, RIGHT, PIN } + }, + { "Tapering", + { ZERO, ZERO, RIGHT, LEFT, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, 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 } + }, + { "TeaLightStick", + { RIGHT, ZERO, PIN, PIN, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN } + }, + { "Tent", + { RIGHT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, 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 } + }, + { "Terrier", + { PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, 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 } + }, + { "ThreePeaks", + { RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT } + }, + { "ToTheFront", + { ZERO, PIN, RIGHT, LEFT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, LEFT, LEFT, PIN, ZERO, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT } + }, + { "Top", + { PIN, LEFT, LEFT, PIN, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, RIGHT, PIN, RIGHT, RIGHT, PIN, 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 } + }, + { "Triangle", + { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT } + }, + { "Tripple", + { PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN } + }, + { "Turtle", + { RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO } + }, + { "Twins", + { ZERO, PIN, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, ZERO, ZERO, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, PIN, 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 } + }, + { "TwoWings", + { PIN, LEFT, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, 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 } + }, + { "USSEnterprice", + { LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, 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 } + }, + { "Upright", + { ZERO, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, 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 } + }, + { "Valley", + { ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, 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 } + }, + { "View", + { ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, PIN, RIGHT, PIN, LEFT } + }, + { "Waterfall", + { LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT } + }, + { "WindWheel", + { PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, 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 } + }, + { "WindowToTheWorld", + { PIN, LEFT, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, 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 } + }, + { "WingNut", + { ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, RIGHT, RIGHT, PIN, 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 } + }, + { "WithoutName", + { PIN, RIGHT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN } + }, + { "Wolf", + { ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN } + }, + { "X", + { LEFT, ZERO, ZERO, PIN, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, RIGHT, PIN, 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); -#endif - for (i = 0; i < 24; i++) { - models[(*count)-1].node[i] = rotations[i] * 90.0; - } - return models; -} +int models = (sizeof(model) / sizeof(struct model_s)); -/* 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"); -#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; - } - } - return models; -} +#define VOFFSET 0.045 -model_t * load_models(char * dirpath, model_t * models, int * count) -{ - char name[1024]; - struct dirent * dp; - DIR * dfd; - - 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); -#endif - models = load_modelfile(name, models, count); - } - } - } - closedir(dfd); - return models; -} +#define X_MASK 1 +#define Y_MASK 2 +#define Z_MASK 4 -/* 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 )) +/* the connecting string that holds the snake together */ +#define MAGICAL_RED_STRING 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)); -} +#define GETSCALAR(vec,mask) ((vec)==(mask) ? 1 : ((vec)==-(mask) ? -1 : 0 )) -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 / 90.0)) { - case ZERO: - dest_dir = -prev_src_dir; - break; - case PIN: - dest_dir = prev_src_dir; - break; - case RIGHT: - case LEFT: - 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; - } +#ifndef MAX +# define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif +#ifndef MIN +# define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif - 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; - - prev_src_dir = src_dir; - prev_dest_dir = dest_dir; - } - - /* 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; - } - } -} +#define RAND(n) ((random() & 0x7fffffff) % ((long) (n))) +#define RANDSIGN() ((random() & 1) ? 1 : -1) -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; - } -} +/* 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 -void start_morph(int model_index, int immediate, glsnake_configuration * bp) -{ - 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]; - } - - calc_snake_metrics(bp); - set_colours(bp, 0); - bp->cur_model = model_index; - bp->morphing = 1; -} +typedef float (*morphFunc)(long); -/* convex hull */ +#ifdef HAVE_GLUT +/* forward definitions for GLUT functions */ +void calc_rotation(); +inline void ui_mousedrag(); +#endif -/* model labels */ -void draw_label(ModeInfo * mi) -{ - glsnake_configuration *bp = &glc[MI_SCREEN(mi)]; - - 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; - - 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); +void gl_init( +#ifndef HAVE_GLUT + ModeInfo * mi +#endif + ) { + 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 (!wireframe) { + 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); } - */ - - 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]); - } - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPopAttrib(); } -/* load the fonts -- this function borrowed from molecule.c */ -static void load_font(ModeInfo * mi, char * res, XFontStruct ** fontp, GLuint * dlistp) +void gettime(snaketime *t) { - const char * font = get_string_resource(res, "Font"); - XFontStruct * f; - Font id; - int first, last; - - if (!font) - font = "-*-helvetica-medium-r-*-*-*-120-*"; - - f = XLoadQueryFont(mi->dpy, font); - if (!f) - f = XLoadQueryFont(mi->dpy, "fixed"); - - id = f->fid; - first = f->min_char_or_byte2; - last = f->max_char_or_byte2; - - clear_gl_error(); - *dlistp = glGenLists((GLuint) last + 1); - check_gl_error("glGenLists"); - glXUseXFont(id, first, last - first + 1, *dlistp + first); - check_gl_error("glXUseXFont"); - - *fontp = f; +#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 */ } +#ifndef HAVE_GLUT +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; + if (!font) + font = "-*-helvetica-medium-r-*-*-*-120-*"; -/* 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(); -} + f = XLoadQueryFont(mi->dpy, font); + if (!f) + f = XLoadQueryFont(mi->dpy, "fixed"); -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); - } + id = f->fid; + first = f->min_char_or_byte2; + last = f->max_char_or_byte2; + + clear_gl_error(); + *dlistp = glGenLists((GLuint) last + 1); + check_gl_error("glGenLists"); + glXUseXFont(id, first, last - first + 1, *dlistp + first); + check_gl_error("glXUseXFont"); + + *fontp = f; } +#endif -/* lifted from lament.c */ -#define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n)))) -#define RANDSIGN() ((random() & 1) ? 1 : -1) +void start_morph(int model_index, int immediate); -void glsnake_init(ModeInfo *mi) -{ - glsnake_configuration * bp; - int wire = MI_IS_WIREFRAME(mi); +/* wot initialises it */ +void glsnake_init( +#ifndef HAVE_GLUT +ModeInfo * mi +#endif +) { +#ifndef HAVE_GLUT + struct glsnake_cfg * bp; - if (!glc) { - glc = (glsnake_configuration *) calloc(MI_NUM_SCREENS(mi), sizeof(glsnake_configuration)); + /* set up the conf struct and glx contexts */ if (!glc) { - fprintf(stderr, "%s: out of memory\n", progname); - exit(1); + 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); + } } bp = &glc[MI_SCREEN(mi)]; - } - - 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)); - } - - /* 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; - - { -# ifdef GETTIMEOFDAY_TWO_ARGS - struct timezone tzp; - gettimeofday(&bp->last_iteration, &tzp); -# else - gettimeofday(&bp->last_iteration); -# endif - } - - memcpy(&bp->last_morph, &(bp->last_iteration), - sizeof(bp->last_morph)); - /* 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) { + + 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, "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]); @@ -757,7 +1497,7 @@ void glsnake_init(ModeInfo *mi) 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]); @@ -767,18 +1507,17 @@ void glsnake_init(ModeInfo *mi) 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]); @@ -786,90 +1525,93 @@ void glsnake_init(ModeInfo *mi) 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(); - } else { + 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]); @@ -880,333 +1622,944 @@ void glsnake_init(ModeInfo *mi) 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(); + glEndList(); + +#ifdef HAVE_GLUT + /* initialise the rotation */ + calc_rotation(); +#endif } -/* "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 timeval 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; - } - - { -# ifdef GETTIMEOFDAY_TWO_ARGS - struct timezone tzp; - gettimeofday(¤t_time, &tzp); -# else - gettimeofday(¤t_time); -# endif - } - - /* Well, ftime gives time with millisecond resolution. - * if current time is exactly equal to last iteration, - * then don't do this block - * (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 - */ - iter_msec = ((long) current_time.tv_usec - bp->last_iteration.tv_usec)/1000L + - ((long) current_time.tv_sec - bp->last_iteration.tv_sec) * 1000L; - if (iter_msec) { - /* save the current time */ - memcpy(&bp->last_iteration, ¤t_time, - sizeof(bp->last_iteration)); +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(GL_TRANSFORM_BIT | GL_ENABLE_BIT); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); +#ifdef HAVE_GLUT + gluOrtho2D(0, glc->width, 0, glc->height); +#else + gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height); +#endif + glColor3f(1.0, 1.0, 1.0); + { + char interactstr[] = "interactive"; + char * s; + int i = 0; +#ifdef HAVE_GLUT + int w; +#endif + + if (interactive) + s = interactstr; + else + s = model[glc->next_model].name; +#ifdef HAVE_GLUT + w = glutBitmapLength(GLUT_BITMAP_HELVETICA_12, (unsigned char *) s); + glRasterPos2f(glc->width - w - 3, 4); + while (s[i] != '\0') + glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, s[i++]); +#else + glRasterPos2f(10, mi->xgwa.height - 10 - (bp->font->ascent + bp->font->descent)); + while (s[i] != '\0') + glCallList(bp->font_list + (int)s[i++]); +#endif + } + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glPopAttrib(); +} + +/* 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; +} + +/* 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, w/(GLfloat)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 +} + +/* 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)); +} + +/* 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]; - /* work out if we have to switch models */ - morf_msec = (bp->last_iteration.tv_usec - bp->last_morph.tv_usec)/1000L + - ((long) (bp->last_iteration.tv_sec - bp->last_morph.tv_sec) * 1000L); - - if ((morf_msec > statictime) && !bp->interactive) { - memcpy(&bp->last_morph, &(bp->last_iteration), - sizeof(bp->last_morph)); - start_morph(RAND(bp->m_count), 0, bp); + /* 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; + } + + 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; + } +} + +/* 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 - 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 */ + 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[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; +} + +/* Start morph process to this model */ +void start_morph(int model_index, int immediate) { + /* if immediate, don't bother morphing, go straight to the next model */ + if (immediate) { + int i; - if (bp->interactive && !bp->morphing) { - usleep(1); - return; + for (i = 0; i < NODE_COUNT; i++) + glc->node[i] = model[model_index].node[i]; } - if (!bp->dragging && !bp->interactive) { - bp->roty += 360/((1000/yspin)/iter_msec); - bp->rotz += 360/((1000/zspin)/iter_msec); + 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[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->morphing = 1; - /* work out the maximum angle for this iteration */ - iter_angle_max = 90.0 * (velocity/1000.0) * iter_msec; + 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; - 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); - } + 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])); + } } + + return MIN(largest_diff / largest_progress, 1.0); +} - if (!still_morphing) - bp->morphing = 0; +#ifdef HAVE_GLUT +void glsnake_idle(); - /* 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]; - } else { - /* We are going too fast, so we may as well let the - * cpu relax a little by sleeping for a millisecond. */ - usleep(1); - } +void restore_idle(int value) +{ + glutIdleFunc(glsnake_idle); } +#endif -static void -snake_bounding_box (ModeInfo *mi, - GLfloat *x1, GLfloat *y1, GLfloat *z1, - GLfloat *x2, GLfloat *y2, GLfloat *z2) +void quick_sleep(void) { - glsnake_configuration *bp = &glc[MI_SCREEN(mi)]; - int i; - GLdouble identity[16] = { 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 }; - GLint vp[4] = { 0, 0, 1, 1 }; - *x1 = *x2 = 0; - *y1 = *y2 = 0; - *z1 = *z2 = 0; - - glPushMatrix(); - glLoadIdentity(); - for (i = 0; i < 24; i++) - { - GLdouble model[16]; - GLdouble x, y, z; - GLfloat ang = bp->node[i].cur_angle; - - glGetDoublev (GL_MODELVIEW_MATRIX, model); - gluProject (0, 0, 0, model, identity, vp, &x, &y, &z); -fprintf (stderr, "%2d: %5.2f %5.2f %5.2f\n", i, (float)x, (float)y, (float)z); - - if (x < *x1) *x1 = x; - else if (x > *x2) *x2 = x; - if (y < *y1) *y1 = y; - else if (y > *y2) *y2 = y; - if (z < *z1) *z1 = z; - else if (z > *z2) *z2 = z; - - 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); - } -fprintf(stderr, "\n"); - glPopMatrix(); - -#if 0 - *x1 -= 1; - *y1 -= 1; - *z1 -= 1; - *x2 += 1; - *y2 += 1; - *z2 += 1; +#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; + } -static void -draw_bounding_box (ModeInfo *mi) -{ - static GLfloat c1[4] = { 0.4, 0.4, 0.4, 1.0 }; - static GLfloat c2[4] = { 1.0, 0.0, 0.0, 1.0 }; - int wire = MI_IS_WIREFRAME(mi); - GLfloat x1, y1, z1, x2, y2, z2; - snake_bounding_box (mi, &x1, &y1, &z1, &x2, &y2, &z2); - - glColor3f (c1[0], c1[1], c1[2]); -/* glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c1);*/ - glFrontFace(GL_CCW); - - glBegin(wire ? GL_LINE_LOOP : GL_QUADS); - glNormal3f(0, 1, 0); - glVertex3f(x1, y1, z1); glVertex3f(x1, y1, z2); - glVertex3f(x2, y1, z2); glVertex3f(x2, y1, z1); - glEnd(); - glBegin(wire ? GL_LINE_LOOP : GL_QUADS); - glNormal3f(0, -1, 0); - glVertex3f(x2, y2, z1); glVertex3f(x2, y2, z2); - glVertex3f(x1, y2, z2); glVertex3f(x1, y2, z1); - glEnd(); - glBegin(wire ? GL_LINE_LOOP : GL_QUADS); - glNormal3f(0, 0, 1); - glVertex3f(x1, y1, z1); glVertex3f(x2, y1, z1); - glVertex3f(x2, y2, z1); glVertex3f(x1, y2, z1); - glEnd(); - glBegin(wire ? GL_LINE_LOOP : GL_QUADS); - glNormal3f(0, 0, -1); - glVertex3f(x1, y2, z2); glVertex3f(x2, y2, z2); - glVertex3f(x2, y1, z2); glVertex3f(x1, y1, z2); - glEnd(); - glBegin(wire ? GL_LINE_LOOP : GL_QUADS); - glNormal3f(1, 0, 0); - glVertex3f(x1, y2, z1); glVertex3f(x1, y2, z2); - glVertex3f(x1, y1, z2); glVertex3f(x1, y1, z1); - glEnd(); - glBegin(wire ? GL_LINE_LOOP : GL_QUADS); - glNormal3f(-1, 0, 0); - glVertex3f(x2, y1, z1); glVertex3f(x2, y1, z2); - glVertex3f(x2, y2, z2); glVertex3f(x2, y2, z1); - glEnd(); - - glPushAttrib (GL_LIGHTING); - glDisable (GL_LIGHTING); - - glColor3f (c2[0], c2[1], c2[2]); - glBegin(GL_LINES); - if (x1 > 0) x1 = 0; if (x2 < 0) x2 = 0; - if (y1 > 0) y1 = 0; if (y2 < 0) y2 = 0; - if (z1 > 0) z1 = 0; if (z2 < 0) z2 = 0; - glVertex3f(x1, 0, 0); glVertex3f(x2, 0, 0); - glVertex3f(0 , y1, 0); glVertex3f(0, y2, 0); - glVertex3f(0, 0, z1); glVertex3f(0, 0, z2); - glEnd(); - - glPopAttrib(); + /* 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(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)); + + /* 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);*/ + + } + + /* 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); + } + } + + 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(); + } } +/* 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 -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); - } - - /* now draw each node along the snake */ - glPushMatrix(); - for (i = 0; i < 24; i++) { - - /* 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); - } + 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; +#endif + + /* clear the buffer */ + glClear(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]; + + /*printf("ang = %f\n", ang);*/ + + glTranslatef(0.5, 0.5, 0.5); /* move to center */ + glRotatef(90, 0.0, 0.0, -1.0); /* reorient */ + glTranslatef(1.0 + explode, 0.0, 0.0); /* move to new pos. */ + glRotatef(180 + 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]; -#if 0 /* #### */ - if (i == 0) glColor3f(0.0, 1.0, 1.0); + /*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); + glColor3f(1.0, 0.0, 0.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 - /* draw the node */ - glCallList(bp->node_list); + glPushMatrix(); + glTranslatef(-com[0], -com[1], -com[2]); - /* now work out where to draw the next one */ +#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 */ + glColor3f(1.0, 1.0, 0.0); + else + glColor3fv(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, -1.0); /* reorient */ + glTranslatef(1.0 + explode, 0.0, 0.0); /* move to new pos. */ + glRotatef(180 + ang, 1.0, 0.0, 0.0); /* pivot to new angle */ + glTranslatef(-0.5, -0.5, -0.5); /* return from center */ + } - /* interpolate between models */ - ang = bp->node[i].cur_angle; + glPopMatrix(); + + if (titles) +#ifdef HAVE_GLUT + draw_title(); +#else + draw_title(mi); +#endif - 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); - } +#ifndef HAVE_GLUT + glsnake_idle(bp); +#endif + + glFlush(); +#ifdef HAVE_GLUT + glutSwapBuffers(); +#else + glXSwapBuffers(dpy, window); +#endif +} - glPopMatrix(); +#ifdef HAVE_GLUT +/* anything that needs to be cleaned up goes here */ +void unmain() { + glutDestroyWindow(glc->window); + free(glc); +} - if (do_bbox) - draw_bounding_box (mi); +void ui_init(int *, char **); - if (labels) - draw_label(mi); - - if (mi->fps_p) - do_fps (mi); +int main(int argc, char ** argv) { + glc = malloc(sizeof(struct glsnake_cfg)); + memset(glc, 0, sizeof(struct glsnake_cfg)); - glsnake_idol(bp); - - glFlush(); - glXSwapBuffers(dpy, window); + glc->width = 320; + glc->height = 240; + + ui_init(&argc, argv); + + gettime(&glc->last_iteration); + memcpy(&glc->last_morph, &glc->last_iteration, sizeof(snaketime)); + srand((unsigned int)GETSECS(glc->last_iteration)); + + glc->prev_colour = glc->next_colour = COLOUR_ACYCLIC; + glc->next_model = RAND(models); + glc->prev_model = 0; + start_morph(glc->prev_model, 1); + + glsnake_init(); + + atexit(unmain); + glutSwapBuffers(); + glutMainLoop(); + + 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 m_s[3], m_e[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); } -#endif /* USE_GL */ +void ui_keyboard(unsigned char c, int x, int y) { + 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 */ + glc->next_model++; + glc->next_model %= models; + start_morph(glc->next_model, 0); + + /* Reset last_morph time */ + gettime(&glc->last_morph); + break; + case ',': + /* previous model */ + glc->next_model = (glc->next_model + models - 1) % models; + start_morph(glc->next_model, 0); + + /* Reset glc->last_morph time */ + gettime(&glc->last_morph); + break; + case '+': + angvel += DEF_ACCEL; + break; + case '-': + if (angvel > DEF_ACCEL) + angvel -= DEF_ACCEL; + 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 'p': + if (glc->paused) { + /* unpausing, reset last_iteration and last_morph time */ + gettime(&glc->last_iteration); + gettime(&glc->last_morph); + } + 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); + 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 { + glutReshapeWindow(glc->old_width, glc->old_height); + glutPositionWindow(50,50); + } + break; + case 't': + titles = 1 - titles; + if (interactive || glc->paused) + glutPostRedisplay(); + break; + case 'a': + 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 ui_special(int key, int x, int y) { + int i; + 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(); + + if (!unknown_key) + glutPostRedisplay(); +} + +void ui_mouse(int button, int state, int x, int y) { + if (button==0) { + switch (state) { + case GLUT_DOWN: + dragging = 1; + m_s[0] = M_SQRT1_2 * + (x - (glc->width / 2.0)) / (glc->width / 2.0); + m_s[1] = M_SQRT1_2 * + ((glc->height / 2.0) - y) / (glc->height / 2.0); + m_s[2] = sqrt(1-(m_s[0]*m_s[0]+m_s[1]*m_s[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(); +} + +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 */ + m_e[0] = (x - (glc->width/ 2.0)) / (glc->width / 2.0); + m_e[1] = ((glc->height / 2.0) - y) / (glc->height / 2.0); + /* calculate the normal of the vector... */ + norm = m_e[0] * m_e[0] + m_e[1] * m_e[1]; + /* check if norm is outside the sphere and wraparound if necessary */ + if (norm > 1.0) { + m_e[0] = -m_e[0]; + m_e[1] = -m_e[1]; + m_e[2] = sqrt(norm - 1); + } else { + /* the z value comes from projecting onto an elliptical spheroid */ + m_e[2] = sqrt(1 - norm); + } + + /* now here, build a quaternion from m_s and m_e */ + q[0] = m_s[1] * m_e[2] - m_s[2] * m_e[1]; + q[1] = m_s[2] * m_e[0] - m_s[0] * m_e[2]; + q[2] = m_s[0] * m_e[1] - m_s[1] * m_e[0]; + q[3] = m_s[0] * m_e[0] + m_s[1] * m_e[1] + m_s[2] * m_e[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(); +} + +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; +} +#endif /* HAVE_GLUT */