/** * 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 towers; public final List cubes; public final List faces; public final List strips; private final Cube[] _cubes; public Model(List towerList, Cube[] cubeArr) { super(new Fixture(cubeArr)); Fixture fixture = (Fixture) this.fixtures.get(0); _cubes = cubeArr; // Make unmodifiable accessors to the model data List cubeList = new ArrayList(); List faceList = new ArrayList(); List stripList = new ArrayList(); 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 cubes; /** * Immutable list of faces */ public final List faces; /** * Immutable list of strips */ public final List strips; /** * Constructs a tower model from these cubes * * @param cubes Array of cubes */ public Tower(List cubes) { super(cubes.toArray(new Cube[] {})); List cubeList = new ArrayList(); List faceList = new ArrayList(); List stripList = new ArrayList(); 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 faces; /** * Immutable list of all strips */ public final List 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 faces = new ArrayList(); private final List strips = new ArrayList(); 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 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 strips = new ArrayList(); 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 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(); } } }