From: Ben Morrow Date: Mon, 19 Aug 2013 02:59:05 +0000 (-0700) Subject: Merge branch 'master' of github.com:sugarcubes/SugarCubes into BenMorrow X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=1adfd26520e2d62cc85788624f711a86614f4c3b;hp=e1635ff6bd6229d0671b167307bc9f1e231cd2ad;p=SugarCubes.git Merge branch 'master' of github.com:sugarcubes/SugarCubes into BenMorrow --- diff --git a/ShaheenGandhi.pde b/ShaheenGandhi.pde index 5ab568e..ff6ce2e 100644 --- a/ShaheenGandhi.pde +++ b/ShaheenGandhi.pde @@ -7,57 +7,56 @@ class HelixPattern extends SCPattern { 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; + + PVector getPointAt(final float t) { + return PVector.add(origin, PVector.mult(vector, t)); } - - boolean isColinear(PVector pt) { - PVector projected = projected(pt); + + boolean isColinear(final PVector pt) { + PVector projected = projectPoint(pt); return projected.x==pt.x && projected.y==pt.y && projected.z==pt.z; } - - float getTValue(PVector pt) { + + float getTValue(final PVector pt) { PVector subtraction = PVector.sub(pt, origin); return subtraction.dot(vector); - } - - PVector projected(PVector pt) { + } + + PVector projectPoint(final PVector pt) { return getPointAt(getTValue(pt)); } - - PVector rotatePoint(PVector pt, float rads) { + + PVector rotatePoint(final PVector pt, final 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); + 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); } } 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 float period; // period of coil + private final float rotationPeriod; // animation period + private final float radius; // radius of coil + private final float girth; // girth of coil private final PVector referencePoint; private float phase; private PVector phaseNormal; @@ -81,107 +80,164 @@ class HelixPattern extends SCPattern { } this.referencePoint = pt; - this.phase = phase; - setPhaseNormalFromPhase(); - } - - private void setPhaseNormalFromPhase() { - phaseNormal = axis.getVector().cross(axis.rotatePoint(referencePoint, phase)); + // The normal is calculated by the cross product of the axis + // and a random point that is not colinear with it. + phaseNormal = axis.getVector().cross(referencePoint); phaseNormal.normalize(); phaseNormal.mult(radius); } - - private void setPhase(float phase) { - this.phase = phase; - setPhaseNormalFromPhase(); + + Line getAxis() { + return axis; } - + void step(int deltaMs) { - setPhase(phase + (deltaMs / rotationPeriod) * TWO_PI); + // Rotate + if (rotationPeriod != 0) { + 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(PVector p) { - // Calculate the projection of this point to the axis. - PVector projectedPoint = axis.projected(p); - + + 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 = abs(d) < girth; - - return color((lx.getBaseHuef() + (360*(phase / TWO_PI)))%360, (inToroid ? 100 : 0), (inToroid ? 100 : 0)); + + // 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); } } private final Helix h1; private final Helix h2; - + 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); - - if (h1on) { - h1c = h1.colorOfPoint(new PVector(p.x,p.y,p.z)); + 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 (h2on) { - h2c = h2.colorOfPoint(new PVector(p.x,p.y,p.z)); + + if (!h2on) { + h2c = color(0,0,0); } - + + 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); } } } diff --git a/_PandaDriver.pde b/_PandaDriver.pde index e62cfff..ef86dbe 100644 --- a/_PandaDriver.pde +++ b/_PandaDriver.pde @@ -62,6 +62,13 @@ public class PandaDriver { } private void buildPointList(Model model, PandaMapping pm) { + final int[][] stripOrderings = new int[][] { + { 2, 1, 0, 3, 13, 12, 15, 14, 4, 7, 6, 5, 11, 10, 9, 8 }, // FRONT_LEFT + { 6, 5, 4, 7, 1, 0, 3, 2, 8, 11, 10, 9, 15, 14, 13, 12 }, // FRONT_RIGHT + { 14, 13, 12, 15, 9, 8, 11, 10, 0, 3, 2, 1, 7, 6, 5, 4 }, // REAR_LEFT + { 10, 9, 8, 11, 5, 4, 7, 6, 12, 15, 14, 13, 3, 2, 1, 0 }, // REAR_RIGHT + }; + int pi = 0; for (int[] channel : pm.channelList) { for (int cubeNumber : channel) { @@ -74,10 +81,14 @@ public class PandaDriver { if (cube == null) { throw new RuntimeException("Non-zero, non-existing cube specified in channel mapping (" + cubeNumber + ")"); } - final int[] stripOrder = new int[] { - 2, 1, 0, 3, 13, 12, 15, 14, 4, 7, 6, 5, 11, 10, 9, 8 - }; - for (int stripIndex : stripOrder) { + int stripOrderIndex = 0; + switch (cube.wiring) { + case FRONT_LEFT: stripOrderIndex = 0; break; + case FRONT_RIGHT: stripOrderIndex = 1; break; + case REAR_LEFT: stripOrderIndex = 2; break; + case REAR_RIGHT: stripOrderIndex = 3; break; + } + for (int stripIndex : stripOrderings[stripOrderIndex]) { Strip s = cube.strips.get(stripIndex); for (int j = s.points.size() - 1; j >= 0; --j) { points[pi++] = s.points.get(j).index; diff --git a/code/GLucose.jar b/code/GLucose.jar index 04bf1fa..54b4b70 100644 Binary files a/code/GLucose.jar and b/code/GLucose.jar differ