Pandaboard support for new 15-cubes in tandem with Grizzly
authorMark Slee <mcslee@gmail.com>
Thu, 27 Mar 2014 00:16:44 +0000 (17:16 -0700)
committerMark Slee <mcslee@gmail.com>
Thu, 27 Mar 2014 00:16:44 +0000 (17:16 -0700)
Grizzly.pde [deleted file]
Internals.pde
Mappings.pde
Model.pde
Output.pde [new file with mode: 0644]
SugarCubes.pde
TestPatterns.pde
UIImplementation.pde

diff --git a/Grizzly.pde b/Grizzly.pde
deleted file mode 100644 (file)
index 5741778..0000000
+++ /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;
-    }
-  }
-}
index 31f8ffcb4b90c27c750f3c59ff5efed0350b00f2..3c1c603f18ba7d3c4d33e67be8eb471a06714776 100644 (file)
@@ -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;
   }
 }
 
index 8798fb59fea91797853017ed978d5ad191538e71..30d06898c49a5317462ebf11534eb3d0595f1586 100644 (file)
@@ -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<Tower> towers = new ArrayList<Tower>();
@@ -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<Cube> tower = new ArrayList<Cube>();
     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);
index 33762464c3c4ed4eaa50cfa225fea6befac50021..264dff15ab10619e0c24e92e73afd8f04e0e856b 100644 (file)
--- a/Model.pde
+++ b/Model.pde
@@ -22,6 +22,8 @@ public static class Model extends LXModel {
   public final List<Cube> cubes;
   public final List<Face> faces;
   public final List<Strip> strips;
+  
+  public final Map<String, Cube> cubeTable;
 
   private final Cube[] _cubes;
 
@@ -35,8 +37,10 @@ public static class Model extends LXModel {
     List<Cube> cubeList = new ArrayList<Cube>();
     List<Face> faceList = new ArrayList<Face>();
     List<Strip> stripList = new ArrayList<Strip>();
+    Map<String, Cube> _cubeTable = new HashMap<String, Cube>();
     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<Cube> cubes) {
+  public Tower(String id, List<Cube> cubes) {
     super(cubes.toArray(new Cube[] {}));
 
+    this.id = id;
+
     List<Cube> cubeList = new ArrayList<Cube>();
     List<Face> faceList = new ArrayList<Face>();
     List<Strip> stripList = new ArrayList<Strip>();
@@ -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 (file)
index 0000000..cd7fd87
--- /dev/null
@@ -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;
+    }
+  }
+}
index f218ccc3d3979e9f2e0064d330c8092f7e0d62a9..c6431d8fe85ee2f4e871b38776e32ca9527b4429 100644 (file)
@@ -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[] {        
 
index 728683ec45dea70eec6ee5e03f4f4f3635449514..ed0c5ee601e231051dd796e8850a64ffdc32f32d 100644 (file)
@@ -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();
index e0b57876ac4fd182fefa6df6b9ec874de6c039bd..0e46e576894b00c7fae89f64848431d6f83a130d 100644 (file)
@@ -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<UIItemList.Item> items = new ArrayList<UIItemList.Item>();
-    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) {