X-Git-Url: https://git.piment-noir.org/?p=SugarCubes.git;a=blobdiff_plain;f=ShaheenGandhi.pde;h=cda8fed4358c5b18302cdce3c6394179456a11c4;hp=09f513f383d484f9379806685f017c7d725d691a;hb=e0b9d8726c661f54f47340449037ac7f6b410de6;hpb=7992264af0268d309cdd43cc18287d825f42d578 diff --git a/ShaheenGandhi.pde b/ShaheenGandhi.pde index 09f513f..cda8fed 100644 --- a/ShaheenGandhi.pde +++ b/ShaheenGandhi.pde @@ -44,9 +44,12 @@ class HelixPattern extends SCPattern { final PVector o = origin; final PVector v = vector; - 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 - cos(t)) + p.x*cos(t) + (-o.z*v.y + o.y*v.z - v.z*p.y + v.y*p.z)*sin(t); - 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 - cos(t)) + p.y*cos(t) + (o.z*v.x - o.x*v.z + v.z*p.x - v.x*p.z)*sin(t); - 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 - cos(t)) + p.z*cos(t) + (-o.y*v.x + o.x*v.y - v.y*p.x + v.x*p.y)*sin(t); + 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,8 +94,16 @@ class HelixPattern extends SCPattern { Line getAxis() { return axis; } + + PVector getPhaseNormal() { + return phaseNormal; + } + + float getPhase() { + return phase; + } - void step(int deltaMs) { + void step(double deltaMs) { // Rotate if (rotationPeriod != 0) { this.phase = (phase + ((float)deltaMs / (float)rotationPeriod) * TWO_PI); @@ -104,56 +115,82 @@ 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) { - float t = axis.getTValue(p); + final float t = axis.getTValue(p); + final PVector axisPoint = axis.getPointAt(t); // For performance reasons, cut out points that are outside of // the tube where the toroidal coil lives. - if (abs(PVector.dist(p, axis.getPointAt(t)) - 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 // 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 // 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( @@ -170,53 +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. - 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)); + 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 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,