added red label for duplicate cubes in mapping + list of unused cubes
[SugarCubes.git] / _Overlay.pde
index 2ff364dc8060c9c20dc9b2626615b7614ba1fb59..2623e8276fb668135acc5a4f039fc0a8cb92e35f 100644 (file)
@@ -33,6 +33,7 @@ abstract class OverlayUI {
   protected final int scrollWidth = 14;
   protected final color lightBlue = #666699;
   protected final color lightGreen = #669966;
+  protected final int toggleButtonSize = 10;
   
   private PImage logo;
 
@@ -40,6 +41,13 @@ abstract class OverlayUI {
   protected final int STATE_ACTIVE = 1;
   protected final int STATE_PENDING = 2;
   
+  protected int[] pandaLeft = new int[pandaBoards.length];
+  protected final int pandaWidth = 64;
+  protected final int pandaHeight = 13;
+  protected final int pandaTop = height-16;
+  
+  protected int eligibleLeft;
+  
   protected OverlayUI() {
     leftPos = width - w;
     leftTextPos = leftPos + 4;
@@ -67,21 +75,43 @@ abstract class OverlayUI {
     text("Tap 'u' to restore UI", width-4, height-6);
   }
 
+  public void drawDanText() {
+    textFont(itemFont);
+    textAlign(LEFT);
+    fill(#FFFFFF);
+    text(DanTextLine1, 4, height-50);
+    text(DanTextLine2, 4, height-30);
+  }
+
   public void drawFPS() {
     textFont(titleFont);
     textAlign(LEFT);
     fill(#666666);
-    text("FPS: " + (((int)(frameRate * 10)) / 10.), 4, height-6);
-    text("Target (-/+):", 50, height-6);
-    fill(#000000);
-    rect(104, height-16, 20, 12);
-    fill(#666666);
-    text("" + targetFramerate, 108, height-6);
-    text("PandaOutput (p):", 134, height-6);
+    int lPos = 4;
+    String fps = "FPS: " + (((int)(frameRate * 10)) / 10.);
+    text(fps, lPos, height-6);
+    lPos += 48;
+    
+    String target = "Target (-/+):";
+    text(target, lPos, height-6);
     fill(#000000);
-    rect(214, height-16, 26, 12);
+    lPos += textWidth(target) + 4;
+    rect(lPos, height-16, 24, 13);
     fill(#666666);
-    text(pandaBoardsEnabled ? "ON" : "OFF", 218, height-6);
+    text("" + targetFramerate, lPos + (24 - textWidth("" + targetFramerate))/2, height-6);
+    lPos += 32;
+    String pandaOutput = "PandaOutput (p):";
+    text(pandaOutput, lPos, height-6);
+    lPos += textWidth(pandaOutput)+4;
+    int pi = 0;
+    for (PandaDriver p : pandaBoards) {
+      pandaLeft[pi++] = lPos;
+      fill(p.enabled ? #666666 : #000000);
+      rect(lPos, pandaTop, pandaWidth, pandaHeight);
+      fill(p.enabled ? #000000 : #666666);
+      text(p.ip, lPos + (pandaWidth - textWidth(p.ip)) / 2, height-6);
+      lPos += pandaWidth + 8;
+    }
 
   }
 
@@ -99,6 +129,17 @@ abstract class OverlayUI {
     return drawObjectList(yPos, title, items, names, stateMethod, sz, 0);
   }
   
+  protected void drawToggleButton(float x, float y, boolean eligible, color textColor) {
+    noFill();
+    stroke(textColor);
+    rect(x, y, toggleButtonSize, toggleButtonSize);
+    if (eligible) {
+      noStroke();
+      fill(textColor);
+      rect(x + 2, y + 2, toggleButtonSize - 4, toggleButtonSize - 4);
+    }
+  }    
+  
   protected int drawObjectList(int yPos, String title, Object[] items, String[] names, Method stateMethod, int scrollLength, int scrollPos) {
     noStroke();
     fill(titleColor);
@@ -106,6 +147,7 @@ abstract class OverlayUI {
     textAlign(LEFT);
     text(title, leftTextPos, yPos += lineHeight);    
     if (items != null) {
+      boolean hasScroll = (scrollPos > 0) || (scrollLength < items.length);
       textFont(itemFont);
       color textColor;      
       boolean even = true;
@@ -132,12 +174,18 @@ abstract class OverlayUI {
             fill(even ? #666666 : #777777);
             break;
         }
+        noStroke();
         rect(leftPos, yPos+6, w, lineHeight);
         fill(textColor);
         text(names[i], leftTextPos, yPos += lineHeight);
+        if (lx.isAutoTransitionEnabled() && items[i] instanceof LXPattern) {
+          boolean eligible = ((LXPattern)items[i]).isEligible();
+          eligibleLeft = leftPos + w - (hasScroll ? scrollWidth : 0) - 15;
+          drawToggleButton(eligibleLeft, yPos-8, eligible, textColor);
+        }
         even = !even;       
       }
-      if ((scrollPos > 0) || (scrollLength < items.length)) {
+      if (hasScroll) {
         int yHere = yPos+6;
         noStroke();
         fill(color(0, 0, 0, 50));
@@ -203,6 +251,9 @@ class ControlUI extends OverlayUI {
   private int firstEffectY;
   private int firstEffectKnobY;
   
+  private int autoRotateX;
+  private int autoRotateY;
+  
   private final int PATTERN_LIST_LENGTH = 8;
   private int patternScrollPos = 0;
 
@@ -229,6 +280,11 @@ class ControlUI extends OverlayUI {
   public void draw() {    
     drawLogoAndBackground();
     int yPos = 0;
+    autoRotateX = leftPos + w - 29;
+    autoRotateY = yPos + 12;
+    drawToggleButton(autoRotateX, autoRotateY, lx.isAutoTransitionEnabled(), #999999);
+    fill(lx.isAutoTransitionEnabled() ? #222222: #999999);
+    text("A", autoRotateX + 2, autoRotateY + 9);
     firstPatternY = yPos + lineHeight + 6;
     yPos = drawObjectList(yPos, "PATTERN", patterns, patternNames, patternStateMethod, PATTERN_LIST_LENGTH, patternScrollPos);
     yPos += controlSpacing;
@@ -370,6 +426,35 @@ class ControlUI extends OverlayUI {
     patternKnobIndex = transitionKnobIndex = effectKnobIndex = -1;
     releaseEffect = -1;
     patternScrolling = false;
+    
+    for (int p = 0; p < pandaLeft.length; ++p) {
+      int xp = pandaLeft[p];
+      if ((mouseX >= xp) &&
+          (mouseX < xp + pandaWidth) &&
+          (mouseY >= pandaTop) &&
+          (mouseY < pandaTop + pandaHeight)) {
+          pandaBoards[p].toggle();
+      }
+    }
+    
+    if (mouseX < leftPos) {
+      return;
+    }
+    
+    if ((mouseX >= autoRotateX) &&
+        (mouseX < autoRotateX + toggleButtonSize) &&
+        (mouseY >= autoRotateY) &&
+        (mouseY < autoRotateY + toggleButtonSize)) {
+      if (lx.isAutoTransitionEnabled()) {
+        lx.disableAutoTransition();
+        println("Auto pattern transition disabled");
+      } else {
+        lx.enableAutoTransition(60000);
+        println("Auto pattern transition enabled");        
+      }
+      return;
+    }
+    
     if (mouseY > tempoY) {
       if (mouseY - tempoY < tempoHeight) {
         lx.tempo.tap();
@@ -404,7 +489,11 @@ class ControlUI extends OverlayUI {
       } else {
         int patternIndex = objectClickIndex(firstPatternY);
         if (patternIndex < patterns.length) {
-          lx.goIndex(patternIndex + patternScrollPos);
+          if (lx.isAutoTransitionEnabled() && (mouseX > eligibleLeft)) {
+            patterns[patternIndex + patternScrollPos].toggleEligible();
+          } else { 
+            lx.goIndex(patternIndex + patternScrollPos);
+          }
         }
       }
     }
@@ -549,6 +638,7 @@ class MappingUI extends OverlayUI {
   
   public void draw() {
     drawLogoAndBackground();
+    
     int yPos = 0;
     firstMappingY = yPos + lineHeight + 6;    
     yPos = drawObjectList(yPos, "MAPPING MODE", mappingModes, mappingModes, mappingModeStateMethod);
@@ -600,6 +690,11 @@ class MappingUI extends OverlayUI {
   public void mousePressed() {
     dragCube = dragStrip = dragChannel = false;
     lastY = mouseY;
+    
+    if (mouseX < leftPos) {
+      return;
+    }
+    
     if (mouseY >= stripFieldY) {
       if (mouseY < stripFieldY + lineHeight) {
         dragStrip = true;
@@ -670,73 +765,140 @@ class MappingUI extends OverlayUI {
 
 class DebugUI {
   
-  final int[][] channelList;
-  final int debugX = 10;
-  final int debugY = 42;
+  final ChannelMapping[] channelList;
+  final int debugX = 5;
+  final int debugY = 5;
   final int debugXSpacing = 28;
-  final int debugYSpacing = 22;
-  final int[][] debugState = new int[17][6];
+  final int debugYSpacing = 21;
+  final int[][] debugState;
+  final int[] indexState;
+  
+  final int ERROR_STATE_USED = 0;
+  final int ERROR_STATE_DUPLICATED = 1;
+  
+  final int CUBE_STATE_UNUSED = 0;
+  final int CUBE_STATE_USED = 1;
   
   final int DEBUG_STATE_ANIM = 0;
   final int DEBUG_STATE_WHITE = 1;
   final int DEBUG_STATE_OFF = 2;
   
-  DebugUI(int[][] frontChannels, int[][] rearChannels) {
-    channelList = new int[frontChannels.length + rearChannels.length][];
+  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 (int[] channel : frontChannels) {
-      channelList[channelIndex++] = channel;
-    }
-    for (int[] channel : rearChannels) {    
-      channelList[channelIndex++] = channel;
+    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() {
+  void draw() {    
     noStroke();
     int xBase = debugX;
     int yPos = debugY;
     
-    fill(color(0, 0, 0, 80));
-    rect(4, 32, 172, 388);
+    fill(#000000);
+    rect(0, 0, debugX + 5*debugXSpacing, height);
     
     int channelNum = 0;
-    for (int[] channel : channelList) {
+    for (ChannelMapping channel : channelList) {
       int xPos = xBase;
       drawNumBox(xPos, yPos, channelNum+1, debugState[channelNum][0]);
+      xPos += debugXSpacing;
       
-      boolean first = true;
-      int cubeNum = 0;
-      for (int cube : channel) {
-        if (cube == 0) {
+      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;
-        }
-        xPos += debugXSpacing;
-        if (first) {
-          first = false;
-        } else {
-          stroke(#999999);          
-          line(xPos - 12, yPos + 8, xPos, yPos + 8);
-        }
-        drawNumBox(xPos, yPos, cube, debugState[channelNum][cubeNum+1]);
-        ++cubeNum;
-      }
+        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();
+    stroke(#CCCCCC);
+    rect(xBase, yPos, 100, 16);
+    fill(#CCCCCC);
+    text("Unused Cubes",  xBase + 5, yPos + 12);
+    yPos += debugYSpacing;
+    
+    int x_index = 0;
+    for (int rawIndex = 1; rawIndex < glucose.model.cubes.size()+1; rawIndex++) {
+      if (indexState[rawIndex] == 0) {
+        drawNumBox(xBase + (x_index * debugXSpacing), yPos, rawIndex, 0, 2);
+        x_index++;
+        if (x_index > 4) {
+          x_index = 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, int label, int state, int cube_state) {
+    if (cube_state > 1) {
+      fill(#FF0000);
+      rect(xPos-2, yPos-2, 20, 20);
+    }
+    drawNumBox(xPos, yPos, label, state);
+  }
+  
   void drawNumBox(int xPos, int yPos, String label, int state) {
     noFill();
     color textColor = #cccccc;
@@ -773,20 +935,68 @@ class DebugUI {
     color white = #FFFFFF;
     color off = #000000;
     int channelIndex = 0;
-    for (int[] channel : channelList) {
-      int cubeIndex = 1;
-      for (int rawCubeIndex : channel) {
-        if (rawCubeIndex > 0) {
-          int 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;
+    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;
           }
-        }
-        ++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;
     }
@@ -798,7 +1008,7 @@ class DebugUI {
     if ((dy >= 0) && (dy < debugState.length)) {
       if ((dx >= 0) && (dx < debugState[dy].length)) {
         int newState = debugState[dy][dx] = (debugState[dy][dx] + 1) % 3;
-        if (dy == 16) {
+        if (dy == debugState.length-1) {
           for (int[] states : debugState) {
             for (int i = 0; i < states.length; ++i) {
               states[i] = newState;