X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=ShaheenGandhi.pde;h=a11d9caffbcbf5b4e81710610dbd7d1a56b449cf;hb=44521cb76a2deb074c9998d12c223efe734d2371;hp=a842fcf4a343b423a61e813353211e10ae4a0e47;hpb=ffb4b60ea2847464c8492419a6499c48d1e5c873;p=SugarCubes.git diff --git a/ShaheenGandhi.pde b/ShaheenGandhi.pde index a842fcf..a11d9ca 100644 --- a/ShaheenGandhi.pde +++ b/ShaheenGandhi.pde @@ -40,14 +40,14 @@ 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; + + 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); + return new PVector(x, y, z); } } @@ -88,11 +88,6 @@ class HelixPattern extends SCPattern { phaseNormal.mult(radius); } - private void setPhase(float phase) { - this.phase = phase; - setPhaseNormalFromPhase(); - } - Line getAxis() { return axis; } @@ -100,31 +95,37 @@ class HelixPattern extends SCPattern { void step(int deltaMs) { // Rotate if (rotationPeriod != 0) { - setPhase(phase + (deltaMs / rotationPeriod) * TWO_PI); + this.phase = (phase + ((float)deltaMs / (float)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); + return axis.rotatePoint(middle, (t / period) * TWO_PI + phase); } color colorOfPoint(final PVector p) { + float t = axis.getTValue(p); + + // 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); + } + // 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 = d < girth; - // Soften edges by fading brightness + // 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, (inToroid ? 80 : 0), b); + return color((lx.getBaseHuef() + (360*(phase / TWO_PI)))%360, 80, b); } } @@ -134,57 +135,109 @@ 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; + 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); 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) + new Line(origin, axis), + helixCoilPeriod, + helixCoilRadius, + helixCoilGirth, + 0, + helixCoilRotationPeriod); h2 = new Helix( - new Line(new PVector(100, 50, 70), new PVector(1,0,0)), - 700, - 50, - 30, + new Line(origin, axis), + helixCoilPeriod, + helixCoilRadius, + helixCoilGirth, 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. - + helixCoilRotationPeriod); } 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); for (Point p : model.points) { - color h1c = color(0,0,0); - color h2c = color(0,0,0); + 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); + } - if (h1on) { - h1c = h1.colorOfPoint(new PVector(p.x,p.y,p.z)); + if (!h2on) { + h2c = color(0,0,0); } - if (h2on) { - h2c = h2.colorOfPoint(new PVector(p.x,p.y,p.z)); + if (!spokesOn) { + spokeColor = color(0,0,0); } // The helices are positioned to not overlap. If that changes, // a better blending formula is probably needed. - colors[p.index] = blendColor(h1c, h2c, ADD); + colors[p.index] = blendColor(blendColor(h1c, h2c, ADD), spokeColor, ADD); } } }