New LX fixes FlashEffect bug
[SugarCubes.git] / OpenPixelControl.pde
1 import processing.net.*;
2
3 // Server for Open Pixel Control patterns (http://openpixelcontrol.org/)
4 class OpenPixelControl extends SCPattern {
5
6 int port = 7890;
7
8 Server server;
9 byte[] buffer;
10 int bufferedByteCount;
11
12 public OpenPixelControl(LX lx, PApplet parent) {
13 super(lx);
14 server = new Server(parent, port);
15 println("Listening for Open Pixel Control data on port " + port);
16
17 // Buffer space for two frames, worst case
18 buffer = new byte[0x10004 * 2];
19 bufferedByteCount = 0;
20
21 // Save a JSON layout file that some Open Pixel Control clients can use
22 writeMappingFile("openpixelcontrol-layout.json");
23 }
24
25 public void run(double deltaMs) {
26 readFromClient();
27 }
28
29 void readFromClient() {
30 Client client = server.available();
31
32 if (client == null) {
33 // No client; flush any stored partial frames
34 bufferedByteCount = 0;
35 return;
36 }
37
38 while (true) {
39 int available = client.available();
40 if (available <= 0) {
41 return;
42 }
43
44 if (bufferedByteCount == 0) {
45 // Read directly to buffer
46 bufferedByteCount = client.readBytes(buffer);
47 } else {
48 // Append to an earlier partial frame
49 byte[] additional = new byte[buffer.length - bufferedByteCount];
50 int additionalLength = client.readBytes(additional);
51 arrayCopy(additional, 0, buffer, bufferedByteCount, additionalLength);
52 bufferedByteCount += additionalLength;
53 }
54
55 // Extract OPC packets from buffer
56 int offset = 0;
57 while (bufferedByteCount - offset >= 4) {
58 int channel = buffer[offset + 0] & 0xFF;
59 int command = buffer[offset + 1] & 0xFF;
60 int length = ((buffer[offset + 2] & 0xFF) << 8) | (buffer[offset + 3] & 0xFF);
61
62 if (bufferedByteCount - offset < length + 4) {
63 // Not enough data for a full packet yet
64 break;
65 }
66
67 // Handle the packet in-place
68 offset += 4;
69 opcPacket(channel, command, offset, length);
70 offset += length;
71 }
72
73 // If we didn't use the whole buffer, save remainder for later
74 bufferedByteCount -= offset;
75 arrayCopy(buffer, offset, buffer, 0, bufferedByteCount);
76 }
77 }
78
79 void opcPacket(int channel, int command, int offset, int length) {
80 // Only look at "Set Pixel Colors" to channel 0.
81 if (channel == 0 && command == 0) {
82
83 // Unpack colors directly into framebuffer
84 for (int i = 0; i < length / 3; i++) {
85 if (i >= colors.length) {
86 break;
87 }
88
89 colors[i] =
90 ((buffer[offset + 0] & 0xFF) << 16) |
91 ((buffer[offset + 1] & 0xFF) << 8) |
92 (buffer[offset + 2] & 0xFF) ;
93
94 offset += 3;
95 }
96 }
97 }
98
99 void writeMappingFile(String filename) {
100 PrintWriter output;
101 output = createWriter(filename);
102
103 // Rearrange points by color buffer index
104 LXPoint[] orderedPoints;
105 int maxIndex = 0;
106 for (LXPoint p : model.points) {
107 maxIndex = max(maxIndex, p.index);
108 }
109 orderedPoints = new LXPoint[maxIndex + 1];
110 for (LXPoint p : model.points) {
111 orderedPoints[p.index] = p;
112 }
113
114 float xCenter = (model.xMax + model.xMin) / 2.0;
115 float yCenter = (model.yMax + model.yMin) / 2.0;
116 float zCenter = (model.zMax + model.zMin) / 2.0;
117 float xSize = model.xMax - model.xMin;
118 float zSize = model.zMax - model.zMin;
119 float maxSize = max(xSize, zSize);
120 float scale = 4.0 / maxSize;
121
122 output.print("[");
123 for (int i = 0; i < orderedPoints.length; i++) {
124 boolean isLast = i == orderedPoints.length - 1;
125 String comma = isLast ? "" : ",";
126 LXPoint p = orderedPoints[i];
127
128 if (p == null) {
129 // Unused index
130 output.print("null" + comma);
131 } else {
132 // Transform coordinates to make sense in the OPC visualizer
133 float x = (p.x - xCenter) * scale;
134 float y = (p.z - zCenter) * scale;
135 float z = (p.y - yCenter) * scale;
136
137 output.print("{\"point\":[" + x + "," + y + "," + z + "]}" + comma);
138 }
139 }
140 output.print("]");
141
142 output.flush();
143 output.close();
144 println("Saved OpenPixelControl model to " + filename);
145 }
146 };