+ 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);
+ }