[Helix] A rotating double helix.
authorShaheen Gandhi <visigoth@gmail.com>
Mon, 12 Aug 2013 06:58:50 +0000 (23:58 -0700)
committerShaheen Gandhi <shaheen@fb.com>
Tue, 13 Aug 2013 07:07:42 +0000 (00:07 -0700)
This pattern draws a double helix that rotates about the X axis.  It is intended
to extend it with line segments connecting the toroidal segments that represent
the edges of the helices, thereby creating a structure that looks like DNA.

ShaheenGandhi.pde [new file with mode: 0644]
SugarCubes.pde

diff --git a/ShaheenGandhi.pde b/ShaheenGandhi.pde
new file mode 100644 (file)
index 0000000..5ab568e
--- /dev/null
@@ -0,0 +1,188 @@
+import toxi.geom.Vec3D;
+import toxi.geom.Matrix4x4;
+
+class HelixPattern extends SCPattern {
+
+  // Stores a line in point + vector form
+  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;
+    }
+    
+    boolean isColinear(PVector pt) {
+      PVector projected = projected(pt);
+      return projected.x==pt.x && projected.y==pt.y && projected.z==pt.z;
+    }
+    
+    float getTValue(PVector pt) {
+      PVector subtraction = PVector.sub(pt, origin);
+      return subtraction.dot(vector);
+    }      
+    
+    PVector projected(PVector pt) {
+      return getPointAt(getTValue(pt));
+    }
+    
+    PVector rotatePoint(PVector pt, 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);
+      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 PVector referencePoint;
+    private float phase;
+    private PVector phaseNormal;
+
+    Helix(Line axis, float period, float radius, float girth, float phase, float rotationPeriod) {
+      this.axis = axis;
+      this.period = period;
+      this.radius = radius;
+      this.girth = girth;
+      this.phase = phase;
+      this.rotationPeriod = rotationPeriod;
+
+      // Generate a normal that will rotate to
+      // produce the helical shape.
+      PVector pt = new PVector(0, 1, 0);
+      if (this.axis.isColinear(pt)) {
+        pt = new PVector(0, 0, 1);
+        if (this.axis.isColinear(pt)) {
+          pt = new PVector(0, 1, 1);
+        }
+      }
+
+      this.referencePoint = pt;
+      this.phase = phase;
+
+      setPhaseNormalFromPhase();      
+    }
+    
+    private void setPhaseNormalFromPhase() {
+      phaseNormal = axis.getVector().cross(axis.rotatePoint(referencePoint, phase));
+      phaseNormal.normalize();
+      phaseNormal.mult(radius);
+    }
+    
+    private void setPhase(float phase) {
+      this.phase = phase;
+      setPhaseNormalFromPhase();
+    }
+    
+    void step(int deltaMs) {
+      setPhase(phase + (deltaMs / 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);
+    }
+    
+    color colorOfPoint(PVector p) {
+      // Calculate the projection of this point to the axis.
+      PVector projectedPoint = axis.projected(p);
+      
+      // 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));
+    }
+  }
+
+  private final Helix h1;
+  private final Helix h2;
+  
+  private final BasicParameter helix1On = new BasicParameter("H1ON", 1);
+  private final BasicParameter helix2On = new BasicParameter("H2ON", 1);
+  
+  public HelixPattern(GLucose glucose) {
+    super(glucose);
+    
+    addParameter(helix1On);
+    addParameter(helix2On);
+    
+    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)
+    h2 = new Helix(
+      new Line(new PVector(100, 50, 70), new PVector(1,0,0)),
+      700,
+      50,
+      30,
+      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.
+  }
+  
+  void run(int deltaMs) {
+    boolean h1on = helix1On.getValue() > 0.5;
+    boolean h2on = helix2On.getValue() > 0.5;
+    
+    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));
+      }
+      
+      if (h2on) {
+        h2c = h2.colorOfPoint(new PVector(p.x,p.y,p.z));
+      }
+      
+      // The helices are positioned to not overlap.  If that changes,
+      // a better blending formula is probably needed.
+      colors[p.index] = blendColor(h1c, h2c, ADD);
+    }
+  }
+}
+
index bb55e6fea0ca40c693dc7169ca9d9877d17cd275..1aac8c94e9cbd3e06261e824b867fdf5052342b7 100644 (file)
@@ -25,6 +25,7 @@
 
 LXPattern[] patterns(GLucose glucose) {
   return new LXPattern[] {
+    new HelixPattern(glucose),
     new ShiftingPlane(glucose),
     new AskewPlanes(glucose),
     new Swarm(glucose),