New LX fixes FlashEffect bug
[SugarCubes.git] / ShaheenGandhi.pde
index d562e3bd3fed0b12357611d46d4bb8072c76ebd6..cda8fed4358c5b18302cdce3c6394179456a11c4 100644 (file)
@@ -103,7 +103,7 @@ class HelixPattern extends SCPattern {
       return phase;
     }
 
-    void step(int deltaMs) {
+    void step(double deltaMs) {
       // Rotate
       if (rotationPeriod != 0) {
         this.phase = (phase + ((float)deltaMs / (float)rotationPeriod) * TWO_PI);
@@ -115,6 +115,13 @@ class HelixPattern extends SCPattern {
       PVector middle = PVector.add(p, phaseNormal);
       return axis.rotatePoint(middle, (t / period) * TWO_PI + phase);
     }
+    
+    private float myDist(PVector p1, PVector p2) {
+      final float x = p2.x-p1.x;
+      final float y = p2.y-p1.y;
+      final float z = p2.z-p1.z;
+      return sqrt(x*x + y*y + z*z);
+    }
 
     color colorOfPoint(final PVector p) {
       final float t = axis.getTValue(p);
@@ -122,8 +129,8 @@ class HelixPattern extends SCPattern {
 
       // For performance reasons, cut out points that are outside of
       // the tube where the toroidal coil lives.
-      if (abs(PVector.dist(p, axisPoint) - radius) > girth*.5f) {
-        return color(0,0,0);
+      if (abs(myDist(p, axisPoint) - radius) > girth*.5f) {
+        return lx.hsb(0,0,0);
       }
 
       // Find the appropriate point for the current rotation
@@ -135,39 +142,55 @@ class HelixPattern extends SCPattern {
       // 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);
+      float d = myDist(p, toroidPoint);
 
       // 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);
+      return lx.hsb((lx.getBaseHuef() + (360*(phase / TWO_PI)))%360, 80, b);
+    }
+  }
+  
+  private class BasePairInfo {
+    Line line;
+    float colorPhase1;
+    float colorPhase2;
+    
+    BasePairInfo(Line line, float colorPhase1, float colorPhase2) {
+      this.line = line;
+      this.colorPhase1 = colorPhase1;
+      this.colorPhase2 = colorPhase2;
     }
   }
 
   private final Helix h1;
   private final Helix h2;
+  private final BasePairInfo[] basePairs;
 
   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 static final float helixCoilPeriod = 100;
-  private static final float helixCoilRadius = 45;
-  private static final float helixCoilGirth = 20;
-  private static final float helixCoilRotationPeriod = 10000;
+  private static final float helixCoilRadius = 50;
+  private static final float helixCoilGirth = 30;
+  private static final float helixCoilRotationPeriod = 5000;
 
   private static final float spokePeriod = 40;
-  private static final float spokeGirth = 10;
+  private static final float spokeGirth = 20;
   private static final float spokePhase = 10;
-  private static final float spokeRadius = 35; // helixCoilRadius - helixCoilGirth*.5f;
+  private static final float spokeRadius = helixCoilRadius - helixCoilGirth*.5f;
+  
+  private static final float tMin = -200;
+  private static final float tMax = 200;
 
-  public HelixPattern(GLucose glucose) {
-    super(glucose);
+  public HelixPattern(LX lx) {
+    super(lx);
 
     addParameter(helix1On);
     addParameter(helix2On);
     addParameter(basePairsOn);
 
-    PVector origin = new PVector(100, 50, 45);
+    PVector origin = new PVector(100, 50, 55);
     PVector axis = new PVector(1,0,0);
 
     h1 = new Helix(
@@ -184,54 +207,67 @@ class HelixPattern extends SCPattern {
       helixCoilGirth,
       PI,
       helixCoilRotationPeriod);
+      
+    basePairs = new BasePairInfo[(int)floor((tMax - tMin)/spokePeriod)];
+  }
+
+  private void calculateSpokes() {
+    float colorPhase = PI/6;
+    for (float t = tMin + spokePhase; t < tMax; t += spokePeriod) {
+      int spokeIndex = (int)floor((t - tMin)/spokePeriod);
+      PVector h1point = h1.pointOnToroidalAxis(t);
+      PVector spokeCenter = h1.getAxis().getPointAt(t);
+      PVector spokeVector = PVector.sub(h1point, spokeCenter);
+      Line spokeLine = new Line(spokeCenter, spokeVector);
+      basePairs[spokeIndex] = new BasePairInfo(spokeLine, colorPhase * spokeIndex, colorPhase * (spokeIndex + 1));
+    }
   }
   
-  private color calculateSpokeColor(final color h1c, final color h2c, final PVector pt) {
+  private color calculateSpokeColor(final PVector 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.
     Line axis = h1.getAxis();
     float t = axis.getTValue(pt) + spokePhase;
-    float spokeAxisTValue = floor(((t + spokePeriod/2) / spokePeriod)) * spokePeriod;
-    PVector h1point = axis.getPointAt(t);
-    h1point.add(h1.getPhaseNormal());
-    h1point = axis.rotatePoint(h1point, (t / helixCoilPeriod) * TWO_PI + h1.getPhase());
-    // TODO(shaheen) investigate why h1.getAxis().getPointAt(spokeAxisTValue) doesn't quite
-    // have the same value as finding the middle between h1point and h2point.
-    PVector spokeCenter = h1.getAxis().getPointAt(spokeAxisTValue);
-    PVector spokeVector = PVector.sub(h1point, spokeCenter);
-    spokeVector.normalize();
-    Line spokeLine = new Line(h1point, spokeVector);
+    int spokeIndex = (int)floor((t - tMin + spokePeriod/2) / spokePeriod);
+    if (spokeIndex < 0 || spokeIndex >= basePairs.length) {
+      return lx.hsb(0,0,0);
+    }
+    BasePairInfo basePair = basePairs[spokeIndex];
+    Line spokeLine = basePair.line;
     PVector pointOnSpoke = spokeLine.projectPoint(pt);
-    float b = ((PVector.dist(pt, pointOnSpoke) < spokeGirth) && (PVector.dist(pointOnSpoke, spokeCenter) < spokeRadius)) ? 100.f : 0.f;
-    return color(100, 80.f, b);
+    float d = PVector.dist(pt, pointOnSpoke);
+    float b = (PVector.dist(pointOnSpoke, spokeLine.getPoint()) < spokeRadius) ? constrain(100*(1 - ((d-.5*spokeGirth)/(spokeGirth*.5))), 0, 100) : 0.f;
+    float phase = spokeLine.getTValue(pointOnSpoke) < 0 ? basePair.colorPhase1 : basePair.colorPhase2;
+    return lx.hsb((lx.getBaseHuef() + (360*(phase / TWO_PI)))%360, 80.f, b);
   }
 
-  void run(int deltaMs) {
+  void run(double deltaMs) {
     boolean h1on = helix1On.getValue() > 0.5;
     boolean h2on = helix2On.getValue() > 0.5;
     boolean spokesOn = (float)basePairsOn.getValue() > 0.5;
 
     h1.step(deltaMs);
     h2.step(deltaMs);
+    calculateSpokes();
 
-    for (Point p : model.points) {
+    for (LXPoint p : model.points) {
       PVector pt = new PVector(p.x,p.y,p.z);
       color h1c = h1.colorOfPoint(pt);
       color h2c = h2.colorOfPoint(pt);
-      color spokeColor = calculateSpokeColor(h1c, h2c, pt);
+      color spokeColor = calculateSpokeColor(pt);
 
       if (!h1on) {
-        h1c = color(0,0,0);
+        h1c = lx.hsb(0,0,0);
       }
 
       if (!h2on) {
-        h2c = color(0,0,0);
+        h2c = lx.hsb(0,0,0);
       }
 
       if (!spokesOn) {
-        spokeColor = color(0,0,0);
+        spokeColor = lx.hsb(0,0,0);
       }
 
       // The helices are positioned to not overlap.  If that changes,