From 3ffd6241fa4f38d82cdb4dca434ca04b1edc9ccd Mon Sep 17 00:00:00 2001 From: Micah Elizabeth Scott Date: Fri, 7 Mar 2014 21:09:12 -0800 Subject: [PATCH] First cut at an Open Pixel Control server --- .gitignore | 2 +- OpenPixelControl.pde | 145 +++++++++++++++++++++++++++++++++++++++++++ SugarCubes.pde | 3 + 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 OpenPixelControl.pde diff --git a/.gitignore b/.gitignore index 10dda95..9c861b3 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .DS_Store data/presets.txt - +openpixelcontrol-layout.json diff --git a/OpenPixelControl.pde b/OpenPixelControl.pde new file mode 100644 index 0000000..118eb2a --- /dev/null +++ b/OpenPixelControl.pde @@ -0,0 +1,145 @@ +import processing.net.*; + +// Server for Open Pixel Control patterns (http://openpixelcontrol.org/) +class OpenPixelControl extends SCPattern { + + int port = 7890; + + Server server; + byte[] buffer; + int bufferedByteCount; + + public OpenPixelControl(LX lx, PApplet parent) { + super(lx); + server = new Server(parent, port); + println("Listening for Open Pixel Control data on port " + port); + + // Buffer space for two frames, worst case + buffer = new byte[0x10004 * 2]; + bufferedByteCount = 0; + + // Save a JSON layout file that some Open Pixel Control clients can use + writeMappingFile("openpixelcontrol-layout.json"); + } + + public void run(double deltaMs) { + readFromClient(); + } + + void readFromClient() { + Client client = server.available(); + + if (client == null) { + // No client; flush any stored partial frames + bufferedByteCount = 0; + return; + } + + while (true) { + int available = client.available(); + if (available <= 0) { + return; + } + + if (bufferedByteCount == 0) { + // Read directly to buffer + bufferedByteCount = client.readBytes(buffer); + } else { + // Append to an earlier partial frame + byte[] additional = client.readBytes(); + arrayCopy(additional, 0, buffer, bufferedByteCount, additional.length); + bufferedByteCount += additional.length; + } + + // Extract OPC packets from buffer + int offset = 0; + while (bufferedByteCount - offset >= 4) { + int channel = buffer[offset + 0] & 0xFF; + int command = buffer[offset + 1] & 0xFF; + int length = ((buffer[offset + 2] & 0xFF) << 8) | (buffer[offset + 3] & 0xFF); + + if (bufferedByteCount - offset < length + 4) { + // Not enough data for a full packet yet + break; + } + + // Handle the packet in-place + offset += 4; + opcPacket(channel, command, offset, length); + offset += length; + } + + // If we didn't use the whole buffer, save remainder for later + bufferedByteCount -= offset; + arrayCopy(buffer, offset, buffer, 0, bufferedByteCount); + } + } + + void opcPacket(int channel, int command, int offset, int length) { + // Only look at "Set Pixel Colors" to channel 0. + if (channel == 0 && command == 0) { + + // Unpack colors directly into framebuffer + for (int i = 0; i < length / 3; i++) { + if (i >= colors.length) { + break; + } + + colors[i] = + ((buffer[offset + 0] & 0xFF) << 16) | + ((buffer[offset + 1] & 0xFF) << 8) | + (buffer[offset + 2] & 0xFF) ; + + offset += 3; + } + } + } + + void writeMappingFile(String filename) { + PrintWriter output; + output = createWriter(filename); + + // Rearrange points by color buffer index + LXPoint[] orderedPoints; + int maxIndex = 0; + for (LXPoint p : model.points) { + maxIndex = max(maxIndex, p.index); + } + orderedPoints = new LXPoint[maxIndex + 1]; + for (LXPoint p : model.points) { + orderedPoints[p.index] = p; + } + + float xCenter = (model.xMax + model.xMin) / 2.0; + float yCenter = (model.yMax + model.yMin) / 2.0; + float zCenter = (model.zMax + model.zMin) / 2.0; + float xSize = model.xMax - model.xMin; + float zSize = model.zMax - model.zMin; + float maxSize = max(xSize, zSize); + float scale = 4.0 / maxSize; + + output.print("["); + for (int i = 0; i < orderedPoints.length; i++) { + boolean isLast = i == orderedPoints.length - 1; + String comma = isLast ? "" : ","; + LXPoint p = orderedPoints[i]; + + if (p == null) { + // Unused index + output.print("null" + comma); + } else { + // Transform coordinates to make sense in the OPC visualizer + float x = (p.x - xCenter) * scale; + float y = (p.z - zCenter) * scale; + float z = (p.y - yCenter) * scale; + + output.print("{\"point\":[" + x + "," + y + "," + z + "]}" + comma); + } + } + output.print("]"); + + output.flush(); + output.close(); + println("Saved OpenPixelControl model to " + filename); + } +}; diff --git a/SugarCubes.pde b/SugarCubes.pde index 9d72c5d..f218ccc 100644 --- a/SugarCubes.pde +++ b/SugarCubes.pde @@ -119,6 +119,9 @@ LXPattern[] patterns(LX lx) { // Micah new Rings(lx), + // Open pixel control server + new OpenPixelControl(lx, this), + // Basic test patterns for reference, not art new TestCubePattern(lx), new TestTowerPattern(lx), -- 2.34.1