Overhaul UI code to make it more flexible and less redrawing, little hierarchy framework
[SugarCubes.git] / _DebugUI.pde
diff --git a/_DebugUI.pde b/_DebugUI.pde
new file mode 100644 (file)
index 0000000..5b99d43
--- /dev/null
@@ -0,0 +1,283 @@
+/**
+ *     DOUBLE BLACK DIAMOND        DOUBLE BLACK DIAMOND
+ *
+ *         //\\   //\\                 //\\   //\\  
+ *        ///\\\ ///\\\               ///\\\ ///\\\
+ *        \\\/// \\\///               \\\/// \\\///
+ *         \\//   \\//                 \\//   \\//
+ *
+ *        EXPERTS ONLY!!              EXPERTS ONLY!!
+ *
+ * Overlay UI that indicates pattern control, etc. This will be moved
+ * into the Processing library once it is stabilized and need not be
+ * regularly modified.
+ */
+
+class DebugUI {
+  
+  final ChannelMapping[] channelList;
+  final int debugX = 5;
+  final int debugY = 5;
+  final int debugXSpacing = 28;
+  final int debugYSpacing = 21;
+  final int[][] debugState;
+  final int[] indexState;
+    
+  final int CUBE_STATE_UNUSED = 0;
+  final int CUBE_STATE_USED = 1;
+  final int CUBE_STATE_DUPLICATED = 2;
+  
+  final int DEBUG_STATE_ANIM = 0;
+  final int DEBUG_STATE_WHITE = 1;
+  final int DEBUG_STATE_OFF = 2;
+  final int DEBUG_STATE_UNUSED = 3;  
+  
+  DebugUI(PandaMapping[] pandaMappings) {
+    int totalChannels = pandaMappings.length * PandaMapping.CHANNELS_PER_BOARD;
+    debugState = new int[totalChannels+1][ChannelMapping.CUBES_PER_CHANNEL+1];
+    indexState = new int[glucose.model.cubes.size()+1];
+    
+    channelList = new ChannelMapping[totalChannels];
+    int channelIndex = 0;
+    for (PandaMapping pm : pandaMappings) {
+      for (ChannelMapping channel : pm.channelList) {
+        channelList[channelIndex++] = channel;
+      }
+    }
+    for (int i = 0; i < debugState.length; ++i) {
+      for (int j = 0; j < debugState[i].length; ++j) {
+        debugState[i][j] = DEBUG_STATE_ANIM;
+      }
+    }
+    
+    for (int rawIndex = 0; rawIndex < glucose.model.cubes.size()+1; ++rawIndex) {
+      indexState[rawIndex] = CUBE_STATE_UNUSED;
+    }
+    for (ChannelMapping channel : channelList) {
+      for (int rawCubeIndex : channel.objectIndices) {
+        if (rawCubeIndex > 0)
+          ++indexState[rawCubeIndex];
+      }
+    }
+  }
+  
+  void draw() {
+    noStroke();
+    int xBase = debugX;
+    int yPos = debugY;
+    
+    textSize(10);
+    
+    fill(#000000);
+    rect(0, 0, debugX + 5*debugXSpacing, height);
+    
+    int channelNum = 0;
+    for (ChannelMapping channel : channelList) {
+      int xPos = xBase;
+      drawNumBox(xPos, yPos, channelNum+1, debugState[channelNum][0]);
+      xPos += debugXSpacing;
+      
+      switch (channel.mode) {
+        case ChannelMapping.MODE_CUBES:
+          int stateIndex = 0;
+          boolean first = true;
+          for (int rawCubeIndex : channel.objectIndices) {
+            if (rawCubeIndex < 0) {
+              break;
+            }
+            if (first) {
+              first = false;
+            } else {
+              stroke(#999999);          
+              line(xPos - 12, yPos + 8, xPos, yPos + 8);
+            }
+            drawNumBox(xPos, yPos, rawCubeIndex, debugState[channelNum][stateIndex+1], indexState[rawCubeIndex]);
+            ++stateIndex;
+            xPos += debugXSpacing;            
+          }
+          break;
+        case ChannelMapping.MODE_BASS:
+          drawNumBox(xPos, yPos, "B", debugState[channelNum][1]);
+          break;
+        case ChannelMapping.MODE_SPEAKER:
+          drawNumBox(xPos, yPos, "S" + channel.objectIndices[0], debugState[channelNum][1]);
+          break;
+        case ChannelMapping.MODE_STRUTS_AND_FLOOR:
+          drawNumBox(xPos, yPos, "F", debugState[channelNum][1]);
+          break;
+        case ChannelMapping.MODE_NULL:
+          break;
+        default:
+          throw new RuntimeException("Unhandled channel mapping mode: " + channel.mode);
+      }          
+      
+      yPos += debugYSpacing;
+      ++channelNum;
+    }
+    drawNumBox(xBase, yPos, "A", debugState[channelNum][0]);
+    yPos += debugYSpacing * 2;
+   
+    noFill();
+    fill(#CCCCCC);
+    text("Unused Cubes",  xBase, yPos + 12);
+    yPos += debugYSpacing;
+    
+    int xIndex = 0;
+    for (int rawIndex = 1; rawIndex <= glucose.model.cubes.size(); ++rawIndex) {
+      if (indexState[rawIndex] == CUBE_STATE_UNUSED) {
+        drawNumBox(xBase + (xIndex * debugXSpacing), yPos, rawIndex, DEBUG_STATE_UNUSED);
+        ++xIndex;
+        if (xIndex > 4) {
+          xIndex = 0;
+          yPos += debugYSpacing + 2;
+        }
+      }
+    }
+  }
+
+  
+  void drawNumBox(int xPos, int yPos, int label, int state) {
+    drawNumBox(xPos, yPos, "" + label, state);
+  }
+  
+  void drawNumBox(int xPos, int yPos, String label, int state) {
+    drawNumBox(xPos, yPos, "" + label, state, CUBE_STATE_USED);
+  }
+
+  void drawNumBox(int xPos, int yPos, int label, int state, int cubeState) {
+    drawNumBox(xPos, yPos, "" + label, state, cubeState);
+  }
+  
+  void drawNumBox(int xPos, int yPos, String label, int state, int cubeState) {
+    noFill();
+    color textColor = #cccccc;
+    switch (state) {
+      case DEBUG_STATE_ANIM:
+        noStroke();
+        fill(#880000);
+        rect(xPos, yPos, 16, 8);
+        fill(#000088);
+        rect(xPos, yPos+8, 16, 8);
+        noFill();
+        stroke(textColor);
+        break;
+      case DEBUG_STATE_WHITE:
+        stroke(textColor);
+        fill(#e9e9e9);
+        textColor = #333333;
+        break;
+      case DEBUG_STATE_OFF:
+        stroke(textColor);
+        break;
+      case DEBUG_STATE_UNUSED:
+        stroke(textColor);
+        fill(#880000);
+        break;
+    }
+    
+    if (cubeState >= CUBE_STATE_DUPLICATED) {
+      stroke(textColor = #FF0000);
+    }
+
+    rect(xPos, yPos, 16, 16);     
+    noStroke();
+    fill(textColor);
+    text(label, xPos + 2, yPos + 12);
+  }
+  
+  void maskColors(color[] colors) {
+    color white = #FFFFFF;
+    color off = #000000;
+    int channelIndex = 0;
+    int state;
+    for (ChannelMapping channel : channelList) {
+      switch (channel.mode) {
+        case ChannelMapping.MODE_CUBES:
+          int cubeIndex = 1;
+          for (int rawCubeIndex : channel.objectIndices) {
+            if (rawCubeIndex >= 0) {
+              state = debugState[channelIndex][cubeIndex];
+              if (state != DEBUG_STATE_ANIM) {
+                color debugColor = (state == DEBUG_STATE_WHITE) ? white : off;
+                Cube cube = glucose.model.getCubeByRawIndex(rawCubeIndex);
+                for (Point p : cube.points) {
+                  colors[p.index] = debugColor;
+                }
+              }
+            }
+            ++cubeIndex;
+          }
+          break;
+            
+         case ChannelMapping.MODE_BASS:
+           state = debugState[channelIndex][1];
+           if (state != DEBUG_STATE_ANIM) {
+              color debugColor = (state == DEBUG_STATE_WHITE) ? white : off;
+              for (Strip s : glucose.model.bassBox.boxStrips) {
+                for (Point p : s.points) {
+                  colors[p.index] = debugColor;
+                }
+              }
+           }
+           break;
+
+         case ChannelMapping.MODE_STRUTS_AND_FLOOR:
+           state = debugState[channelIndex][1];
+           if (state != DEBUG_STATE_ANIM) {
+              color debugColor = (state == DEBUG_STATE_WHITE) ? white : off;
+              for (Point p : glucose.model.boothFloor.points) {
+                colors[p.index] = debugColor;
+              }
+              for (Strip s : glucose.model.bassBox.struts) {
+                for (Point p : s.points) {
+                  colors[p.index] = debugColor;
+                }
+              }
+           }
+           break;
+           
+         case ChannelMapping.MODE_SPEAKER:
+           state = debugState[channelIndex][1];
+           if (state != DEBUG_STATE_ANIM) {
+              color debugColor = (state == DEBUG_STATE_WHITE) ? white : off;
+              for (Point p : glucose.model.speakers.get(channel.objectIndices[0]).points) {
+                colors[p.index] = debugColor;
+              }
+           }
+           break;
+           
+         case ChannelMapping.MODE_NULL:
+           break;
+           
+        default:
+          throw new RuntimeException("Unhandled channel mapping mode: " + channel.mode);           
+      }
+      ++channelIndex;
+    }
+  }
+  
+  boolean mousePressed() {
+    int dx = (mouseX - debugX) / debugXSpacing;
+    int dy = (mouseY - debugY) / debugYSpacing;
+    if ((dy < 0) || (dy >= debugState.length)) {
+      return false;
+    }
+    if ((dx < 0) || (dx >= debugState[dy].length)) {
+      return false;
+    }
+    int newState = debugState[dy][dx] = (debugState[dy][dx] + 1) % 3;
+    if (dy == debugState.length-1) {
+      for (int[] states : debugState) {
+        for (int i = 0; i < states.length; ++i) {
+          states[i] = newState;
+        }
+      }
+    } else if (dx == 0) {
+      for (int i = 0; i < debugState[dy].length; ++i) {
+        debugState[dy][i] = newState;
+      }
+    }
+    return true;
+  }    
+}
+