[Helix] Cleanups
[SugarCubes.git] / ShaheenGandhi.pde
CommitLineData
fd8a39b0
SG
1import toxi.geom.Vec3D;
2import toxi.geom.Matrix4x4;
3
4class HelixPattern extends SCPattern {
5
6 // Stores a line in point + vector form
7 private class Line {
8 private final PVector origin;
9 private final PVector vector;
ee1f21c7 10
fd8a39b0
SG
11 Line(PVector pt, PVector v) {
12 origin = pt;
13 vector = v.get();
14 vector.normalize();
15 }
ee1f21c7 16
fd8a39b0
SG
17 PVector getPoint() {
18 return origin;
19 }
ee1f21c7 20
fd8a39b0
SG
21 PVector getVector() {
22 return vector;
23 }
ee1f21c7
SG
24
25 PVector getPointAt(final float t) {
26 return PVector.add(origin, PVector.mult(vector, t));
fd8a39b0 27 }
ee1f21c7
SG
28
29 boolean isColinear(final PVector pt) {
30 PVector projected = projectPoint(pt);
fd8a39b0
SG
31 return projected.x==pt.x && projected.y==pt.y && projected.z==pt.z;
32 }
ee1f21c7
SG
33
34 float getTValue(final PVector pt) {
fd8a39b0
SG
35 PVector subtraction = PVector.sub(pt, origin);
36 return subtraction.dot(vector);
ee1f21c7
SG
37 }
38
39 PVector projectPoint(final PVector pt) {
fd8a39b0
SG
40 return getPointAt(getTValue(pt));
41 }
ee1f21c7
SG
42
43 PVector rotatePoint(final PVector pt, final float rads) {
fd8a39b0
SG
44 Vec3D axisVec3D = new Vec3D(vector.x, vector.y, vector.z);
45 Matrix4x4 mat = new Matrix4x4();
46 mat.rotateAroundAxis(axisVec3D, rads);
47 Vec3D ptVec3D = new Vec3D(pt.x, pt.y, pt.z);
48 Vec3D rotatedPt = mat.applyTo(ptVec3D);
49 return new PVector(rotatedPt.x, rotatedPt.y, rotatedPt.z);
50 }
51 }
52
53 private class Helix {
54 private final Line axis;
ee1f21c7
SG
55 private final float period; // period of coil
56 private final float rotationPeriod; // animation period
57 private final float radius; // radius of coil
58 private final float girth; // girth of coil
fd8a39b0
SG
59 private final PVector referencePoint;
60 private float phase;
61 private PVector phaseNormal;
62
63 Helix(Line axis, float period, float radius, float girth, float phase, float rotationPeriod) {
64 this.axis = axis;
65 this.period = period;
66 this.radius = radius;
67 this.girth = girth;
68 this.phase = phase;
69 this.rotationPeriod = rotationPeriod;
70
71 // Generate a normal that will rotate to
72 // produce the helical shape.
73 PVector pt = new PVector(0, 1, 0);
74 if (this.axis.isColinear(pt)) {
75 pt = new PVector(0, 0, 1);
76 if (this.axis.isColinear(pt)) {
77 pt = new PVector(0, 1, 1);
78 }
79 }
80
81 this.referencePoint = pt;
fd8a39b0 82
ee1f21c7
SG
83 // The normal is calculated by the cross product of the axis
84 // and a random point that is not colinear with it.
85 phaseNormal = axis.getVector().cross(referencePoint);
fd8a39b0
SG
86 phaseNormal.normalize();
87 phaseNormal.mult(radius);
88 }
ee1f21c7 89
fd8a39b0
SG
90 private void setPhase(float phase) {
91 this.phase = phase;
92 setPhaseNormalFromPhase();
93 }
ee1f21c7
SG
94
95 Line getAxis() {
96 return axis;
97 }
98
fd8a39b0 99 void step(int deltaMs) {
ee1f21c7
SG
100 // Rotate
101 if (rotationPeriod != 0) {
102 setPhase(phase + (deltaMs / rotationPeriod) * TWO_PI);
103 }
fd8a39b0 104 }
ee1f21c7 105
fd8a39b0
SG
106 PVector pointOnToroidalAxis(float t) {
107 PVector p = axis.getPointAt(t);
108 PVector middle = PVector.add(p, phaseNormal);
109 return axis.rotatePoint(middle, (t / period) * TWO_PI);
110 }
ee1f21c7
SG
111
112 color colorOfPoint(final PVector p) {
fd8a39b0
SG
113 // Find the appropriate point for the current rotation
114 // of the helix.
115 float t = axis.getTValue(projectedPoint);
116 PVector toroidPoint = pointOnToroidalAxis(t);
ee1f21c7 117
fd8a39b0
SG
118 // The rotated point represents the middle of the girth of
119 // the helix. Figure out if the current point is inside that
120 // region.
121 float d = PVector.dist(p, toroidPoint);
c27cb078 122 boolean inToroid = d < girth;
ee1f21c7 123
c27cb078
SG
124 // Soften edges by fading brightness
125 float b = constrain(100*(1 - ((d-.5*girth)/(girth*.5))), 0, 100);
126 return color((lx.getBaseHuef() + (360*(phase / TWO_PI)))%360, (inToroid ? 80 : 0), b);
fd8a39b0
SG
127 }
128 }
129
130 private final Helix h1;
131 private final Helix h2;
ee1f21c7 132
fd8a39b0
SG
133 private final BasicParameter helix1On = new BasicParameter("H1ON", 1);
134 private final BasicParameter helix2On = new BasicParameter("H2ON", 1);
ee1f21c7 135
fd8a39b0
SG
136 public HelixPattern(GLucose glucose) {
137 super(glucose);
ee1f21c7 138
fd8a39b0
SG
139 addParameter(helix1On);
140 addParameter(helix2On);
ee1f21c7 141
fd8a39b0
SG
142 h1 = new Helix(
143 new Line(new PVector(100, 50, 70), new PVector(1,0,0)),
144 700, // period
145 50, // radius
146 30, // girth
147 0, // phase
148 10000); // rotation period (ms)
149 h2 = new Helix(
150 new Line(new PVector(100, 50, 70), new PVector(1,0,0)),
151 700,
152 50,
153 30,
154 PI,
155 10000);
156
157 // TODO(shaheen) calculate line segments between
158 // toroidal points selected by stepping the
159 // parameterized t value. select base pairs and
160 // associated colors. lerp between colors for each
161 // base pair to produce a DNA effect.
ee1f21c7 162
fd8a39b0 163 }
ee1f21c7 164
fd8a39b0
SG
165 void run(int deltaMs) {
166 boolean h1on = helix1On.getValue() > 0.5;
167 boolean h2on = helix2On.getValue() > 0.5;
ee1f21c7 168
fd8a39b0
SG
169 h1.step(deltaMs);
170 h2.step(deltaMs);
ee1f21c7 171
fd8a39b0
SG
172 for (Point p : model.points) {
173 color h1c = color(0,0,0);
174 color h2c = color(0,0,0);
ee1f21c7 175
fd8a39b0
SG
176 if (h1on) {
177 h1c = h1.colorOfPoint(new PVector(p.x,p.y,p.z));
178 }
ee1f21c7 179
fd8a39b0
SG
180 if (h2on) {
181 h2c = h2.colorOfPoint(new PVector(p.x,p.y,p.z));
182 }
ee1f21c7 183
fd8a39b0
SG
184 // The helices are positioned to not overlap. If that changes,
185 // a better blending formula is probably needed.
186 colors[p.index] = blendColor(h1c, h2c, ADD);
187 }
188 }
189}
190