+ 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 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;
+ int spokeIndex = (int)floor((t - tMin + spokePeriod/2) / spokePeriod);
+ if (spokeIndex < 0 || spokeIndex >= basePairs.length) {
+ return color(0,0,0);
+ }
+ BasePairInfo basePair = basePairs[spokeIndex];
+ Line spokeLine = basePair.line;
+ PVector pointOnSpoke = spokeLine.projectPoint(pt);
+ 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 color((lx.getBaseHuef() + (360*(phase / TWO_PI)))%360, 80.f, b);