redo with new anti-GLucose
[SugarCubes.git] / Model.pde
diff --git a/Model.pde b/Model.pde
new file mode 100644 (file)
index 0000000..3376246
--- /dev/null
+++ b/Model.pde
@@ -0,0 +1,365 @@
+/**
+ *     DOUBLE BLACK DIAMOND        DOUBLE BLACK DIAMOND
+ *
+ *         //\\   //\\                 //\\   //\\  
+ *        ///\\\ ///\\\               ///\\\ ///\\\
+ *        \\\/// \\\///               \\\/// \\\///
+ *         \\//   \\//                 \\//   \\//
+ *
+ *        EXPERTS ONLY!!              EXPERTS ONLY!!
+ *
+ * Contains the model definitions for the cube structures.
+ */
+
+/**
+ * Top-level model of the entire sculpture. This contains a list of
+ * every cube on the sculpture, which forms a hierarchy of faces, strips
+ * and points.
+ */
+public static class Model extends LXModel {
+
+  public final List<Tower> towers;
+  public final List<Cube> cubes;
+  public final List<Face> faces;
+  public final List<Strip> strips;
+
+  private final Cube[] _cubes;
+
+  public Model(List<Tower> towerList, Cube[] cubeArr) {
+    super(new Fixture(cubeArr));
+    Fixture fixture = (Fixture) this.fixtures.get(0);
+
+    _cubes = cubeArr;
+
+    // Make unmodifiable accessors to the model data
+    List<Cube> cubeList = new ArrayList<Cube>();
+    List<Face> faceList = new ArrayList<Face>();
+    List<Strip> stripList = new ArrayList<Strip>();
+    for (Cube cube : _cubes) {
+      if (cube != null) {
+        cubeList.add(cube);
+        for (Face face : cube.faces) {
+          faceList.add(face);
+          for (Strip strip : face.strips) {
+            stripList.add(strip);
+          }
+        }
+      }
+    }
+
+    this.towers = Collections.unmodifiableList(towerList);
+    this.cubes = Collections.unmodifiableList(cubeList);
+    this.faces = Collections.unmodifiableList(faceList);
+    this.strips = Collections.unmodifiableList(stripList);
+  }
+
+  private static class Fixture extends LXAbstractFixture {
+
+    private Fixture(Cube[] cubeArr) {
+      for (Cube cube : cubeArr) {
+        if (cube != null) {
+          for (LXPoint point : cube.points) {
+            this.points.add(point);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * TODO(mcslee): figure out better solution
+   * 
+   * @param index
+   * @return
+   */
+  public Cube getCubeByRawIndex(int index) {
+    return _cubes[index];
+  }
+}
+
+/**
+ * Model of a set of cubes stacked in a tower
+ */
+public static class Tower extends LXModel {
+  /**
+   * Immutable list of cubes
+   */
+  public final List<Cube> cubes;
+
+  /**
+   * Immutable list of faces
+   */
+  public final List<Face> faces;
+
+  /**
+        * Immutable list of strips
+        */
+  public final List<Strip> strips;
+
+  /**
+   * Constructs a tower model from these cubes
+   * 
+   * @param cubes Array of cubes
+   */
+  public Tower(List<Cube> cubes) {
+    super(cubes.toArray(new Cube[] {}));
+
+    List<Cube> cubeList = new ArrayList<Cube>();
+    List<Face> faceList = new ArrayList<Face>();
+    List<Strip> stripList = new ArrayList<Strip>();
+
+    for (Cube cube : cubes) {
+      cubeList.add(cube);
+      for (Face face : cube.faces) {
+        faceList.add(face);
+        for (Strip strip : face.strips) {
+          stripList.add(strip);
+        }
+      }
+    }
+    this.cubes = Collections.unmodifiableList(cubeList);
+    this.faces = Collections.unmodifiableList(faceList);
+    this.strips = Collections.unmodifiableList(stripList);
+  }
+}
+
+/**
+ * Model of a single cube, which has an orientation and position on the
+ * car. The position is specified in x,y,z coordinates with rotation. The
+ * x axis is left->right, y is bottom->top, and z is front->back.
+ * 
+ * A cube's x,y,z position is specified as the left, bottom, front corner.
+ * 
+ * Dimensions are all specified in real-world inches.
+ */
+public static class Cube extends LXModel {
+
+  public final static int FACES_PER_CUBE = 4;  
+  public static final int POINTS_PER_STRIP = 16;
+
+  public final static int STRIPS_PER_CUBE = FACES_PER_CUBE*Face.STRIPS_PER_FACE;
+  public final static int POINTS_PER_CUBE = STRIPS_PER_CUBE*POINTS_PER_STRIP;
+  public final static int POINTS_PER_FACE = Face.STRIPS_PER_FACE*POINTS_PER_STRIP;
+
+  public final static float EDGE_HEIGHT = 21.75f;
+  public final static float EDGE_WIDTH = 24.625f;
+  public final static float CHANNEL_WIDTH = 1.5f;
+
+  public final static Face.Metrics FACE_METRICS = new Face.Metrics(
+    new Strip.Metrics(EDGE_WIDTH, POINTS_PER_STRIP), 
+    new Strip.Metrics(EDGE_HEIGHT, POINTS_PER_STRIP)
+  );
+
+  /**
+   * Immutable list of all cube faces
+   */
+  public final List<Face> faces;
+
+  /**
+   * Immutable list of all strips
+   */
+  public final List<Strip> strips;
+
+  /**
+   * Front left corner x coordinate 
+   */
+  public final float x;
+
+  /**
+   * Front left corner y coordinate 
+   */
+  public final float y;
+
+  /**
+   * Front left corner z coordinate 
+   */
+  public final float z;
+
+  /**
+   * Rotation about the x-axis 
+   */
+  public final float rx;
+
+  /**
+   * Rotation about the y-axis 
+   */
+  public final float ry;
+
+  /**
+   * Rotation about the z-axis 
+   */
+  public final float rz;
+
+  public Cube(double x, double y, double z, double rx, double ry, double rz) {
+    this((float) x, (float) y, (float) z, (float) rx, (float) ry, (float) rz);
+  }
+
+  public Cube(float x, float y, float z, float rx, float ry, float rz) {
+    super(new Fixture(x, y, z, rx, ry, rz));
+    Fixture fixture = (Fixture) this.fixtures.get(0);
+
+    while (rx < 0) rx += 360;
+    while (ry < 0) ry += 360;
+    while (rz < 0) rz += 360;
+    rx = rx % 360;
+    ry = ry % 360;
+    rz = rz % 360;
+
+    this.x = x; 
+    this.y = y;
+    this.z = z;
+    this.rx = rx;
+    this.ry = ry;
+    this.rz = rz;
+
+    this.faces = Collections.unmodifiableList(fixture.faces);
+    this.strips = Collections.unmodifiableList(fixture.strips);
+  }
+
+  private static class Fixture extends LXAbstractFixture {
+
+    private final List<Face> faces = new ArrayList<Face>();
+    private final List<Strip> strips = new ArrayList<Strip>();
+
+    private Fixture(float x, float y, float z, float rx, float ry, float rz) {
+      LXTransform t = new LXTransform();
+      t.translate(x, y, z);
+      t.rotateX(rx * PI / 180.);
+      t.rotateY(ry * PI / 180.);
+      t.rotateZ(rz * PI / 180.);               
+
+      for (int i = 0; i < FACES_PER_CUBE; i++) {
+        Face face = new Face(FACE_METRICS, (ry + 90*i) % 360, t);
+        this.faces.add(face);
+        for (Strip s : face.strips) {
+          this.strips.add(s);
+        }
+        for (LXPoint p : face.points) {
+          this.points.add(p);
+        }
+        t.translate(EDGE_WIDTH, 0, 0);
+        t.rotateY(HALF_PI);
+      }
+    }
+  }
+}
+
+/**
+ * A face is a component of a cube. It is comprised of four strips forming
+ * the lights on this side of a cube. A whole cube is formed by four faces.
+ */
+public static class Face extends LXModel {
+
+  public final static int STRIPS_PER_FACE = 4;
+
+  public static class Metrics {
+    final Strip.Metrics horizontal;
+    final Strip.Metrics vertical;
+
+    public Metrics(Strip.Metrics horizontal, Strip.Metrics vertical) {
+      this.horizontal = horizontal;
+      this.vertical = vertical;
+    }
+  }
+
+  /**
+   * Immutable list of strips
+   */
+  public final List<Strip> strips;
+
+  /**
+   * Rotation of the face about the y-axis
+   */
+  public final float ry;
+
+  Face(Metrics metrics, float ry, LXTransform transform) {
+    super(new Fixture(metrics, ry, transform));
+    Fixture fixture = (Fixture) this.fixtures.get(0);
+    this.ry = ry;
+    this.strips = Collections.unmodifiableList(fixture.strips);
+  }
+
+  private static class Fixture extends LXAbstractFixture {
+
+    private final List<Strip> strips = new ArrayList<Strip>();
+
+    private Fixture(Metrics metrics, float ry, LXTransform transform) {
+      transform.push();
+      transform.translate(0, metrics.vertical.length, 0);
+      for (int i = 0; i < STRIPS_PER_FACE; i++) {
+        boolean isHorizontal = (i % 2 == 0);
+        Strip.Metrics stripMetrics = isHorizontal ? metrics.horizontal : metrics.vertical;
+        Strip strip = new Strip(stripMetrics, ry, transform, isHorizontal);
+        this.strips.add(strip);
+        transform.translate(isHorizontal ? metrics.horizontal.length : metrics.vertical.length, 0, 0);
+        transform.rotateZ(HALF_PI);
+        for (LXPoint p : strip.points) {
+          this.points.add(p);
+        }
+      }
+      transform.pop();
+    }
+  }
+}
+
+/**
+ * A strip is a linear run of points along a single edge of one cube.
+ */
+public static class Strip extends LXModel {
+
+  public static final float POINT_SPACING = 18.625f / 15.f;
+
+  public static class Metrics {
+
+    public final float length;
+    public final int numPoints;
+
+    public Metrics(float length, int numPoints) {
+      this.length = length;
+      this.numPoints = numPoints;
+    }
+  }
+
+  public final Metrics metrics;
+
+  /**
+   * Whether this is a horizontal strip
+   */
+  public final boolean isHorizontal;
+
+  /**
+   * Rotation about the y axis
+   */
+  public final float ry;
+
+  public Object obj1 = null, obj2 = null;
+
+  Strip(Metrics metrics, float ry, List<LXPoint> points, boolean isHorizontal) {
+    super(points);
+    this.isHorizontal = isHorizontal;
+    this.metrics = metrics;            
+    this.ry = ry;
+  }
+
+  Strip(Metrics metrics, float ry, LXTransform transform, boolean isHorizontal) {
+    super(new Fixture(metrics, ry, transform));
+    this.metrics = metrics;
+    this.isHorizontal = isHorizontal;
+    this.ry = ry;
+  }
+
+  private static class Fixture extends LXAbstractFixture {
+    private Fixture(Metrics metrics, float ry, LXTransform transform) {
+      float offset = (metrics.length - (metrics.numPoints - 1) * POINT_SPACING) / 2.f;
+      transform.push();
+      transform.translate(offset, -Cube.CHANNEL_WIDTH/2.f, 0);
+      for (int i = 0; i < metrics.numPoints; i++) {
+        LXPoint point = new LXPoint(transform.x(), transform.y(), transform.z());
+        this.points.add(point);
+        transform.translate(POINT_SPACING, 0, 0);
+      }
+      transform.pop();
+    }
+  }
+}
+