Merge branch 'integration'
authorBen Morrow <childoftv@gmail.com>
Mon, 19 Aug 2013 03:40:47 +0000 (20:40 -0700)
committerBen Morrow <childoftv@gmail.com>
Mon, 19 Aug 2013 03:40:47 +0000 (20:40 -0700)
BenMorrow.pde [new file with mode: 0644]
GranimPattern.pde [new file with mode: 0644]
MarkSlee.pde
ShaheenGandhi.pde
TestPatterns.pde
_Mappings.pde
_Overlay.pde
_PandaDriver.pde
code/GLucose.jar

diff --git a/BenMorrow.pde b/BenMorrow.pde
new file mode 100644 (file)
index 0000000..9294250
--- /dev/null
@@ -0,0 +1,178 @@
+class Sandbox extends SCPattern
+{
+       int c=0;
+       int prevC=0;
+       int huerange=255;
+       int pointrange= model.points.size();
+       int striprange= model.strips.size();
+       int facerange= model.faces.size();
+       int cuberange = model.cubes.size();
+       int towerrange = model.towers.size();
+       int counter=0;
+
+       Sandbox(GLucose glucose) {
+               super(glucose);
+               println("points "+pointrange);
+               println("strips "+striprange);
+               println("faces "+facerange);
+               println("cubes "+cuberange);
+               println("towers "+towerrange);
+       }
+       
+       public void run(int deltaMs) {
+               
+
+               if(counter % 10 ==0)
+               {
+                       doDraw(c,0);
+                       c = (c + 1) % towerrange;
+                       long col = color(Math.round(Math.random()*255),255,255) ;
+                       doDraw(c,col);
+               }
+               counter++;
+
+       }
+
+       public void doDraw(int c,long col)
+       {
+                       Tower t= model.towers.get((int) c);
+                       for(Point p : t.points)
+                       {
+                               colors[p.index] = (int) col;
+                       }
+       }
+};
+
+class GranimTestPattern extends GranimPattern
+{
+       GranimTestPattern(GLucose glucose)
+       {
+               super(glucose);
+               addGraphic("myReds",new RedsGraphic(100));
+               int[] dots = {0,128,0,128,0,128,0,128,0,128,0,128};
+               addGraphic("myOtherColors",new ColorDotsGraphic(dots));
+
+               getGraphicByName("myOtherColors").position=100;
+       }
+       int counter=0;
+       public void run(int deltaMs) 
+       {
+               clearALL();
+               super.run(deltaMs);
+               
+               if(counter % 3 ==0)
+               {
+                       Graphic reds = getGraphicByName("myReds");
+                       Graphic others = getGraphicByName("myOtherColors");
+                       reds.position = reds.position + 1 % 19000;
+                       others.position = others.position + 10 % 19000;
+               }
+       }
+       public void clearALL()
+       {
+               for(int i = 0; i < colors.length; i++)
+               {
+                       colors[i] = 0;
+               }
+       }
+
+
+}
+
+class GranimTestPattern2 extends GranimPattern
+{
+       GranimTestPattern2(GLucose glucose)
+       {
+               super(glucose);
+               /*for(int i = 0;i < 100; i++)
+               {
+                       Graphic g = addGraphic("myReds_"+i,new RedsGraphic(Math.round(Math.random() * 100)));
+
+               }*/
+               Graphic g = addGraphic("myRandoms",new RandomsGranim(50));
+               g.position = 200;
+               
+       }
+       int counter=0;
+       float count=0;
+       public void run(int deltaMs) 
+       {
+               clearALL();
+               super.run(deltaMs);
+               Graphic randomsGraphic = getGraphicByName("myRandoms");
+               randomsGraphic.position = Math.round(sin(count)*1000)+5000;
+               count+= 0.005;
+       }
+       public void clearALL()
+       {
+               for(Point p : model.points)
+               {
+                       colors[p.index] = 0;
+               }
+       }
+
+
+};
+
+class DriveableCrossSections extends CrossSections
+{
+       BasicParameter xd; 
+       BasicParameter yd;
+       BasicParameter zd;
+       BasicParameter mode; 
+
+       DriveableCrossSections(GLucose glucose) {
+               super(glucose); 
+       }
+
+       public void addParams()
+       {
+               mode = new BasicParameter("Mode", 0.0);
+               xd = new BasicParameter("XD", 0.0);
+               yd = new BasicParameter("YD", 0.0);
+               zd = new BasicParameter("ZD", 0.0);
+               addParameter(mode);
+               addParameter(xd);
+           addParameter(yd);
+           addParameter(zd);
+           addParameter(xr);
+           addParameter(yr);
+           addParameter(zr);    
+           addParameter(xw);
+           addParameter(xl);
+           addParameter(yl);
+           addParameter(zl);
+               addParameter(yw);    
+           addParameter(zw);
+       }
+
+       public void onParameterChanged(LXParameter p) {
+                       if(p == mode)
+                       {
+                               if(interactive())
+                               {
+                                       xd.setValue(x.getValue()/200);
+                                       yd.setValue(y.getValue()/200);
+                                       zd.setValue(z.getValue()/100);
+                               }
+                       }
+       }
+
+       boolean interactive()
+       {
+               return Math.round(mode.getValuef())>0.5;
+       }
+
+       public void updateXYZVals()
+       {
+               if(interactive())
+               {
+                       xv = xd.getValuef()*200;
+                   yv = yd.getValuef()*200;
+                   zv = zd.getValuef()*100;
+               }else{
+                       super.updateXYZVals();
+               }
+       }
+
+}
\ No newline at end of file
diff --git a/GranimPattern.pde b/GranimPattern.pde
new file mode 100644 (file)
index 0000000..0a34baf
--- /dev/null
@@ -0,0 +1,232 @@
+import java.util.LinkedHashMap;
+class Graphic
+{
+       public boolean changed = false;
+       public int position  = 0;
+       public ArrayList<Integer> graphicBuffer;
+       Graphic()
+       {       
+               graphicBuffer = new ArrayList<Integer>();
+       }
+       public int width()
+       {
+               return graphicBuffer.size();
+       }
+
+       
+};
+class Granim extends Graphic
+{
+       HashMap<String,Graphic> displayList;
+       
+       Granim()
+       {
+               displayList = new HashMap<String,Graphic>();
+       }
+       public Graphic addGraphic(String name, Graphic g)
+       {
+               while(width()< g.position+1)
+               {
+                               graphicBuffer.add(color(0,0,0));
+               }
+               drawAll();
+               displayList.put(name , g);
+               changed =true;
+               return g;
+       }
+
+       public Graphic getGraphicByName(String name)
+       {
+               return displayList.get(name);
+       }
+
+       public void update()
+       {
+               
+               for(Graphic g : displayList.values())
+               {
+                       if(g instanceof Granim)
+                       {
+                               ((Granim) g).update();
+                               
+                       }
+                       changed = changed || g.changed;
+                       if(changed)
+                       {
+                               while(width()< g.position + g.width())
+                               {
+                                       graphicBuffer.add(color(0,0,0));
+                               }
+                               if(g.changed)
+                               {
+                                       drawOne(g);
+                                       g.changed =false;
+                               }
+                       }
+               }
+               changed = false;
+
+       }
+       public void drawOne(Graphic g)
+       {
+               graphicBuffer.addAll(g.position,g.graphicBuffer);
+       }
+       public void drawAll()
+       {
+       }
+};
+class GranimPattern extends SCPattern
+{
+       HashMap<String,Graphic> displayList;
+
+       GranimPattern(GLucose glucose)
+       {
+               super(glucose);
+               displayList = new HashMap<String,Graphic>();
+       }
+
+       public Graphic addGraphic(String name, Graphic g)
+       {
+               displayList.put(name,g);
+               return g;
+       }
+
+       public Graphic getGraphicByName(String name)
+       {
+               return displayList.get(name);
+       }
+
+       public void run(int deltaMs) 
+       {
+               drawToPointList();
+       }
+       private Integer[] gbuffer;
+       public void drawToPointList()
+       {
+               for(Graphic g : displayList.values())
+               {
+                       if(g instanceof Granim)
+                       {
+                               ((Granim) g).update();
+                       }
+                       List<Point> drawList = model.points.subList(Math.min(g.position,colors.length-1), Math.min(g.position + g.width(),colors.length-1));
+                       //println("drawlistsize "+drawList.size());
+                       
+                       gbuffer = g.graphicBuffer.toArray(new Integer[0]);
+                       
+                       for (int i=0; i < drawList.size(); i++)
+                       {
+                               colors[drawList.get(i).index] = gbuffer[i];
+                       }
+                       g.changed =false;
+               }
+       }
+
+};
+
+class RedsGraphic extends Graphic
+{
+       RedsGraphic()
+       {
+               super();
+               drawit(10);
+       }
+       RedsGraphic(int len)
+       {
+               super();
+               drawit(len);
+               
+       }
+       void drawit(int len)
+       {
+               for(int i = 0; i < len ;i++)
+               {
+                       graphicBuffer.add(color(0,255,255));
+               }
+       }
+};
+
+class RedsGranim extends Granim
+{
+       RedsGranim()
+       {
+               super();
+               addGraphic("myreds", new RedsGraphic(10));
+       }
+       RedsGranim(int len)
+       {
+               super();
+               addGraphic("myreds", new RedsGraphic(len));
+       }
+       public float count = 0.0;
+       public void update()
+       {
+               Graphic g=getGraphicByName("myreds");
+               g.position = Math.round(sin(count)*20)+100;
+               count+= 0.1;
+               if(count>Math.PI*2)
+               {
+                       count=0;
+               }
+               super.update();
+       }
+       
+};
+
+class RandomsGranim extends Granim
+{
+       private int _len =0 ;
+       RandomsGranim()
+       {
+               super();
+               _len =100;
+               addGraphic("myrandoms", makeGraphic(_len));
+       }
+       RandomsGranim(int len)
+       {
+               super();
+               _len=len;
+               addGraphic("myrandoms", makeGraphic(len));
+       }
+       public Graphic makeGraphic(int len)
+       {
+               int[] colors= new int[len]; 
+               for(int i =0;i<len;i++)
+               {
+                       colors[i]=(int) Math.round(Math.random()*255);
+               }
+               return new ColorDotsGraphic(colors);
+       }
+       private int count =1;
+       private int instanceCount =0;
+       public void update()
+       {
+               super.update();
+               if(instanceCount<50 && count % 20==0)
+               {
+                       instanceCount++;
+                       Graphic h=addGraphic("myrandoms_"+instanceCount, makeGraphic(_len));
+                       h.position = instanceCount*(_len+100);
+                       println("one more " + instanceCount+" at "+h.position);
+                       count=0;
+                       changed = true;
+               }
+               count++;
+               
+       }
+       
+};
+
+
+class ColorDotsGraphic extends Graphic
+{
+       ColorDotsGraphic(int[] colorSequence)
+       {
+               super();
+               for (int colorVal : colorSequence)
+               {
+                       graphicBuffer.add(color(colorVal, 255, 255));
+               }
+               changed = true;
+       }
+};
index 1ecb640dbd10c7bd59581ea3f34f3a6660171719..f955f97bd32474d7997ae50da122b729c73fa607 100644 (file)
@@ -365,6 +365,11 @@ class CrossSections extends SCPattern {
     addModulator(x).trigger();
     addModulator(y).trigger();
     addModulator(z).trigger();
+    addParams();
+  }
+  
+  public void addParams()
+  {
     addParameter(xr);
     addParameter(yr);
     addParameter(zr);    
@@ -375,8 +380,8 @@ class CrossSections extends SCPattern {
     addParameter(yw);    
     addParameter(zw);
   }
-  
-  void onParameterChanged(LXParameter p) {
+
+  public void onParameterChanged(LXParameter p) {
     if (p == xr) {
       x.setDuration(10000 - 8800*p.getValuef());
     } else if (p == yr) {
@@ -386,10 +391,19 @@ class CrossSections extends SCPattern {
     }
   }
 
+  float xv;
+  float yv;
+  float zv;  
+
+  public void updateXYZVals()
+  {
+    xv = x.getValuef();
+    yv = y.getValuef();
+    zv = z.getValuef(); 
+  }
+
   public void run(int deltaMs) {
-    float xv = x.getValuef();
-    float yv = y.getValuef();
-    float zv = z.getValuef();    
+    updateXYZVals();   
     float xlv = 100*xl.getValuef();
     float ylv = 100*yl.getValuef();
     float zlv = 100*zl.getValuef();
@@ -499,10 +513,10 @@ class AskewPlanes extends SCPattern {
     private final SinLFO a;
     private final SinLFO b;
     private final SinLFO c;
-    float av;
-    float bv;
-    float cv;
-    float denom;
+    float av = 1;
+    float bv = 1;
+    float cv = 1;
+    float denom = 0.1;
     
     Plane(int i) {
       addModulator(a = new SinLFO(-1, 1, 4000 + 1029*i)).trigger();
@@ -528,25 +542,33 @@ class AskewPlanes extends SCPattern {
       planes[i] = new Plane(i);
     }
   }
-
-  private final float denoms[] = new float[NUM_PLANES];
   
   public void run(int deltaMs) {
     float huev = lx.getBaseHuef();
-    int i = 0;
-    for (Plane p : planes) {
-      p.run(deltaMs);
-    }
+    
+    // This is super fucking bizarre. But if this is a for loop, the framerate
+    // tanks to like 30FPS, instead of 60. Call them manually and it works fine.
+    // Doesn't make ANY sense... there must be some weird side effect going on
+    // with the Processing internals perhaps?
+//    for (Plane plane : planes) {
+//      plane.run(deltaMs);
+//    }
+    planes[0].run(deltaMs);
+    planes[1].run(deltaMs);
+    planes[2].run(deltaMs);    
+    
     for (Point p : model.points) {
-       float d = MAX_FLOAT;
-       for (Plane plane : planes) {
-         d = min(d, abs(plane.av*(p.fx-model.xMax/2.) + plane.bv*(p.fy-model.yMax/2.) + plane.cv) / plane.denom);
-       }
-       colors[p.index] = color(
-         (lx.getBaseHuef() + abs(p.fx-model.xMax/2.)*.3 + p.fy*.8) % 360,
-         max(0, 100 - .8*abs(p.fx - model.xMax/2.)),
-         constrain(140 - 10.*d, 0, 100)
-       );
+      float d = MAX_FLOAT;
+      for (Plane plane : planes) {
+        if (plane.denom != 0) {
+          d = min(d, abs(plane.av*(p.fx-model.cx) + plane.bv*(p.fy-model.cy) + plane.cv) / plane.denom);
+        }
+      }
+      colors[p.index] = color(
+        (huev + abs(p.fx-model.cx)*.3 + p.fy*.8) % 360,
+        max(0, 100 - .8*abs(p.fx - model.cx)),
+        constrain(140 - 10.*d, 0, 100)
+      );
     }
   }
 }
@@ -574,9 +596,9 @@ class ShiftingPlane extends SCPattern {
     float dv = d.getValuef();    
     float denom = sqrt(av*av + bv*bv + cv*cv);
     for (Point p : model.points) {
-      float d = abs(av*(p.fx-model.xMax/2.) + bv*(p.fy-model.yMax/2.) + cv*(p.fz-model.zMax/2.) + dv) / denom;
+      float d = abs(av*(p.fx-model.cx) + bv*(p.fy-model.cy) + cv*(p.fz-model.cz) + dv) / denom;
       colors[p.index] = color(
-        (hv + abs(p.fx-model.xMax/2.)*.6 + abs(p.fy-model.yMax/2)*.9 + abs(p.fz - model.zMax/2.)) % 360,
+        (hv + abs(p.fx-model.cx)*.6 + abs(p.fy-model.cy)*.9 + abs(p.fz - model.cz)) % 360,
         constrain(110 - d*6, 0, 100),
         constrain(130 - 7*d, 0, 100)
       );
index 5ab568e3d5f9f5af6ef5325118b2767dc7238091..ff6ce2edcf82d4ce840eb105eaff462a5bda97a2 100644 (file)
@@ -7,57 +7,56 @@ class HelixPattern extends SCPattern {
   private class Line {
     private final PVector origin;
     private final PVector vector;
-    
+
     Line(PVector pt, PVector v) {
       origin = pt;
       vector = v.get();
       vector.normalize();
     }
-    
+
     PVector getPoint() {
       return origin;
     }
-    
+
     PVector getVector() {
       return vector;
     }
-    
-    PVector getPointAt(float t) {
-      PVector pt = PVector.mult(vector, t);
-      pt.add(origin);
-      return pt;
+
+    PVector getPointAt(final float t) {
+      return PVector.add(origin, PVector.mult(vector, t));
     }
-    
-    boolean isColinear(PVector pt) {
-      PVector projected = projected(pt);
+
+    boolean isColinear(final PVector pt) {
+      PVector projected = projectPoint(pt);
       return projected.x==pt.x && projected.y==pt.y && projected.z==pt.z;
     }
-    
-    float getTValue(PVector pt) {
+
+    float getTValue(final PVector pt) {
       PVector subtraction = PVector.sub(pt, origin);
       return subtraction.dot(vector);
-    }      
-    
-    PVector projected(PVector pt) {
+    }
+
+    PVector projectPoint(final PVector pt) {
       return getPointAt(getTValue(pt));
     }
-    
-    PVector rotatePoint(PVector pt, float rads) {
+
+    PVector rotatePoint(final PVector pt, final float rads) {
       Vec3D axisVec3D = new Vec3D(vector.x, vector.y, vector.z);
-      Matrix4x4 mat = new Matrix4x4();
-      mat.rotateAroundAxis(axisVec3D, rads);
-      Vec3D ptVec3D = new Vec3D(pt.x, pt.y, pt.z);
-      Vec3D rotatedPt = mat.applyTo(ptVec3D);
+      Vec3D originVec3D = new Vec3D(origin.x, origin.y, origin.z);
+      Matrix4x4 mat = new Matrix4x4().identity()
+        .rotateAroundAxis(axisVec3D, rads);
+      Vec3D ptVec3D = new Vec3D(pt.x, pt.y, pt.z).sub(originVec3D);
+      Vec3D rotatedPt = mat.applyTo(ptVec3D).add(originVec3D);
       return new PVector(rotatedPt.x, rotatedPt.y, rotatedPt.z);
     }
   }
 
   private class Helix {
     private final Line axis;
-    private final float period;
-    private final float rotationPeriod;
-    private final float radius;
-    private final float girth;
+    private final float period; // period of coil
+    private final float rotationPeriod; // animation period
+    private final float radius; // radius of coil
+    private final float girth; // girth of coil
     private final PVector referencePoint;
     private float phase;
     private PVector phaseNormal;
@@ -81,107 +80,164 @@ class HelixPattern extends SCPattern {
       }
 
       this.referencePoint = pt;
-      this.phase = phase;
 
-      setPhaseNormalFromPhase();      
-    }
-    
-    private void setPhaseNormalFromPhase() {
-      phaseNormal = axis.getVector().cross(axis.rotatePoint(referencePoint, phase));
+      // The normal is calculated by the cross product of the axis
+      // and a random point that is not colinear with it.
+      phaseNormal = axis.getVector().cross(referencePoint);
       phaseNormal.normalize();
       phaseNormal.mult(radius);
     }
-    
-    private void setPhase(float phase) {
-      this.phase = phase;
-      setPhaseNormalFromPhase();
+
+    Line getAxis() {
+      return axis;
     }
-    
+
     void step(int deltaMs) {
-      setPhase(phase + (deltaMs / rotationPeriod) * TWO_PI);
+      // Rotate
+      if (rotationPeriod != 0) {
+        this.phase = (phase + ((float)deltaMs / (float)rotationPeriod) * TWO_PI);
+      }
     }
-    
+
     PVector pointOnToroidalAxis(float t) {
       PVector p = axis.getPointAt(t);
       PVector middle = PVector.add(p, phaseNormal);
-      return axis.rotatePoint(middle, (t / period) * TWO_PI);
+      return axis.rotatePoint(middle, (t / period) * TWO_PI + phase);
     }
-    
-    color colorOfPoint(PVector p) {
-      // Calculate the projection of this point to the axis.
-      PVector projectedPoint = axis.projected(p);
-      
+
+    color colorOfPoint(final PVector p) {
+      float t = axis.getTValue(p);
+
+      // For performance reasons, cut out points that are outside of
+      // the tube where the toroidal coil lives.
+      if (abs(PVector.dist(p, axis.getPointAt(t)) - radius) > girth*.5f) {
+        return color(0,0,0);
+      }
+
       // Find the appropriate point for the current rotation
       // of the helix.
-      float t = axis.getTValue(projectedPoint);
       PVector toroidPoint = pointOnToroidalAxis(t);
-      
+
       // The rotated point represents the middle of the girth of
       // the helix.  Figure out if the current point is inside that
       // region.
       float d = PVector.dist(p, toroidPoint);
-      boolean inToroid = abs(d) < girth;
-      
-      return color((lx.getBaseHuef() + (360*(phase / TWO_PI)))%360, (inToroid ? 100 : 0), (inToroid ? 100 : 0));
+
+      // Soften edges by fading brightness.
+      float b = constrain(100*(1 - ((d-.5*girth)/(girth*.5))), 0, 100);
+      return color((lx.getBaseHuef() + (360*(phase / TWO_PI)))%360, 80, b);
     }
   }
 
   private final Helix h1;
   private final Helix h2;
-  
+
   private final BasicParameter helix1On = new BasicParameter("H1ON", 1);
   private final BasicParameter helix2On = new BasicParameter("H2ON", 1);
-  
+
+  private final BasicParameter basePairsOn = new BasicParameter("BPON", 1);
+  private final BasicParameter spokePeriodParam = new BasicParameter("SPPD", 0.40);
+  private final BasicParameter spokePhaseParam = new BasicParameter("SPPH", 0.25);
+
+  private static final float helixCoilPeriod = 100;
+  private static final float helixCoilRadius = 45;
+  private static final float helixCoilGirth = 20;
+  private static final float helixCoilRotationPeriod = 10000;
+
   public HelixPattern(GLucose glucose) {
     super(glucose);
-    
+
     addParameter(helix1On);
     addParameter(helix2On);
-    
+    addParameter(basePairsOn);
+    addParameter(spokePhaseParam);
+    addParameter(spokePeriodParam);
+
+    PVector origin = new PVector(100, 50, 45);
+    PVector axis = new PVector(1,0,0);
+
     h1 = new Helix(
-      new Line(new PVector(100, 50, 70), new PVector(1,0,0)),
-      700, // period
-      50, // radius
-      30, // girth
-      0,  // phase
-      10000); // rotation period (ms)
+      new Line(origin, axis),
+      helixCoilPeriod,
+      helixCoilRadius,
+      helixCoilGirth,
+      0,
+      helixCoilRotationPeriod);
     h2 = new Helix(
-      new Line(new PVector(100, 50, 70), new PVector(1,0,0)),
-      700,
-      50,
-      30,
+      new Line(origin, axis),
+      helixCoilPeriod,
+      helixCoilRadius,
+      helixCoilGirth,
       PI,
-      10000);
-
-    // TODO(shaheen) calculate line segments between
-    // toroidal points selected by stepping the
-    // parameterized t value.  select base pairs and
-    // associated colors.  lerp between colors for each
-    // base pair to produce a DNA effect.
+      helixCoilRotationPeriod);
   }
-  
+
   void run(int deltaMs) {
     boolean h1on = helix1On.getValue() > 0.5;
     boolean h2on = helix2On.getValue() > 0.5;
-    
+    boolean spokesOn = (float)basePairsOn.getValue() > 0.5;
+    float spokePeriod = (float)spokePeriodParam.getValue() * 100 + 1;
+    float spokeGirth = 10;
+    float spokePhase = (float)spokePhaseParam.getValue() * spokePeriod;
+    float spokeRadius = helixCoilRadius - helixCoilGirth*.5f;
+
     h1.step(deltaMs);
     h2.step(deltaMs);
-    
+
     for (Point p : model.points) {
-      color h1c = color(0,0,0);
-      color h2c = color(0,0,0);
-      
-      if (h1on) {
-        h1c = h1.colorOfPoint(new PVector(p.x,p.y,p.z));
+      PVector pt = new PVector(p.x,p.y,p.z);
+      color h1c = h1.colorOfPoint(pt);
+      color h2c = h2.colorOfPoint(pt);
+
+      // Find the closest spoke's t-value and calculate its
+      // axis.  Until everything animates in the model reference
+      // frame, this has to be calculated at every step because
+      // the helices rotate.
+      float t = h1.getAxis().getTValue(pt) + spokePhase;
+      float spokeAxisTValue = floor(((t + spokePeriod/2) / spokePeriod)) * spokePeriod;
+      PVector h1point = h1.pointOnToroidalAxis(spokeAxisTValue);
+      PVector h2point = h2.pointOnToroidalAxis(spokeAxisTValue);
+      PVector spokeVector = PVector.sub(h2point, h1point);
+      spokeVector.normalize();
+      Line spokeLine = new Line(h1point, spokeVector);
+      float spokeLength = PVector.dist(h1point, h2point);
+      // TODO(shaheen) investigate why h1.getAxis().getPointAt(spokeAxisTValue) doesn't quite
+      // have the same value.
+      PVector spokeCenter = PVector.add(h1point, PVector.mult(spokeVector, spokeLength/2.f));
+      PVector spokeStart = PVector.add(spokeCenter, PVector.mult(spokeLine.getVector(), -spokeRadius));
+      PVector spokeEnd = PVector.add(spokeCenter, PVector.mult(spokeLine.getVector(), spokeRadius));
+      float spokeStartTValue = spokeLine.getTValue(spokeStart);
+      float spokeEndTValue = spokeLine.getTValue(spokeEnd);
+      PVector pointOnSpoke = spokeLine.projectPoint(pt);
+      float projectedTValue = spokeLine.getTValue(pointOnSpoke);
+      float percentage = constrain(PVector.dist(pointOnSpoke, spokeStart) / spokeLength, 0.f, 1.f);
+      float b = ((PVector.dist(pt, pointOnSpoke) < spokeGirth) && (PVector.dist(pointOnSpoke, spokeCenter) < spokeRadius)) ? 100.f : 0.f;
+
+      color spokeColor;
+
+      if (spokeStartTValue < spokeEndTValue) {
+        spokeColor = lerpColor(h1c, h2c, percentage);
+      } else {
+        spokeColor = lerpColor(h2c, h1c, percentage);
+      }
+
+      spokeColor = color(hue(spokeColor), 80.f, b);
+
+      if (!h1on) {
+        h1c = color(0,0,0);
       }
-      
-      if (h2on) {
-        h2c = h2.colorOfPoint(new PVector(p.x,p.y,p.z));
+
+      if (!h2on) {
+        h2c = color(0,0,0);
       }
-      
+
+      if (!spokesOn) {
+        spokeColor = color(0,0,0);
+      }
+
       // The helices are positioned to not overlap.  If that changes,
       // a better blending formula is probably needed.
-      colors[p.index] = blendColor(h1c, h2c, ADD);
+      colors[p.index] = blendColor(blendColor(h1c, h2c, ADD), spokeColor, ADD);
     }
   }
 }
index 38ea2296200d31e7f13e370a7d1f3acc35a7df7d..14572a261a00aed742cde23da645fe14d082f94d 100644 (file)
@@ -1,8 +1,15 @@
+abstract class TestPattern extends SCPattern {
+  public TestPattern(GLucose glucose) {
+    super(glucose);
+    setEligible(false);
+  }
+}
+
 /**
  * Simplest demonstration of using the rotating master hue.
  * All pixels are full-on the same color.
  */
-class TestHuePattern extends SCPattern {
+class TestHuePattern extends TestPattern {
   public TestHuePattern(GLucose glucose) {
     super(glucose);
   }
@@ -19,7 +26,7 @@ class TestHuePattern extends SCPattern {
 /**
  * Test of a wave moving across the X axis.
  */
-class TestXPattern extends SCPattern {
+class TestXPattern extends TestPattern {
   private final SinLFO xPos = new SinLFO(0, model.xMax, 4000);
   public TestXPattern(GLucose glucose) {
     super(glucose);
@@ -41,7 +48,7 @@ class TestXPattern extends SCPattern {
 /**
  * Test of a wave on the Y axis.
  */
-class TestYPattern extends SCPattern {
+class TestYPattern extends TestPattern {
   private final SinLFO yPos = new SinLFO(0, model.yMax, 4000);
   public TestYPattern(GLucose glucose) {
     super(glucose);
@@ -59,7 +66,7 @@ class TestYPattern extends SCPattern {
 /**
  * Test of a wave on the Z axis.
  */
-class TestZPattern extends SCPattern {
+class TestZPattern extends TestPattern {
   private final SinLFO zPos = new SinLFO(0, model.zMax, 4000);
   public TestZPattern(GLucose glucose) {
     super(glucose);
@@ -77,7 +84,7 @@ class TestZPattern extends SCPattern {
 /**
  * This shows how to iterate over towers, enumerated in the model.
  */
-class TestTowerPattern extends SCPattern {
+class TestTowerPattern extends TestPattern {
   private final SawLFO towerIndex = new SawLFO(0, model.towers.size(), 1000*model.towers.size());
   
   public TestTowerPattern(GLucose glucose) {
@@ -119,7 +126,7 @@ class TestTowerPattern extends SCPattern {
  * of sparse, non-uniformly spaced pixels. Mutating the structure would move
  * things to a space where there are no pixels in 99% of the cases.
  */
-class TestProjectionPattern extends SCPattern {
+class TestProjectionPattern extends TestPattern {
   
   private final Projection projection;
   private final SawLFO angle = new SawLFO(0, TWO_PI, 9000);
@@ -161,7 +168,7 @@ class TestProjectionPattern extends SCPattern {
   } 
 }
 
-class TestCubePattern extends SCPattern {
+class TestCubePattern extends TestPattern {
   
   private SawLFO index = new SawLFO(0, Cube.POINTS_PER_CUBE, Cube.POINTS_PER_CUBE*60);
   
@@ -185,7 +192,7 @@ class TestCubePattern extends SCPattern {
   }
 }
 
-class MappingTool extends SCPattern {
+class MappingTool extends TestPattern {
     
   private int cubeIndex = 0;
   private int stripIndex = 0;
index b049bf7c3b972d6e2f458df52809d6ddf6512141..967048f1ca0cc157f56bbfc45e11cec1d3a0e3ee 100644 (file)
@@ -38,149 +38,153 @@ public Model buildModel() {
   // y-axis.
   //
   // The cubes automatically increment their y-position by Cube.EDGE_HEIGHT.
+  
+  final float STACKED_RELATIVE = 1;
+  final float STACKED_REL_SPIN = 2;
+  
   TowerMapping[] mapping = new TowerMapping[] {
     
     new TowerMapping(0, 0, 0, new float[][] {
-      {0, 0},
-      {5, -10, 20},
-      {0, -6},
-      {-5, -2, -20},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, 5, -10, 20},
+      {STACKED_RELATIVE, 0, -6},
+      {STACKED_RELATIVE, -5, -2, -20},
     }),
 
     new TowerMapping(Cube.EDGE_WIDTH + 2, 0, 0, new float[][] {
-      {0, 0},
-      {0, 5, 10},
-      {0, 2, 20},
-      {0, 0, 30},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, 0, 5, 10},
+      {STACKED_RELATIVE, 0, 2, 20},
+      {STACKED_RELATIVE, 0, 0, 30},
     }),
     
     // Back Cubes behind DJ platform (in order of increasing x)
     new TowerMapping(50, 5, BASS_DEPTH, new float[][] {
-      {0, 0},
-      {2, 0, 20},
-      {-2, 10},
-      {-5, 15, -20},
-      {-2, 13},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, 2, 0, 20},
+      {STACKED_RELATIVE, -2, 10},
+      {STACKED_RELATIVE, -5, 15, -20},
+      {STACKED_RELATIVE, -2, 13},
     }),
     
     new TowerMapping(79, 5, BASS_DEPTH, new float[][] {
-      {0, 0},
-      {2, 0, 20},
-      {4, 10},
-      {2, 15, -20},
-      {0, 13},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, 2, 0, 20},
+      {STACKED_RELATIVE, 4, 10},
+      {STACKED_RELATIVE, 2, 15, -20},
+      {STACKED_RELATIVE, 0, 13},
     }),
     
     new TowerMapping(107, 5, BASS_DEPTH, new float[][] {
-      {0, 0},
-      {4, 0, 20},
-      {6, 10},
-      {3, 15, -20},
-      // {8,  13},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, 4, 0, 20},
+      {STACKED_RELATIVE, 6, 10},
+      {STACKED_RELATIVE, 3, 15, -20},
+      // {STACKED_RELATIVE, 8,  13},
     }),
     
     new TowerMapping(133, 5, BASS_DEPTH, new float[][] {
-      {0, 0},
-      {-2, 0, 20},
-      {0, 10},
-      {2, 15, -20},
-      // {4, 13}
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, -2, 0, 20},
+      {STACKED_RELATIVE, 0, 10},
+      {STACKED_RELATIVE, 2, 15, -20},
+      // {STACKED_RELATIVE, 4, 13}
     }),
     
     new TowerMapping(165, 5, BASS_DEPTH, new float[][] {
-      {0, 0},
-      {-1, 20},
-      {2, 10},
-      {-2, 15, -20},
-      {3, 13},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, -1, 20},
+      {STACKED_RELATIVE, 2, 10},
+      {STACKED_RELATIVE, -2, 15, -20},
+      {STACKED_RELATIVE, 3, 13},
     }),
     
     // front DJ cubes
     new TowerMapping((TRAILER_WIDTH - BASS_WIDTH)/2, BASS_HEIGHT, 10, new float[][] {
-      {0, 0},
-      {0, -10, 20},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, 0, -10, 20},
     }),
     
     new TowerMapping((TRAILER_WIDTH - BASS_WIDTH)/2 + Cube.EDGE_HEIGHT, BASS_HEIGHT, 10, new float[][] {
-      {3, 0},
-      {2, -10, 20},
+      {STACKED_RELATIVE, 3, 0},
+      {STACKED_RELATIVE, 2, -10, 20},
     }),
     
     new TowerMapping((TRAILER_WIDTH - BASS_WIDTH)/2 + 2*Cube.EDGE_HEIGHT + 5, BASS_HEIGHT, 10, new float[][] {
-      {0, 0},
-      {1, 0, 10},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, 1, 0, 10},
     }),
     
     new TowerMapping((TRAILER_WIDTH - BASS_WIDTH)/2 + 3*Cube.EDGE_HEIGHT + 9, BASS_HEIGHT, 10, new float[][] {
-      {0, 0},
-      {-1, 0},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, -1, 0},
     }),
     
     new TowerMapping((TRAILER_WIDTH - BASS_WIDTH)/2 + 4*Cube.EDGE_HEIGHT + 15, BASS_HEIGHT, 10, new float[][] {
-      {0, 0},
-      {-1, 0},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, -1, 0},
     }),
     
     // left dj cubes    
     new TowerMapping((TRAILER_WIDTH - BASS_WIDTH)/2, BASS_HEIGHT, Cube.EDGE_HEIGHT + 2, new float[][] {
-      {0, 0},
-      {0, 2, 20},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, 0, 2, 20},
     }),
     
     new TowerMapping((TRAILER_WIDTH - BASS_WIDTH)/2, BASS_HEIGHT, 2*Cube.EDGE_HEIGHT + 4, new float[][] {
-      {0, 0},
-      {0, 2, 20},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, 0, 2, 20},
     }),
     
     // right dj cubes    
     new TowerMapping((TRAILER_WIDTH - BASS_WIDTH)/2 + 4*Cube.EDGE_HEIGHT + 15, BASS_HEIGHT, Cube.EDGE_HEIGHT + 2, new float[][] {
-      {0, 0},
-      {0, 2, 20},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, 0, 2, 20},
     }),
     
     new TowerMapping((TRAILER_WIDTH - BASS_WIDTH)/2 + 4*Cube.EDGE_HEIGHT + 15, BASS_HEIGHT, 2*Cube.EDGE_HEIGHT + 4, new float[][] {
-      {0, 0},
-      {0, 2, 20},
+      {STACKED_RELATIVE, 0, 0},
+      {STACKED_RELATIVE, 0, 2, 20},
     }),
 
     new TowerMapping(200, 0, 0, new float[][] {
-      {0, 10},
-      {5, 0, 20},
-      {0, 4},
-      {-5, 8, -20},
-      {0, 3},
+      {STACKED_RELATIVE, 0, 10},
+      {STACKED_RELATIVE, 5, 0, 20},
+      {STACKED_RELATIVE, 0, 4},
+      {STACKED_RELATIVE, -5, 8, -20},
+      {STACKED_RELATIVE, 0, 3},
     }),
     
     new TowerMapping(0, 0, Cube.EDGE_HEIGHT + 10, new float[][] {
-      {10, 0, 40},
-      {3, -2, 20},
-      {0, 0, 40},
-      {0, 0, 60},
-      {0, 0, 40},
+      {STACKED_RELATIVE, 10, 0, 40},
+      {STACKED_RELATIVE, 3, -2, 20},
+      {STACKED_RELATIVE, 0, 0, 40},
+      {STACKED_RELATIVE, 0, 0, 60},
+      {STACKED_RELATIVE, 0, 0, 40},
     }),
     
     new TowerMapping(20, 0, 2*Cube.EDGE_HEIGHT + 18, new float[][] {
-      {0, 0, 40},
-      {10, 0, 20},
-      {5, 0, 40},
-      {10, 0, 60},
-      {12, 0, 40},
+      {STACKED_RELATIVE, 0, 0, 40},
+      {STACKED_RELATIVE, 10, 0, 20},
+      {STACKED_RELATIVE, 5, 0, 40},
+      {STACKED_RELATIVE, 10, 0, 60},
+      {STACKED_RELATIVE, 12, 0, 40},
     }),
     
     new TowerMapping(210, 0, Cube.EDGE_HEIGHT + 15, new float[][] {
-      {0, 0, 40},
-      {5, 0, 20},
-      {8, 0, 40},
-      {3, 0, 60},
-      {0, 0, 40},
+      {STACKED_RELATIVE, 0, 0, 40},
+      {STACKED_RELATIVE, 5, 0, 20},
+      {STACKED_RELATIVE, 8, 0, 40},
+      {STACKED_RELATIVE, 3, 0, 60},
+      {STACKED_RELATIVE, 0, 0, 40},
     }),
     
     new TowerMapping(210, 0, 2*Cube.EDGE_HEIGHT + 25, new float[][] {
-      {0, 0, 40},
-      {5, 0, 20},
-      {2, 0, 40},
-      {5, 0, 60},
-      {0, 0, 40},
+      {STACKED_RELATIVE, 0, 0, 40},
+      {STACKED_RELATIVE, 5, 0, 20},
+      {STACKED_RELATIVE, 2, 0, 40},
+      {STACKED_RELATIVE, 5, 0, 60},
+      {STACKED_RELATIVE, 0, 0, 40},
     }),
     
   };
@@ -189,16 +193,25 @@ public Model buildModel() {
   ArrayList<Cube> tower;
   Cube[] cubes = new Cube[79];
   int cubeIndex = 1;  
-  float x, y, z, ry;
+  float tx, ty, tz, px, pz, ny, dx, dz, ry;
   for (TowerMapping tm : mapping) {
     tower = new ArrayList<Cube>();
-    x = tm.x;
-    y = tm.y;
-    z = tm.z;
+    px = tx = tm.x;
+    ny = ty = tm.y;
+    pz = tz = tm.z;
+    int ti = 0;
     for (float[] cp : tm.cubePositions) {
-      ry = (cp.length >= 3) ? cp[2] : 0;
-      tower.add(cubes[cubeIndex++] = new Cube(x + cp[0], y, z + cp[1], 0, ry, 0));
-      y += Cube.EDGE_HEIGHT;
+      float mode = cp[0];
+      if (mode == STACKED_RELATIVE) {
+        dx = cp[1];
+        dz = cp[2];
+        ry = (cp.length >= 4) ? cp[3] : 0;
+        tower.add(cubes[cubeIndex++] = new Cube(px = tx + dx, ny, pz = tz + dz, 0, ry, 0));
+        ny += Cube.EDGE_HEIGHT;
+      } else if (mode == STACKED_REL_SPIN) {
+        // Same as above but the front left of this cube is actually its back right for wiring
+        // TODO(mcslee): implement this
+      }
     }
     towerList.add(new Tower(tower));
   }
index 29e2353a8a0989627326d30c99947fab0662243a..a02aa12fc653111915f22c6d93673ec21d91fc41 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;
 
@@ -45,6 +46,8 @@ abstract class OverlayUI {
   protected final int pandaHeight = 13;
   protected final int pandaTop = height-16;
   
+  protected int eligibleLeft;
+  
   protected OverlayUI() {
     leftPos = width - w;
     leftTextPos = leftPos + 4;
@@ -110,6 +113,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);
@@ -117,6 +131,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;
@@ -143,12 +158,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));
@@ -214,6 +235,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;
 
@@ -240,6 +264,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;
@@ -396,6 +425,20 @@ class ControlUI extends OverlayUI {
       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();
@@ -430,7 +473,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);
+          }
         }
       }
     }
index e62cfff7b98ca5529282197698346d7e7275a6e7..ef86dbe71714473e20375ca7f42df31c26a55451 100644 (file)
@@ -62,6 +62,13 @@ public class PandaDriver {
   } 
 
   private void buildPointList(Model model, PandaMapping pm) {
+    final int[][] stripOrderings = new int[][] {
+      {  2,  1,  0,  3, 13, 12, 15, 14,  4,  7,  6,  5, 11, 10,  9,  8 }, // FRONT_LEFT
+      {  6,  5,  4,  7,  1,  0,  3,  2,  8, 11, 10,  9, 15, 14, 13, 12 }, // FRONT_RIGHT
+      { 14, 13, 12, 15,  9,  8, 11, 10,  0,  3,  2,  1,  7,  6,  5,  4 }, // REAR_LEFT
+      { 10,  9,  8, 11,  5,  4,  7,  6, 12, 15, 14, 13,  3,  2,  1,  0 }, // REAR_RIGHT
+    };
+
     int pi = 0;
     for (int[] channel : pm.channelList) {
       for (int cubeNumber : channel) {
@@ -74,10 +81,14 @@ public class PandaDriver {
           if (cube == null) {
             throw new RuntimeException("Non-zero, non-existing cube specified in channel mapping (" + cubeNumber + ")");
           }
-          final int[] stripOrder = new int[] {
-            2, 1, 0, 3, 13, 12, 15, 14, 4, 7, 6, 5, 11, 10, 9, 8
-          };
-          for (int stripIndex : stripOrder) {
+          int stripOrderIndex = 0;
+          switch (cube.wiring) {
+            case FRONT_LEFT: stripOrderIndex = 0; break;
+            case FRONT_RIGHT: stripOrderIndex = 1; break;
+            case REAR_LEFT: stripOrderIndex = 2; break;
+            case REAR_RIGHT: stripOrderIndex = 3; break;
+          }
+          for (int stripIndex : stripOrderings[stripOrderIndex]) {
             Strip s = cube.strips.get(stripIndex);
             for (int j = s.points.size() - 1; j >= 0; --j) {
               points[pi++] = s.points.get(j).index;
index 3525e185d14bb71910f52411c518c4ef2917e3a9..54b4b706c11da63d6ebdae3b92eab06d903c732e 100644 (file)
Binary files a/code/GLucose.jar and b/code/GLucose.jar differ