[Helix][perf] Inline pointOnToroidalAxis
[SugarCubes.git] / ShaheenGandhi.pde
index ff6ce2edcf82d4ce840eb105eaff462a5bda97a2..dcdb90800a84c754aa4bbb9a2f20a327d6ab0b24 100644 (file)
@@ -40,14 +40,17 @@ class HelixPattern extends SCPattern {
       return getPointAt(getTValue(pt));
     }
 
-    PVector rotatePoint(final PVector pt, final float rads) {
-      Vec3D axisVec3D = new Vec3D(vector.x, vector.y, vector.z);
-      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);
+    PVector rotatePoint(final PVector p, final float t) {
+      final PVector o = origin;
+      final PVector v = vector;
+      
+      final float cost = cos(t);
+      final float sint = sin(t);
+
+      float x = (o.x*(v.y*v.y + v.z*v.z) - v.x*(o.y*v.y + o.z*v.z - v.x*p.x - v.y*p.y - v.z*p.z))*(1 - cost) + p.x*cost + (-o.z*v.y + o.y*v.z - v.z*p.y + v.y*p.z)*sint;
+      float y = (o.y*(v.x*v.x + v.z*v.z) - v.y*(o.x*v.x + o.z*v.z - v.x*p.x - v.y*p.y - v.z*p.z))*(1 - cost) + p.y*cost + (o.z*v.x - o.x*v.z + v.z*p.x - v.x*p.z)*sint;
+      float z = (o.z*(v.x*v.x + v.y*v.y) - v.z*(o.x*v.x + o.y*v.y - v.x*p.x - v.y*p.y - v.z*p.z))*(1 - cost) + p.z*cost + (-o.y*v.x + o.x*v.y - v.y*p.x + v.x*p.y)*sint;
+      return new PVector(x, y, z);
     }
   }
 
@@ -91,6 +94,14 @@ class HelixPattern extends SCPattern {
     Line getAxis() {
       return axis;
     }
+    
+    PVector getPhaseNormal() {
+      return phaseNormal;
+    }
+    
+    float getPhase() {
+      return phase;
+    }
 
     void step(int deltaMs) {
       // Rotate
@@ -116,7 +127,9 @@ class HelixPattern extends SCPattern {
 
       // Find the appropriate point for the current rotation
       // of the helix.
-      PVector toroidPoint = pointOnToroidalAxis(t);
+      PVector toroidPoint = axisPoint;
+      toroidPoint.add(phaseNormal);
+      toroidPoint = axis.rotatePoint(toroidPoint, (t / period) * TWO_PI + phase);
 
       // The rotated point represents the middle of the girth of
       // the helix.  Figure out if the current point is inside that
@@ -134,24 +147,24 @@ class HelixPattern extends SCPattern {
 
   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;
 
+  private static final float spokePeriod = 40;
+  private static final float spokeGirth = 10;
+  private static final float spokePhase = 10;
+  private static final float spokeRadius = 35; // helixCoilRadius - helixCoilGirth*.5f;
+
   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);
@@ -171,15 +184,33 @@ class HelixPattern extends SCPattern {
       PI,
       helixCoilRotationPeriod);
   }
+  
+  private color calculateSpokeColor(final color h1c, final color h2c, 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);
+    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);
+  }
 
   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);
@@ -188,40 +219,7 @@ class HelixPattern extends SCPattern {
       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);
+      color spokeColor = calculateSpokeColor(h1c, h2c, pt);
 
       if (!h1on) {
         h1c = color(0,0,0);