From bffb6a80f723b2795e47e56a7d8eb3d65e9324a5 Mon Sep 17 00:00:00 2001 From: Mark Slee Date: Wed, 26 Mar 2014 17:16:44 -0700 Subject: [PATCH] Pandaboard support for new 15-cubes in tandem with Grizzly --- Grizzly.pde | 158 ------------------------- Internals.pde | 22 ++-- Mappings.pde | 107 ++++++++++++++--- Model.pde | 29 ++++- Output.pde | 270 +++++++++++++++++++++++++++++++++++++++++++ SugarCubes.pde | 1 - TestPatterns.pde | 38 +----- UIImplementation.pde | 52 ++++----- 8 files changed, 425 insertions(+), 252 deletions(-) delete mode 100644 Grizzly.pde create mode 100644 Output.pde diff --git a/Grizzly.pde b/Grizzly.pde deleted file mode 100644 index 5741778..0000000 --- a/Grizzly.pde +++ /dev/null @@ -1,158 +0,0 @@ -/** - * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND - * - * //\\ //\\ //\\ //\\ - * ///\\\ ///\\\ ///\\\ ///\\\ - * \\\/// \\\/// \\\/// \\\/// - * \\// \\// \\// \\//H - * - * EXPERTS ONLY!! EXPERTS ONLY!! - * - * If you are an artist, you may ignore this file! It contains - * the code to drive grizzly board outputs. - */ -import java.net.*; -GrizzlyOutput[] buildGrizzlies() throws SocketException, UnknownHostException { - return new GrizzlyOutput[] { - new GrizzlyOutput(lx, "192.168.88.100", 0, 0, 0, 39, 38, 40, 0, 37, 35, 0, 21, 20, 22, 0, 33, 32 ), - new GrizzlyOutput(lx, "192.168.88.104", 0, 13, 12, 0, 1, 2, 6, 5, 7, 0, 0, 4, 3, 9, 10, 11 ), - new GrizzlyOutput(lx, "192.168.88.105", 42, 41, 0, 43, 45, 44, 0, 0, 0, 0, 0, 0, 0, 24, 23, 25 ), - new GrizzlyOutput(lx, "192.168.88.107", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), - }; -} - -/** - * Grizzly Output, sends packets to one grizzly board with a fixed IP and a number - * of channels. - */ -class GrizzlyOutput extends LXDatagramOutput { - - public final String ipAddress; - - private int frameNumber = 0; - - public GrizzlyOutput(LX lx, String ipAddress, int ... cubeIndices) throws UnknownHostException, SocketException { - super(lx); - this.ipAddress = ipAddress; - int channelNum = 0; - for (int rawCubeIndex : cubeIndices) { - if (rawCubeIndex > 0) { - Cube cube = model.getCubeByRawIndex(rawCubeIndex); - addDatagram(new GrizzlyDatagram(this, channelNum, cube).setAddress(ipAddress)); - } - ++channelNum; - } - this.enabled.setValue(false); - } - - protected void beforeSend(int[] colors) { - ++frameNumber; - } - - public int getFrameNumber() { - return this.frameNumber; - } -} - -/** - * Datagram to a Grizzlyboard. A simple fixed OSC packet. - */ -static class GrizzlyDatagram extends LXDatagram { - - private static byte[] oscString(String s) { - int len = s.length(); - int padding = (4 - ((len + 1) % 4)) % 4; - byte[] bytes = new byte[len + 1 + padding]; - System.arraycopy(s.getBytes(), 0, bytes, 0, len); - for (int i = len; i < bytes.length; ++i) { - bytes[i] = 0; - } - return bytes; - } - - private static int oscIntCopy(int i, byte[] buffer, int pos) { - buffer[pos] = (byte) ((i >> 24) & 0xff); - buffer[pos + 1] = (byte) ((i >> 16) & 0xff); - buffer[pos + 2] = (byte) ((i >> 8) & 0xff); - buffer[pos + 3] = (byte) (i & 0xff); - return 4; - } - - private final static int[] STRIP_ORDERING = new int[] { 9, 8, 11, 5, 4, 7, 6, 10, 14, 2, 1, 0, 3, 13, 12, 15 }; - - private static int[] cubePointIndices(Cube cube) { - int[] pointIndices = new int[Cube.POINTS_PER_CUBE - 2]; - int pi = 0; - for (int stripIndex : STRIP_ORDERING) { - Strip strip = cube.strips.get(stripIndex); - int stripLen = ((stripIndex == 9) || (stripIndex == 15)) ? 15 : 16; - for (int i = stripLen-1; i >= 0; --i) { - pointIndices[pi++] = strip.points.get(i).index; - } - } - return pointIndices; - } - - private final static byte[] OSC_ADDRESS = oscString("/shady/pointbuffer"); - private final static byte[] OSC_TYPETAG = oscString(",iiiiibi"); - - private final static int HEADER_LENGTH = OSC_ADDRESS.length + OSC_TYPETAG.length + 24; - private final static int FOOTER_LENGTH = 4; - - private final static int OSC_PORT = 779; - - private GrizzlyOutput output; - - private int[] pointIndices; - - private final int frameNumberPos; - - private final int dataPos; - - public GrizzlyDatagram(GrizzlyOutput output, int channelNum, Cube cube) { - this(output, channelNum, cubePointIndices(cube)); - } - - public GrizzlyDatagram(GrizzlyOutput output, int channelNum, int[] pointIndices) { - super(HEADER_LENGTH + 4*pointIndices.length + FOOTER_LENGTH); - setPort(OSC_PORT); - - this.output = output; - this.pointIndices = pointIndices; - int dataLength = 4*pointIndices.length; - - int pos = 0; - - // OSC address - System.arraycopy(OSC_ADDRESS, 0, this.buffer, pos, OSC_ADDRESS.length); - pos += OSC_ADDRESS.length; - - // OSC typetag - System.arraycopy(OSC_TYPETAG, 0, this.buffer, pos, OSC_TYPETAG.length); - pos += OSC_TYPETAG.length; - this.frameNumberPos = pos; - pos += oscIntCopy(0, this.buffer, pos); // placeholder for frame number - pos += oscIntCopy(0xDEADBEEF, this.buffer, pos); - pos += oscIntCopy(channelNum, this.buffer, pos); - pos += oscIntCopy(0xFEEDBEEF, this.buffer, pos); - pos += oscIntCopy(dataLength, this.buffer, pos); - pos += oscIntCopy(dataLength, this.buffer, pos); - this.dataPos = pos; - - // end header - oscIntCopy(0xBEFFFFEB, this.buffer, this.buffer.length - 4); - } - - void onSend(int[] colors) { - oscIntCopy(this.output.getFrameNumber(), this.buffer, frameNumberPos); - int dataIndex = this.dataPos; - for (int index : this.pointIndices) { - color c = (index >= 0) ? colors[index] : 0; - this.buffer[dataIndex] = (byte) 0; // unused, alpha - this.buffer[dataIndex + 1] = (byte) ((c >> 16) & 0xff); // r - this.buffer[dataIndex + 2] = (byte) ((c >> 8) & 0xff); // g - this.buffer[dataIndex + 3] = (byte) (c & 0xff); // b - dataIndex += 4; - } - } -} diff --git a/Internals.pde b/Internals.pde index 31f8ffc..3c1c603 100644 --- a/Internals.pde +++ b/Internals.pde @@ -29,6 +29,7 @@ import ddf.minim.analysis.*; import processing.opengl.*; import rwmidi.*; import java.lang.reflect.*; +import java.net.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -56,7 +57,7 @@ Effects effects; LXEffect[] effectsArr; DiscreteParameter selectedEffect; MappingTool mappingTool; -GrizzlyOutput[] grizzlies; +IPOutput[] outputs = {}; PresetManager presetManager; MidiEngine midiEngine; @@ -64,6 +65,7 @@ MidiEngine midiEngine; boolean mappingMode = false; boolean debugMode = false; boolean simulationOn = true; +boolean structureOn = false; boolean diagnosticsOn = false; LXPattern restoreToPattern = null; PImage logo; @@ -170,16 +172,15 @@ void setup() { logTime("Setup MIDI devices"); // Build output driver - grizzlies = new GrizzlyOutput[]{}; try { - grizzlies = buildGrizzlies(); - for (LXOutput output : grizzlies) { + outputs = buildOutputs(); + for (LXOutput output : outputs) { lx.addOutput(output); } } catch (Exception x) { x.printStackTrace(); } - logTime("Built Grizzly Outputs"); + logTime("Built Grizzly + Panda Outputs"); // Mapping tool mappingTool = new MappingTool(lx); @@ -202,7 +203,7 @@ void setup() { // Right controls new UIPatternDeck(lx.ui, lx.engine.getDeck(RIGHT_DECK), "PATTERN B", width-144, 4, 140, 324), uiMidi = new UIMidi(midiEngine, width-144, 332, 140, 158), - new UIOutput(grizzlies, width-144, 494, 140, 106), + new UIOutput(outputs, width-144, 494, 140, 106), // Crossfader uiCrossfader = new UICrossfader(width/2-90, height-90, 180, 86), @@ -224,7 +225,7 @@ void setup() { println("Total setup: " + (millis() - startMillis) + "ms"); println("Hit the 'o' key to toggle live output"); - lx.engine.framesPerSecond.setValue(120); + lx.engine.framesPerSecond.setValue(60); lx.engine.setThreaded(true); } @@ -502,7 +503,7 @@ void keyPressed() { break; case 'o': case 'p': - for (LXOutput output : grizzlies) { + for (LXOutput output : outputs) { output.enabled.toggle(); } break; @@ -516,6 +517,11 @@ void keyPressed() { simulationOn = !simulationOn; } break; + case 'S': + if (!midiEngine.isQwertyEnabled()) { + structureOn = !structureOn; + } + break; } } diff --git a/Mappings.pde b/Mappings.pde index 8798fb5..30d0689 100644 --- a/Mappings.pde +++ b/Mappings.pde @@ -23,19 +23,90 @@ static final float FLOOR = 0; * The first value is the offset moving NE (towards back-right). * The second value is the offset moving NW (negative comes forward-right). */ -static final float[][] TOWER_CONFIG = new float[][] { - new float[] { 0, 0, RISER, 4 }, - new float[] { 25, -10, RISER, 4 }, - new float[] { 50, -22.5, FLOOR, 5 }, - new float[] { 17.25, -35.5, FLOOR, 6 }, - new float[] { 43.25, -51.5, RISER, 6 }, - new float[] { 69.25, -56, FLOOR, 6 }, - new float[] { 12.75, -62.5, RISER, 4 }, - new float[] { 38.75, -78.5, FLOOR, 5 }, - new float[] { 65.75, -83, RISER, 5 }, - +static final TowerConfig[] TOWER_CONFIG = { + new TowerConfig("A", 0, 0, RISER, 4), + new TowerConfig("B", 25, -10, RISER, 4), + new TowerConfig("C", 50, -22.5, FLOOR, 5), + new TowerConfig("D", 17.25, -35.5, FLOOR, 6), + new TowerConfig("E", 43.25, -51.5, RISER, 6), + new TowerConfig("F", 69.25, -56, FLOOR, 6), + new TowerConfig("G", 12.75, -62.5, RISER, 4), + new TowerConfig("H", 38.75, -78.5, FLOOR, 5), + new TowerConfig("I", 65.75, -83, RISER, 5), + new TowerConfig("J", 11.75, -89, RISER, 3), + new TowerConfig("K", -50, -28, FLOOR, 3), + new TowerConfig("L", -14, -28, FLOOR, 4), + new TowerConfig("M", -16, -81, FLOOR, 3), + new TowerConfig("N", -56, 8, FLOOR, 2), + new TowerConfig("O", -9, 27, RISER, 2), + new TowerConfig("P", -28, 0, RISER, 3), + new TowerConfig("Q", -14, 54, FLOOR, 2), + new TowerConfig("R", -30, -54, RISER, 2), }; +/** + * Mappings for the output drivers. Can mix grizzly boards with + * panda boards in here no problem. + */ +IPOutput[] buildOutputs() throws SocketException, UnknownHostException { + return new IPOutput[] { +// new GrizzlyOutput(lx, "192.168.88.100", 0, 0, 0, 39, 38, 40, 0, 37, 35, 0, 21, 20, 22, 0, 33, 32 ), +// new GrizzlyOutput(lx, "192.168.88.104", 0, 13, 12, 0, 1, 2, 6, 5, 7, 0, 0, 4, 3, 9, 10, 11 ), +// new GrizzlyOutput(lx, "192.168.88.105", 42, 41, 0, 43, 45, 44, 0, 0, 0, 0, 0, 0, 0, 24, 23, 25 ), +// new GrizzlyOutput(lx, "192.168.88.107", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), + + new PandaOutput(lx, "10.200.1.29", new String[][] { + new String[] { "00", "00", "00", "00" }, // 1 + new String[] { "00", "00", "00", "00" }, // 2 + new String[] { "E1", "E2", "E3", "E4" }, // 3 + new String[] { "E5", "E6", "D5", "D6" }, // 4 + new String[] { "H2", "H3", "H4", "H5" }, // 5 + new String[] { "G1", "G2", "G3", "G4" }, // 6 + new String[] { "00", "00", "00", "00" }, // 7 + new String[] { "D1", "D2", "D3", "D4" }, // 8 + }), + + new PandaOutput(lx, "10.200.1.30", new String[][] { + new String[] { "00", "00", "00", "00" }, // 1 + new String[] { "J1", "J2", "J3", "00" }, // 2 + new String[] { "B1", "B2", "B3", "B4" }, // 3 + new String[] { "K1", "K2", "K3", "00" }, // 4 + new String[] { "F1", "F2", "F3", "F4" }, // 5 + new String[] { "L1", "L2", "L3", "L4" }, // 6 + new String[] { "C1", "C2", "C3", "C4" }, // 7 + new String[] { "I1", "I2", "I3", "I4" }, // 8 + }), + + new PandaOutput(lx, "10.200.1.31", new String[][] { + new String[] { "00", "00", "00", "00" }, // 1 + new String[] { "00", "00", "00", "00" }, // 2 + new String[] { "A1", "A2", "A3", "A4" }, // 3 + new String[] { "M1", "M2", "M3", "00" }, // 4 + new String[] { "N1", "N2", "O1", "O2" }, // 5 + new String[] { "C5", "F5", "F6", "I5" }, // 6 + new String[] { "P1", "P2", "P3", "00" }, // 7 + new String[] { "Q1", "Q2", "R1", "R2" }, // 8 + }), + + }; +} + +static class TowerConfig { + public final String id; + public final float x; + public final float z; + public final float base; + public final int numCubes; + + public TowerConfig(String id, float z, float x, float base, int numCubes) { + this.id = id; + this.x = x; + this.z = z; + this.base = base; + this.numCubes = numCubes; + } +} + public Model buildModel() { List towers = new ArrayList(); @@ -44,11 +115,11 @@ public Model buildModel() { float rt2 = sqrt(2); float x, y, z, xd, zd, num; - for (float[] tc : TOWER_CONFIG) { - x = -tc[1]; - z = tc[0]; - y = tc[2]; - num = tc[3]; + for (TowerConfig tc : TOWER_CONFIG) { + x = -tc.x; + z = tc.z; + y = tc.base; + num = tc.numCubes; if (z < x) { zd = -(x-z)/rt2; xd = z*rt2 - zd; @@ -58,12 +129,12 @@ public Model buildModel() { } List tower = new ArrayList(); for (int n = 0; n < num; ++n) { - Cube cube = new Cube(xd + 24, y, zd + 84, 0, -45, 0); + Cube cube = new Cube(tc.id + (n+1), xd + 24, y, zd + 84, 0, -45, 0); tower.add(cube); cubes[cubeIndex++] = cube; y += SPACING; } - towers.add(new Tower(tower)); + towers.add(new Tower(tc.id, tower)); } return new Model(towers, cubes); diff --git a/Model.pde b/Model.pde index 3376246..264dff1 100644 --- a/Model.pde +++ b/Model.pde @@ -22,6 +22,8 @@ public static class Model extends LXModel { public final List cubes; public final List faces; public final List strips; + + public final Map cubeTable; private final Cube[] _cubes; @@ -35,8 +37,10 @@ public static class Model extends LXModel { List cubeList = new ArrayList(); List faceList = new ArrayList(); List stripList = new ArrayList(); + Map _cubeTable = new HashMap(); for (Cube cube : _cubes) { if (cube != null) { + _cubeTable.put(cube.id, cube); cubeList.add(cube); for (Face face : cube.faces) { faceList.add(face); @@ -51,6 +55,7 @@ public static class Model extends LXModel { this.cubes = Collections.unmodifiableList(cubeList); this.faces = Collections.unmodifiableList(faceList); this.strips = Collections.unmodifiableList(stripList); + this.cubeTable = Collections.unmodifiableMap(_cubeTable); } private static class Fixture extends LXAbstractFixture { @@ -75,12 +80,22 @@ public static class Model extends LXModel { public Cube getCubeByRawIndex(int index) { return _cubes[index]; } + + public Cube getCubeById(String id) { + return this.cubeTable.get(id); + } } /** * Model of a set of cubes stacked in a tower */ public static class Tower extends LXModel { + + /** + * Tower id + */ + public final String id; + /** * Immutable list of cubes */ @@ -101,9 +116,11 @@ public static class Tower extends LXModel { * * @param cubes Array of cubes */ - public Tower(List cubes) { + public Tower(String id, List cubes) { super(cubes.toArray(new Cube[] {})); + this.id = id; + List cubeList = new ArrayList(); List faceList = new ArrayList(); List stripList = new ArrayList(); @@ -150,6 +167,8 @@ public static class Cube extends LXModel { new Strip.Metrics(EDGE_HEIGHT, POINTS_PER_STRIP) ); + public final String id; + /** * Immutable list of all cube faces */ @@ -190,14 +209,12 @@ public static class Cube extends LXModel { */ 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) { + public Cube(String id, 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); + this.id = id; + while (rx < 0) rx += 360; while (ry < 0) ry += 360; while (rz < 0) rz += 360; diff --git a/Output.pde b/Output.pde new file mode 100644 index 0000000..cd7fd87 --- /dev/null +++ b/Output.pde @@ -0,0 +1,270 @@ +/** + * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND + * + * //\\ //\\ //\\ //\\ + * ///\\\ ///\\\ ///\\\ ///\\\ + * \\\/// \\\/// \\\/// \\\/// + * \\// \\// \\// \\//H + * + * EXPERTS ONLY!! EXPERTS ONLY!! + * + * If you are an artist, you may ignore this file! It contains + * the code to drive grizzly board outputs. + */ + +private final static int[] STRIP_ORDERING = new int[] { 9, 8, 11, 5, 4, 7, 6, 10, 14, 2, 1, 0, 3, 13, 12, 15 }; + +static int[] cubePointIndices(Cube cube) { + int[] pointIndices = new int[Cube.POINTS_PER_CUBE - 2]; + int pi = 0; + for (int stripIndex : STRIP_ORDERING) { + Strip strip = cube.strips.get(stripIndex); + int stripLen = ((stripIndex == 9) || (stripIndex == 15)) ? 15 : 16; + for (int i = stripLen-1; i >= 0; --i) { + pointIndices[pi++] = strip.points.get(i).index; + } + } + return pointIndices; +} + +abstract class IPOutput extends LXDatagramOutput { + public final String ipAddress; + + protected IPOutput(LX lx, String ipAddress) throws SocketException { + super(lx); + this.ipAddress = ipAddress; + } +} + +static abstract class OSCDatagram extends LXDatagram { + + protected OSCDatagram(int bufferSize) { + super(bufferSize); + } + + protected final static int OSC_PORT = 779; + + protected static byte[] oscString(String s) { + int len = s.length(); + int padding = (4 - ((len + 1) % 4)) % 4; + byte[] bytes = new byte[len + 1 + padding]; + System.arraycopy(s.getBytes(), 0, bytes, 0, len); + for (int i = len; i < bytes.length; ++i) { + bytes[i] = 0; + } + return bytes; + } + + protected static int oscIntCopy(int i, byte[] buffer, int pos) { + buffer[pos] = (byte) ((i >> 24) & 0xff); + buffer[pos + 1] = (byte) ((i >> 16) & 0xff); + buffer[pos + 2] = (byte) ((i >> 8) & 0xff); + buffer[pos + 3] = (byte) (i & 0xff); + return 4; + } +} + +/** + * Grizzly Output, sends packets to one grizzly board with a fixed IP and a number + * of channels. + */ +class GrizzlyOutput extends IPOutput { + + private int frameNumber = 0; + + public GrizzlyOutput(LX lx, String ipAddress, int ... cubeIndices) throws UnknownHostException, SocketException { + super(lx, ipAddress); + int channelNum = 0; + for (int rawCubeIndex : cubeIndices) { + if (rawCubeIndex > 0) { + Cube cube = model.getCubeByRawIndex(rawCubeIndex); + addDatagram(new GrizzlyDatagram(this, channelNum, cube).setAddress(ipAddress)); + } + ++channelNum; + } + this.enabled.setValue(false); + } + + protected void beforeSend(int[] colors) { + ++frameNumber; + } + + public int getFrameNumber() { + return this.frameNumber; + } +} + +/** + * Datagram to a Grizzlyboard. A simple fixed OSC packet. + */ +static class GrizzlyDatagram extends OSCDatagram { + + private final static byte[] OSC_ADDRESS = oscString("/shady/pointbuffer"); + private final static byte[] OSC_TYPETAG = oscString(",iiiiibi"); + + private final static int HEADER_LENGTH = OSC_ADDRESS.length + OSC_TYPETAG.length + 24; + private final static int FOOTER_LENGTH = 4; + + private GrizzlyOutput output; + + private int[] pointIndices; + + private final int frameNumberPos; + + public GrizzlyDatagram(GrizzlyOutput output, int channelNum, Cube cube) { + this(output, channelNum, cubePointIndices(cube)); + } + + public GrizzlyDatagram(GrizzlyOutput output, int channelNum, int[] pointIndices) { + super(HEADER_LENGTH + 4*pointIndices.length + FOOTER_LENGTH); + setPort(OSC_PORT); + + this.output = output; + this.pointIndices = pointIndices; + int dataLength = 4*pointIndices.length; + + int pos = 0; + + // OSC address + System.arraycopy(OSC_ADDRESS, 0, this.buffer, pos, OSC_ADDRESS.length); + pos += OSC_ADDRESS.length; + + // OSC typetag + System.arraycopy(OSC_TYPETAG, 0, this.buffer, pos, OSC_TYPETAG.length); + pos += OSC_TYPETAG.length; + this.frameNumberPos = pos; + pos += oscIntCopy(0, this.buffer, pos); // placeholder for frame number + pos += oscIntCopy(0xDEADBEEF, this.buffer, pos); + pos += oscIntCopy(channelNum, this.buffer, pos); + pos += oscIntCopy(0xFEEDBEEF, this.buffer, pos); + pos += oscIntCopy(dataLength, this.buffer, pos); + pos += oscIntCopy(dataLength, this.buffer, pos); + + // end header + oscIntCopy(0xBEFFFFEB, this.buffer, this.buffer.length - 4); + } + + void onSend(int[] colors) { + oscIntCopy(this.output.getFrameNumber(), this.buffer, frameNumberPos); + int dataIndex = HEADER_LENGTH; + for (int index : this.pointIndices) { + color c = (index >= 0) ? colors[index] : 0; + this.buffer[dataIndex] = (byte) 0; // unused, alpha + this.buffer[dataIndex + 1] = (byte) ((c >> 16) & 0xff); // r + this.buffer[dataIndex + 2] = (byte) ((c >> 8) & 0xff); // g + this.buffer[dataIndex + 3] = (byte) (c & 0xff); // b + dataIndex += 4; + } + } +} + +/** + * Grizzly Output, sends packets to one grizzly board with a fixed IP and a number + * of channels. + */ +class PandaOutput extends IPOutput { + + public PandaOutput(LX lx, String ipAddress, String[][] channelMappings) throws UnknownHostException, SocketException { + super(lx, ipAddress); + int packetNum = 0; + int[] packetPointIndices = new int[PandaDatagram.PIXELS_PER_PACKET]; + int[] noCubePoints = new int[Cube.POINTS_PER_CUBE - 2]; // 15-strips + for (int i = 0; i < noCubePoints.length; ++i) { + noCubePoints[i] = -1; + } + int[] cubePointIndices; + int pi = 0; + for (String[] channel : channelMappings) { + for (int i = 0; i < PandaDatagram.CUBES_PER_CHANNEL; ++i) { + Cube cube = model.getCubeById((i < channel.length) ? channel[i] : null); + cubePointIndices = (cube == null) ? noCubePoints : cubePointIndices(cube); + for (int index : cubePointIndices) { + packetPointIndices[pi++] = index; + if (pi >= PandaDatagram.PIXELS_PER_PACKET) { + addDatagram(new PandaDatagram(packetNum, packetPointIndices).setAddress(ipAddress)); + pi = 0; + packetPointIndices = new int[PandaDatagram.PIXELS_PER_PACKET]; + ++packetNum; + } + } + } + // 2 dummy pixels per cube at the end of each channel, due to 15-strips + for (int i = 0; i < PandaDatagram.CUBES_PER_CHANNEL * 2; ++i) { + packetPointIndices[pi++] = -1; + if (pi >= PandaDatagram.PIXELS_PER_PACKET) { + addDatagram(new PandaDatagram(packetNum, packetPointIndices).setAddress(ipAddress)); + pi = 0; + packetPointIndices = new int[PandaDatagram.PIXELS_PER_PACKET]; + ++packetNum; + } + } + } + if (pi > 0) { + addDatagram(new PandaDatagram(packetNum, packetPointIndices).setAddress(ipAddress)); + } + this.enabled.setValue(false); + } +} + +/** + * Datagram to a Panda board. A simple fixed OSC packet. + */ +static class PandaDatagram extends OSCDatagram { + + private final static byte[] OSC_ADDRESS = oscString("/shady/pointbuffer"); + private final static byte[] OSC_TYPETAG = oscString(",iib"); + + private final static int HEADER_LENGTH = OSC_ADDRESS.length + OSC_TYPETAG.length + 12; + + private final static int PORT = 9001; + + public final static int CUBES_PER_CHANNEL = 4; + + public final static int PIXELS_PER_CHANNEL = Cube.POINTS_PER_CUBE * CUBES_PER_CHANNEL; + + public final static int PIXELS_PER_PACKET = 352; + + public final static int BYTES_PER_PIXEL = 4; + + public final static int PACKET_SIZE = BYTES_PER_PIXEL*PIXELS_PER_PACKET; + + private int[] pointIndices; + + public PandaDatagram(int packetNum, int[] pointIndices) { + super(HEADER_LENGTH + PACKET_SIZE); + setPort(PORT); + + this.pointIndices = pointIndices; + + int pos = 0; + + // OSC address + System.arraycopy(OSC_ADDRESS, 0, this.buffer, pos, OSC_ADDRESS.length); + pos += OSC_ADDRESS.length; + + // OSC typetag + System.arraycopy(OSC_TYPETAG, 0, this.buffer, pos, OSC_TYPETAG.length); + pos += OSC_TYPETAG.length; + + // Packet number + pos += oscIntCopy(packetNum, this.buffer, pos); + + // Length of blob + pos += oscIntCopy(PACKET_SIZE, this.buffer, pos); + + // Length of blob in osc-blob + pos += oscIntCopy(PACKET_SIZE, this.buffer, pos); + } + + void onSend(int[] colors) { + int dataIndex = HEADER_LENGTH; + for (int index : this.pointIndices) { + color c = (index >= 0) ? colors[index] : 0; + this.buffer[dataIndex] = (byte) 0; // unused, alpha + this.buffer[dataIndex + 1] = (byte) ((c >> 16) & 0xff); // r + this.buffer[dataIndex + 2] = (byte) ((c >> 8) & 0xff); // g + this.buffer[dataIndex + 3] = (byte) (c & 0xff); // b + dataIndex += 4; + } + } +} diff --git a/SugarCubes.pde b/SugarCubes.pde index f218ccc..c6431d8 100644 --- a/SugarCubes.pde +++ b/SugarCubes.pde @@ -22,7 +22,6 @@ * If you're an artist, create a new tab in the Processing environment with * your name. Implement your classes there, and add them to the list below. */ - LXPattern[] patterns(LX lx) { return new LXPattern[] { diff --git a/TestPatterns.pde b/TestPatterns.pde index 728683e..ed0c5ee 100644 --- a/TestPatterns.pde +++ b/TestPatterns.pde @@ -228,7 +228,6 @@ class MappingTool extends TestPattern { private int cubeIndex = 0; private int stripIndex = 0; - private int channelIndex = 0; public final int MAPPING_MODE_ALL = 0; public final int MAPPING_MODE_CHANNEL = 1; @@ -243,22 +242,9 @@ class MappingTool extends TestPattern { public boolean channelModeRed = true; public boolean channelModeGreen = false; public boolean channelModeBlue = false; - - private final int numChannels; - + MappingTool(LX lx) { super(lx); - // TODO(mcslee): port channels to grizzly - numChannels = 1; - setChannel(); - } - - public int numChannels() { - return numChannels; - } - - private void setChannel() { - // TODO(mcslee): port to grizzly } private int indexOfCubeInChannel(Cube c) { @@ -357,23 +343,6 @@ class MappingTool extends TestPattern { } } - public void setChannel(int index) { - if (numChannels > 0) { - channelIndex = index % numChannels; - } - setChannel(); - } - - public void incChannel() { - channelIndex = (channelIndex + 1) % numChannels; - setChannel(); - } - - public void decChannel() { - channelIndex = (channelIndex + numChannels - 1) % numChannels; - setChannel(); - } - public void setStrip(int index) { stripIndex = index % Cube.STRIPS_PER_CUBE; } @@ -388,8 +357,8 @@ class MappingTool extends TestPattern { public void keyPressed(UIMapping uiMapping) { switch (keyCode) { - case UP: if (mappingMode == MAPPING_MODE_CHANNEL) incChannel(); else incCube(); break; - case DOWN: if (mappingMode == MAPPING_MODE_CHANNEL) decChannel(); else decCube(); break; + case UP: incCube(); break; + case DOWN: decCube(); break; case LEFT: decStrip(); break; case RIGHT: incStrip(); break; } @@ -398,7 +367,6 @@ class MappingTool extends TestPattern { case 'g': channelModeGreen = !channelModeGreen; break; case 'b': channelModeBlue = !channelModeBlue; break; } - uiMapping.setChannelID(channelIndex+1); uiMapping.setCubeID(cubeIndex+1); uiMapping.setStripID(stripIndex+1); uiMapping.redraw(); diff --git a/UIImplementation.pde b/UIImplementation.pde index e0b5787..0e46e57 100644 --- a/UIImplementation.pde +++ b/UIImplementation.pde @@ -115,8 +115,10 @@ FloatBuffer vertData; popMatrix(); noStroke(); - for (Cube c : model.cubes) { - drawCube(c); + if (structureOn) { + for (Cube c : model.cubes) { + drawCube(c); + } } noFill(); @@ -183,7 +185,7 @@ FloatBuffer allocateDirectFloatBuffer(int n) { float in = .15; noStroke(); fill(#393939); - // drawBox(c.x+in, c.y+in, c.z+in, c.rx, c.ry, c.rz, Cube.EDGE_WIDTH-in*2, Cube.EDGE_HEIGHT-in*2, Cube.EDGE_WIDTH-in*2, Cube.CHANNEL_WIDTH-in); + drawBox(c.x+in, c.y+in, c.z+in, c.rx, c.ry, c.rz, Cube.EDGE_WIDTH-in*2, Cube.EDGE_HEIGHT-in*2, Cube.EDGE_WIDTH-in*2, Cube.CHANNEL_WIDTH-in); } void drawBox(float x, float y, float z, float rx, float ry, float rz, float xd, float yd, float zd, float sw) { @@ -374,28 +376,28 @@ class UIEffects extends UIWindow { } class UIOutput extends UIWindow { - public UIOutput(GrizzlyOutput[] grizzlies, float x, float y, float w, float h) { + public UIOutput(IPOutput[] outputs, float x, float y, float w, float h) { super(lx.ui, "OUTPUT", x, y, w, h); float yp = UIWindow.TITLE_LABEL_HEIGHT; - final UIItemList outputs = new UIItemList(1, UIWindow.TITLE_LABEL_HEIGHT, w-2, 80); + final UIItemList itemList = new UIItemList(1, UIWindow.TITLE_LABEL_HEIGHT, w-2, 80); List items = new ArrayList(); - for (GrizzlyOutput grizzly : grizzlies) { - items.add(new GrizzlyScrollItem(grizzly)); - grizzly.enabled.addListener(new LXParameterListener() { + for (IPOutput output : outputs) { + items.add(new OutputItem(output)); + output.enabled.addListener(new LXParameterListener() { public void onParameterChanged(LXParameter parameter) { - outputs.redraw(); + itemList.redraw(); } }); } - outputs.setItems(items).addToContainer(this); + itemList.setItems(items).addToContainer(this); } - class GrizzlyScrollItem extends UIItemList.AbstractItem { - final GrizzlyOutput output; + class OutputItem extends UIItemList.AbstractItem { + final IPOutput output; - GrizzlyScrollItem(GrizzlyOutput output) { + OutputItem(IPOutput output) { this.output = output; } @@ -461,7 +463,7 @@ class UIMapping extends UIWindow { private final MappingTool mappingTool; - private final UIIntegerBox channelBox; + private final UILabel cubeLabel; private final UIIntegerBox cubeBox; private final UIIntegerBox stripBox; @@ -476,18 +478,19 @@ class UIMapping extends UIWindow { else if (value == MAP_MODE_CHANNEL) mappingTool.mappingMode = mappingTool.MAPPING_MODE_CHANNEL; else if (value == MAP_MODE_CUBE) mappingTool.mappingMode = mappingTool.MAPPING_MODE_SINGLE_CUBE; } - }.setOptions(new String[] { MAP_MODE_ALL, MAP_MODE_CHANNEL, MAP_MODE_CUBE }).addToContainer(this); + }.setOptions(new String[] { MAP_MODE_ALL, MAP_MODE_CUBE }).addToContainer(this); yp += 24; - new UILabel(4, yp+8, w-10, 20).setLabel("CHANNEL ID").addToContainer(this); + new UILabel(4, yp+8, w-10, 20).setLabel("CUBE ID").addToContainer(this); yp += 24; - (channelBox = new UIIntegerBox(4, yp, w-10, 20) { - protected void onValueChange(int value) { - mappingTool.setChannel(value-1); - } - }).setRange(1, mappingTool.numChannels()).addToContainer(this); + (cubeLabel = new UILabel(4, yp, w-10, 20)) + .setAlignment(CENTER, CENTER) + .setLabel(model.cubes.get(0).id) + .setBackgroundColor(#222222) + .setBorderColor(#666666) + .addToContainer(this); yp += 24; - new UILabel(4, yp+8, w-10, 20).setLabel("CUBE ID").addToContainer(this); + new UILabel(4, yp+8, w-10, 20).setLabel("CUBE NUMBER").addToContainer(this); yp += 24; (cubeBox = new UIIntegerBox(4, yp, w-10, 20) { protected void onValueChange(int value) { @@ -528,12 +531,9 @@ class UIMapping extends UIWindow { } - public void setChannelID(int value) { - channelBox.setValue(value); - } - public void setCubeID(int value) { cubeBox.setValue(value); + cubeLabel.setLabel(model.getCubeByRawIndex(value).id); } public void setStripID(int value) { -- 2.34.1