New LX fixes FlashEffect bug
[SugarCubes.git] / Model.pde
1 /**
2 * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND
3 *
4 * //\\ //\\ //\\ //\\
5 * ///\\\ ///\\\ ///\\\ ///\\\
6 * \\\/// \\\/// \\\/// \\\///
7 * \\// \\// \\// \\//
8 *
9 * EXPERTS ONLY!! EXPERTS ONLY!!
10 *
11 * Contains the model definitions for the cube structures.
12 */
13
14 /**
15 * Top-level model of the entire sculpture. This contains a list of
16 * every cube on the sculpture, which forms a hierarchy of faces, strips
17 * and points.
18 */
19 public static class Model extends LXModel {
20
21 public final List<Tower> towers;
22 public final List<Cube> cubes;
23 public final List<Face> faces;
24 public final List<Strip> strips;
25
26 public final Map<String, Cube> cubeTable;
27
28 private final Cube[] _cubes;
29
30 public Model(List<Tower> towerList, Cube[] cubeArr) {
31 super(new Fixture(cubeArr));
32 Fixture fixture = (Fixture) this.fixtures.get(0);
33
34 _cubes = cubeArr;
35
36 // Make unmodifiable accessors to the model data
37 List<Cube> cubeList = new ArrayList<Cube>();
38 List<Face> faceList = new ArrayList<Face>();
39 List<Strip> stripList = new ArrayList<Strip>();
40 Map<String, Cube> _cubeTable = new HashMap<String, Cube>();
41 for (Cube cube : _cubes) {
42 if (cube != null) {
43 _cubeTable.put(cube.id, cube);
44 cubeList.add(cube);
45 for (Face face : cube.faces) {
46 faceList.add(face);
47 for (Strip strip : face.strips) {
48 stripList.add(strip);
49 }
50 }
51 }
52 }
53
54 this.towers = Collections.unmodifiableList(towerList);
55 this.cubes = Collections.unmodifiableList(cubeList);
56 this.faces = Collections.unmodifiableList(faceList);
57 this.strips = Collections.unmodifiableList(stripList);
58 this.cubeTable = Collections.unmodifiableMap(_cubeTable);
59 }
60
61 private static class Fixture extends LXAbstractFixture {
62
63 private Fixture(Cube[] cubeArr) {
64 for (Cube cube : cubeArr) {
65 if (cube != null) {
66 for (LXPoint point : cube.points) {
67 this.points.add(point);
68 }
69 }
70 }
71 }
72 }
73
74 /**
75 * TODO(mcslee): figure out better solution
76 *
77 * @param index
78 * @return
79 */
80 public Cube getCubeByRawIndex(int index) {
81 return _cubes[index];
82 }
83
84 public Cube getCubeById(String id) {
85 return this.cubeTable.get(id);
86 }
87 }
88
89 /**
90 * Model of a set of cubes stacked in a tower
91 */
92 public static class Tower extends LXModel {
93
94 /**
95 * Tower id
96 */
97 public final String id;
98
99 /**
100 * Immutable list of cubes
101 */
102 public final List<Cube> cubes;
103
104 /**
105 * Immutable list of faces
106 */
107 public final List<Face> faces;
108
109 /**
110 * Immutable list of strips
111 */
112 public final List<Strip> strips;
113
114 /**
115 * Constructs a tower model from these cubes
116 *
117 * @param cubes Array of cubes
118 */
119 public Tower(String id, List<Cube> cubes) {
120 super(cubes.toArray(new Cube[] {}));
121
122 this.id = id;
123
124 List<Cube> cubeList = new ArrayList<Cube>();
125 List<Face> faceList = new ArrayList<Face>();
126 List<Strip> stripList = new ArrayList<Strip>();
127
128 for (Cube cube : cubes) {
129 cubeList.add(cube);
130 for (Face face : cube.faces) {
131 faceList.add(face);
132 for (Strip strip : face.strips) {
133 stripList.add(strip);
134 }
135 }
136 }
137 this.cubes = Collections.unmodifiableList(cubeList);
138 this.faces = Collections.unmodifiableList(faceList);
139 this.strips = Collections.unmodifiableList(stripList);
140 }
141 }
142
143 /**
144 * Model of a single cube, which has an orientation and position on the
145 * car. The position is specified in x,y,z coordinates with rotation. The
146 * x axis is left->right, y is bottom->top, and z is front->back.
147 *
148 * A cube's x,y,z position is specified as the left, bottom, front corner.
149 *
150 * Dimensions are all specified in real-world inches.
151 */
152 public static class Cube extends LXModel {
153
154 public final static int FACES_PER_CUBE = 4;
155 public static final int POINTS_PER_STRIP = 16;
156
157 public final static int STRIPS_PER_CUBE = FACES_PER_CUBE*Face.STRIPS_PER_FACE;
158 public final static int POINTS_PER_CUBE = STRIPS_PER_CUBE*POINTS_PER_STRIP;
159 public final static int POINTS_PER_FACE = Face.STRIPS_PER_FACE*POINTS_PER_STRIP;
160
161 public final static float EDGE_HEIGHT = 21.75f;
162 public final static float EDGE_WIDTH = 24.625f;
163 public final static float CHANNEL_WIDTH = 1.5f;
164
165 public final static Face.Metrics FACE_METRICS = new Face.Metrics(
166 new Strip.Metrics(EDGE_WIDTH, POINTS_PER_STRIP),
167 new Strip.Metrics(EDGE_HEIGHT, POINTS_PER_STRIP)
168 );
169
170 public final String id;
171
172 /**
173 * Immutable list of all cube faces
174 */
175 public final List<Face> faces;
176
177 /**
178 * Immutable list of all strips
179 */
180 public final List<Strip> strips;
181
182 /**
183 * Front left corner x coordinate
184 */
185 public final float x;
186
187 /**
188 * Front left corner y coordinate
189 */
190 public final float y;
191
192 /**
193 * Front left corner z coordinate
194 */
195 public final float z;
196
197 /**
198 * Rotation about the x-axis
199 */
200 public final float rx;
201
202 /**
203 * Rotation about the y-axis
204 */
205 public final float ry;
206
207 /**
208 * Rotation about the z-axis
209 */
210 public final float rz;
211
212 public Cube(String id, float x, float y, float z, float rx, float ry, float rz) {
213 super(new Fixture(x, y, z, rx, ry, rz));
214 Fixture fixture = (Fixture) this.fixtures.get(0);
215
216 this.id = id;
217
218 while (rx < 0) rx += 360;
219 while (ry < 0) ry += 360;
220 while (rz < 0) rz += 360;
221 rx = rx % 360;
222 ry = ry % 360;
223 rz = rz % 360;
224
225 this.x = x;
226 this.y = y;
227 this.z = z;
228 this.rx = rx;
229 this.ry = ry;
230 this.rz = rz;
231
232 this.faces = Collections.unmodifiableList(fixture.faces);
233 this.strips = Collections.unmodifiableList(fixture.strips);
234 }
235
236 private static class Fixture extends LXAbstractFixture {
237
238 private final List<Face> faces = new ArrayList<Face>();
239 private final List<Strip> strips = new ArrayList<Strip>();
240
241 private Fixture(float x, float y, float z, float rx, float ry, float rz) {
242 LXTransform t = new LXTransform();
243 t.translate(x, y, z);
244 t.rotateX(rx * PI / 180.);
245 t.rotateY(ry * PI / 180.);
246 t.rotateZ(rz * PI / 180.);
247
248 for (int i = 0; i < FACES_PER_CUBE; i++) {
249 Face face = new Face(FACE_METRICS, (ry + 90*i) % 360, t);
250 this.faces.add(face);
251 for (Strip s : face.strips) {
252 this.strips.add(s);
253 }
254 for (LXPoint p : face.points) {
255 this.points.add(p);
256 }
257 t.translate(EDGE_WIDTH, 0, 0);
258 t.rotateY(HALF_PI);
259 }
260 }
261 }
262 }
263
264 /**
265 * A face is a component of a cube. It is comprised of four strips forming
266 * the lights on this side of a cube. A whole cube is formed by four faces.
267 */
268 public static class Face extends LXModel {
269
270 public final static int STRIPS_PER_FACE = 4;
271
272 public static class Metrics {
273 final Strip.Metrics horizontal;
274 final Strip.Metrics vertical;
275
276 public Metrics(Strip.Metrics horizontal, Strip.Metrics vertical) {
277 this.horizontal = horizontal;
278 this.vertical = vertical;
279 }
280 }
281
282 /**
283 * Immutable list of strips
284 */
285 public final List<Strip> strips;
286
287 /**
288 * Rotation of the face about the y-axis
289 */
290 public final float ry;
291
292 Face(Metrics metrics, float ry, LXTransform transform) {
293 super(new Fixture(metrics, ry, transform));
294 Fixture fixture = (Fixture) this.fixtures.get(0);
295 this.ry = ry;
296 this.strips = Collections.unmodifiableList(fixture.strips);
297 }
298
299 private static class Fixture extends LXAbstractFixture {
300
301 private final List<Strip> strips = new ArrayList<Strip>();
302
303 private Fixture(Metrics metrics, float ry, LXTransform transform) {
304 transform.push();
305 transform.translate(0, metrics.vertical.length, 0);
306 for (int i = 0; i < STRIPS_PER_FACE; i++) {
307 boolean isHorizontal = (i % 2 == 0);
308 Strip.Metrics stripMetrics = isHorizontal ? metrics.horizontal : metrics.vertical;
309 Strip strip = new Strip(stripMetrics, ry, transform, isHorizontal);
310 this.strips.add(strip);
311 transform.translate(isHorizontal ? metrics.horizontal.length : metrics.vertical.length, 0, 0);
312 transform.rotateZ(HALF_PI);
313 for (LXPoint p : strip.points) {
314 this.points.add(p);
315 }
316 }
317 transform.pop();
318 }
319 }
320 }
321
322 /**
323 * A strip is a linear run of points along a single edge of one cube.
324 */
325 public static class Strip extends LXModel {
326
327 public static final float POINT_SPACING = 18.625f / 15.f;
328
329 public static class Metrics {
330
331 public final float length;
332 public final int numPoints;
333
334 public Metrics(float length, int numPoints) {
335 this.length = length;
336 this.numPoints = numPoints;
337 }
338 }
339
340 public final Metrics metrics;
341
342 /**
343 * Whether this is a horizontal strip
344 */
345 public final boolean isHorizontal;
346
347 /**
348 * Rotation about the y axis
349 */
350 public final float ry;
351
352 public Object obj1 = null, obj2 = null;
353
354 Strip(Metrics metrics, float ry, List<LXPoint> points, boolean isHorizontal) {
355 super(points);
356 this.isHorizontal = isHorizontal;
357 this.metrics = metrics;
358 this.ry = ry;
359 }
360
361 Strip(Metrics metrics, float ry, LXTransform transform, boolean isHorizontal) {
362 super(new Fixture(metrics, ry, transform));
363 this.metrics = metrics;
364 this.isHorizontal = isHorizontal;
365 this.ry = ry;
366 }
367
368 private static class Fixture extends LXAbstractFixture {
369 private Fixture(Metrics metrics, float ry, LXTransform transform) {
370 float offset = (metrics.length - (metrics.numPoints - 1) * POINT_SPACING) / 2.f;
371 transform.push();
372 transform.translate(offset, -Cube.CHANNEL_WIDTH/2.f, 0);
373 for (int i = 0; i < metrics.numPoints; i++) {
374 LXPoint point = new LXPoint(transform.x(), transform.y(), transform.z());
375 this.points.add(point);
376 transform.translate(POINT_SPACING, 0, 0);
377 }
378 transform.pop();
379 }
380 }
381 }
382