/* JitterbugModel.java History: 08 Jul 08 Extracted from JitterbugController.java. */ /** The device-independent representation of the jitterbug. It is organized into 12 points. Conceptually the points are grouped into triangles. Each point is a member of two triangles, and those two triangles are linked at that point. There are eight triangles in all, organized into two groups of four triangles, group A and group B. The linked triangles indirectly form six, most of the time non-planar, quadrilaterals which become squares at the greatest extent of the jitterbug. The jitterbug can be completely triangulated by diagonalizing these quadrilaterals. There are two ways to do it, hence two groups of six lines. Based on notes of October 14, 16 and 24, 1981. At its maximum extension, the jitterbug has radius 1.0. */ public class JitterbugModel { /** The representation for each of the eight triangles making up the jitterbug. */ public static class Triangle { public final static int GROUP_A = 0; public final static int GROUP_B = 1; public final static int NGROUPS = 2; /** Zero-based indexes into the array of points for the JitterbugModel. */ public final int m_p1, m_p2, m_p3; public final int m_group; /** Requires three zero-based indexes (not equal to each other) into the array of points for the JitterbugModel and a value indicating which of the two groups the Triangle belongs to, GROUP_A (value == 0) or GROUP_B (value == 1). */ public Triangle(int p1, int p2, int p3, int group) { if (p1 < 0 || p1 > NPOINTS - 1 || p1 == p2 || p1 == p3 || p2 < 0 || p2 > NPOINTS - 1 || p2 == p3 || p3 < 0 || p3 > NPOINTS - 1) throw new IllegalArgumentException("Bad triangle cluster: " + p1 + " " + p2 + " " + p3); m_p1 = p1; m_p2 = p2; m_p3 = p3; if (group != GROUP_A && group != GROUP_B) throw new IllegalArgumentException("Bad triangle group: " + group); m_group = group; } } /** The representation for each of the 12 lines which, in groups of six, triangulate the jitterbug. */ public static class Line { public final static int GROUP_A = 0; public final static int GROUP_B = 1; public final static int NGROUPS = 2; /** Zero-based indexes into the array of points for the JitterbugModel. */ public final int m_p1, m_p2; public final int m_group; /** Requires two zero-based indexes (both distinct as far as the index is concerned) into the array of points for the JitterbugModel and a value indicating which of the two groups the Line belongs to, GROUP_A (value == 0) or GROUP_B (value == 1). */ public Line(int p1, int p2, int group) { if (p1 < 0 || p1 > NPOINTS - 1 || p1 == p2 || p2 < 0 || p2 > NPOINTS - 1) throw new IllegalArgumentException("Bad point pair: " + p1 + " " + p2); m_p1 = p1; m_p2 = p2; m_group = group; } } /** Number of points in the model. */ public final static int NPOINTS = 12; /** Number of Triangles in the model. */ public final static int NTRIANGLES = 8; /** Number of Lines in the model. */ public final static int NLINES = 12; /** Representations for the eight Triangles in the model. */ public final static Triangle[] m_triangles; /** Representations for the 12 triangulating Lines in the model. */ public final static Line[] m_lines; /** Half the maximum length of the triangulating lines. */ public final static float HALFMAX = 0.5f*(float)Math.sqrt(8.0/3.0); /** Minimum value (in degrees) for angle of rotation of jitterbug's triangles. */ public static final int MIN_ANGLE = 0; /** Maximum value (in degrees) for angle of rotation of jitterbug's triangles. */ public static final int MAX_ANGLE = 120; /** Value (in degrees) for angle of rotation of jitterbug's triangles when it is in the octahedron state. */ public static final int OCTA_ANGLE = 0; /** Value (in degrees) for angle of rotation of jitterbug's triangles when it is in the t-icosahedron state. */ public static final int TICOSA_ANGLE = 30; /** Value (in degrees) for angle of rotation of jitterbug's triangles when it is in the icosahedron state. This value is an integral approximation within about a fifth of a degree of the floating-point representation. */ public static final int ICOSA_ANGLE = 38; /** Value (in degrees) for angle of rotation of jitterbug's triangles when it is in the vector equilibrium state. */ public static final int VE_ANGLE = 60; /** Greatest common multiple of TICOSA_ANGLE, ICOSA_ANGLE and VE_ANGLE. This factor is used to increment the angle as the model cycles. */ public static final int ANGLE_GCM = 2; /** Value to indicate when jitterbug is in default stage. The stage index is used to indicate when the jitterbug is in specific geometrical configurations. The default value is used when it's in none of these configurations. This is the smallest stage index. */ public static final int DEFAULT_STAGE = 1; /** Value to indicate when jitterbug is in first octahedral stage. @see #DEFAULT_STAGE */ public static final int OCTA1_STAGE = 2; /** Value to indicate when jitterbug is in first tensegrity icosa stage. @see #DEFAULT_STAGE */ public static final int TICOSA1_STAGE = 3; /** Value to indicate when jitterbug is in first icosahedral stage. @see #DEFAULT_STAGE */ public static final int ICOSA1_STAGE = 4; /** Value to indicate when jitterbug is in the vector equilibrium stage. @see #DEFAULT_STAGE */ public static final int VE_STAGE = 5; /** Value to indicate when jitterbug is in second icosahedral stage. @see #DEFAULT_STAGE */ public static final int ICOSA2_STAGE = 6; /** Value to indicate when jitterbug is in second tensegrity icosa stage. @see #DEFAULT_STAGE */ public static final int TICOSA2_STAGE = 7; /** Value to indicate when jitterbug is in second octahedral stage. @see #DEFAULT_STAGE */ public static final int OCTA2_STAGE = 8; /** Greatest value for the stage index. @see #DEFAULT_STAGE */ public static final int MAX_STAGE = OCTA2_STAGE; /** Get the current stage (stage value indicates when the jitterbug is in specific geometrical configurations). */ public static int getStage(int angle) { if (angle < MIN_ANGLE) angle = MIN_ANGLE; else if (angle > MAX_ANGLE) angle = MAX_ANGLE; switch (angle) { case OCTA_ANGLE: return OCTA1_STAGE; case TICOSA_ANGLE: return TICOSA1_STAGE; case ICOSA_ANGLE: return ICOSA1_STAGE; case VE_ANGLE: return VE_STAGE; case MAX_ANGLE - ICOSA_ANGLE: return ICOSA2_STAGE; case MAX_ANGLE - TICOSA_ANGLE: return TICOSA2_STAGE; case MAX_ANGLE - OCTA_ANGLE: return OCTA2_STAGE; default: return DEFAULT_STAGE; } } /** Get stage label (stage value indicates when the jitterbug is in specific geometrical configurations). */ public static String getStageLabel(int stage) { switch (stage) { case OCTA1_STAGE: return "Octahedron_(1)"; case TICOSA1_STAGE: return "Tensegrity_Icosahedron_(1)"; case ICOSA1_STAGE: return "Icosahedron_(1)"; case VE_STAGE: return "Vector_Equilibrium"; case ICOSA2_STAGE: return "Icosahedron_(2)"; case TICOSA2_STAGE: return "Tensegrity_Icosahedron_(2)"; case OCTA2_STAGE: return "Octahedron_(2)"; default: return "Default"; } } /** Get the angle corresponding to a particular stage. DEFAULT_STAGE is not admissible. @param stage value of stage @return angle in degrees */ public static int getAngle(int stage) { switch (stage) { case OCTA1_STAGE: return OCTA_ANGLE; case TICOSA1_STAGE: return TICOSA_ANGLE; case ICOSA1_STAGE: return ICOSA_ANGLE; case VE_STAGE: return VE_ANGLE; case ICOSA2_STAGE: return MAX_ANGLE - ICOSA_ANGLE; case TICOSA2_STAGE: return MAX_ANGLE - TICOSA_ANGLE; case OCTA2_STAGE: return MAX_ANGLE - OCTA_ANGLE; default: throw new IllegalArgumentException("Bad stage value: " + stage); } } /** Create the Triangle and Line representations. */ static { // initialize Triangles m_triangles = new Triangle[NTRIANGLES]; m_triangles[0] = new Triangle( 0, 2, 1, Triangle.GROUP_A); m_triangles[1] = new Triangle( 4, 6, 11, Triangle.GROUP_A); m_triangles[2] = new Triangle( 3, 8, 10, Triangle.GROUP_A); m_triangles[3] = new Triangle( 5, 7, 9, Triangle.GROUP_A); m_triangles[4] = new Triangle( 2, 6, 10, Triangle.GROUP_B); m_triangles[5] = new Triangle( 1, 8, 9, Triangle.GROUP_B); m_triangles[6] = new Triangle( 0, 7, 11, Triangle.GROUP_B); m_triangles[7] = new Triangle( 5, 3, 4, Triangle.GROUP_B); // initialize Lines m_lines = new Line[NLINES]; m_lines[ 0] = new Line(11, 2, Line.GROUP_A); m_lines[ 1] = new Line( 5, 8, Line.GROUP_A); m_lines[ 2] = new Line( 0, 9, Line.GROUP_A); m_lines[ 3] = new Line( 6, 3, Line.GROUP_A); m_lines[ 4] = new Line( 4, 7, Line.GROUP_A); m_lines[ 5] = new Line(10, 1, Line.GROUP_A); m_lines[ 6] = new Line( 6, 0, Line.GROUP_B); m_lines[ 7] = new Line( 3, 9, Line.GROUP_B); m_lines[ 8] = new Line( 1, 7, Line.GROUP_B); m_lines[ 9] = new Line( 4, 10, Line.GROUP_B); m_lines[10] = new Line( 5, 11, Line.GROUP_B); m_lines[11] = new Line( 2, 8, Line.GROUP_B); } /** Compute coordinate values for the jitterbug vertices and store in the supplied array. @param angle angle in degrees @param coords array sufficiently large (at least 3*NPOINTS) to hold the coordinate values */ public static void computeCoords(int angle, float coords[]) { double radian_angle = angle*Math.PI/180.0; // compute the coordinates of the first point using the formulas coords[0] = (float)(HALFMAX*Math.sin(radian_angle)); coords[1] = (float)(HALFMAX*Math.sin(2.0*Math.PI/3.0 - radian_angle)); coords[2] = (float)(0.0); // compute the coordinates of the other points using symmetry transforms coords[ 1*3 + 0] = +coords[1]; coords[ 1*3 + 1] = +coords[2]; coords[ 1*3 + 2] = +coords[0]; coords[ 2*3 + 0] = +coords[2]; coords[ 2*3 + 1] = +coords[0]; coords[ 2*3 + 2] = +coords[1]; coords[ 3*3 + 0] = -coords[0]; coords[ 3*3 + 1] = -coords[1]; coords[ 3*3 + 2] = +coords[2]; coords[ 4*3 + 0] = -coords[1]; coords[ 4*3 + 1] = +coords[2]; coords[ 4*3 + 2] = -coords[0]; coords[ 5*3 + 0] = +coords[2]; coords[ 5*3 + 1] = -coords[0]; coords[ 5*3 + 2] = -coords[1]; coords[ 6*3 + 0] = -coords[0]; coords[ 6*3 + 1] = +coords[1]; coords[ 6*3 + 2] = -coords[2]; coords[ 7*3 + 0] = +coords[1]; coords[ 7*3 + 1] = -coords[2]; coords[ 7*3 + 2] = -coords[0]; coords[ 8*3 + 0] = -coords[2]; coords[ 8*3 + 1] = -coords[0]; coords[ 8*3 + 2] = +coords[1]; coords[ 9*3 + 0] = +coords[0]; coords[ 9*3 + 1] = -coords[1]; coords[ 9*3 + 2] = -coords[2]; coords[10*3 + 0] = -coords[1]; coords[10*3 + 1] = -coords[2]; coords[10*3 + 2] = +coords[0]; coords[11*3 + 0] = -coords[2]; coords[11*3 + 1] = +coords[0]; coords[11*3 + 2] = -coords[1]; } }