Commit | Line | Data |
---|---|---|
a41f334c | 1 | color BLACK = #000000; |
e28f168c AG |
2 | |
3 | class Gimbal extends SCPattern { | |
4 | ||
5 | private final boolean DEBUG_MANUAL_ABG = false; | |
6 | private final int MAXIMUM_BEATS_PER_REVOLUTION = 100; | |
7 | ||
8 | private boolean first_run = true; | |
9fa29818 | 9 | private final LXProjection projection; |
e28f168c AG |
10 | private final BasicParameter beatsPerRevolutionParam = new BasicParameter("SLOW", 20./MAXIMUM_BEATS_PER_REVOLUTION); |
11 | private final BasicParameter hueDeltaParam = new BasicParameter("HUED", 60./360); | |
12 | private final BasicParameter fadeFromCoreParam = new BasicParameter("FADE", 1); | |
13 | private final BasicParameter girthParam = new BasicParameter("GRTH", .18); | |
14 | private final BasicParameter ringExtendParam = new BasicParameter("XTND", 1); | |
15 | private final BasicParameter relativeSpeedParam = new BasicParameter("RLSP", .83); | |
16 | private final BasicParameter sizeParam = new BasicParameter("SIZE", .9); | |
17 | ||
18 | private final BasicParameter aP = new BasicParameter("a", 0); | |
19 | private final BasicParameter bP = new BasicParameter("b", 0); | |
20 | private final BasicParameter gP = new BasicParameter("g", 0); | |
21 | ||
22 | Gimbal(GLucose glucose) { | |
23 | super(glucose); | |
9fa29818 | 24 | projection = new LXProjection(model); |
e28f168c AG |
25 | addParameter(beatsPerRevolutionParam); |
26 | addParameter(hueDeltaParam); | |
27 | addParameter(fadeFromCoreParam); | |
28 | addParameter(girthParam); | |
29 | addParameter(ringExtendParam); | |
30 | addParameter(relativeSpeedParam); | |
31 | addParameter(sizeParam); | |
32 | ||
33 | if (DEBUG_MANUAL_ABG) { | |
34 | addParameter(aP); | |
35 | addParameter(bP); | |
36 | addParameter(gP); | |
37 | } | |
38 | } | |
39 | ||
40 | float a = 0, b = 0, g = 0; | |
41 | ||
34327c96 | 42 | public void run(double deltaMs) { |
e28f168c AG |
43 | |
44 | if (DEBUG_MANUAL_ABG) { | |
45 | a = aP.getValuef() * (2 * PI); | |
46 | b = bP.getValuef() * (2 * PI); | |
47 | g = gP.getValuef() * (2 * PI); | |
48 | } else { | |
49 | float relativeSpeed = relativeSpeedParam.getValuef(); | |
50 | float time = millis() / 1000.f; | |
51 | ||
52 | int beatsPerRevolution = (int) (beatsPerRevolutionParam.getValuef() * MAXIMUM_BEATS_PER_REVOLUTION) + 1; | |
53 | float radiansPerMs = 2 * PI // radians / revolution | |
54 | / beatsPerRevolution // beats / revolution | |
55 | * lx.tempo.bpmf() // BPM beats / min | |
56 | / 60 // sec / min | |
57 | / 1000; // ms / sec | |
58 | ||
59 | a += deltaMs * radiansPerMs * pow(relativeSpeed, 0); | |
60 | b += deltaMs * radiansPerMs * pow(relativeSpeed, 1); | |
61 | g += deltaMs * radiansPerMs * pow(relativeSpeed, 2); | |
62 | a %= 2 * PI; | |
63 | b %= 2 * PI; | |
64 | g %= 2 * PI; | |
65 | } | |
66 | ||
67 | float hue = lx.getBaseHuef(); | |
68 | float hue_delta = hueDeltaParam.getValuef() * 360; | |
69 | ||
70 | float radius1 = model.xMax / 2 * sizeParam.getValuef(); | |
71 | float radius2 = ((model.xMax + model.yMax) / 2) / 2 * sizeParam.getValuef(); | |
72 | float radius3 = model.yMax / 2 * sizeParam.getValuef(); | |
73 | float girth = model.xMax * girthParam.getValuef(); | |
74 | Ring ring1 = new Ring((hue + hue_delta * 0) % 360, radius1, girth); | |
75 | Ring ring2 = new Ring((hue + hue_delta * 1) % 360, radius2, girth); | |
76 | Ring ring3 = new Ring((hue + hue_delta * 2) % 360, radius3, girth); | |
77 | ||
2bb56822 | 78 | projection.reset() |
e28f168c | 79 | // Translate so the center of the car is the origin |
2bb56822 | 80 | .center(); |
e28f168c | 81 | |
9fa29818 | 82 | for (LXVector c : projection) { |
e28f168c AG |
83 | //if (first_run) println(c.x + "," + c.y + "," + c.z); |
84 | ||
85 | rotate3d(c, a, 0, 0); | |
86 | rotate3d(c, PI/4, PI/4, PI/4); | |
87 | color color1 = ring1.colorFor(c); | |
88 | ||
89 | rotate3d(c, 0, b, 0); | |
90 | color color2 = ring2.colorFor(c); | |
91 | ||
92 | rotate3d(c, 0, 0, g); | |
93 | color color3 = ring3.colorFor(c); | |
94 | ||
95 | colors[c.index] = specialBlend(color1, color2, color3); | |
96 | } | |
97 | ||
98 | first_run = false; | |
99 | } | |
100 | ||
101 | class Ring { | |
102 | ||
103 | float hue; | |
104 | float radius, girth; | |
105 | ||
106 | public Ring(float hue, float radius, float girth) { | |
107 | this.hue = hue; | |
108 | this.radius = radius; | |
109 | this.girth = girth; | |
110 | } | |
111 | ||
9fa29818 | 112 | public color colorFor(LXVector c) { |
e28f168c AG |
113 | float theta = atan2(c.y, c.x); |
114 | float nearest_circle_x = cos(theta) * radius; | |
115 | float nearest_circle_y = sin(theta) * radius; | |
116 | float nearest_circle_z = 0; | |
117 | ||
118 | float distance_to_circle | |
119 | = sqrt(pow(nearest_circle_x - c.x, 2) | |
120 | + pow(nearest_circle_y - c.y, 2) | |
121 | + pow(nearest_circle_z - c.z * ringExtendParam.getValuef(), 2)); | |
122 | ||
123 | float xy_distance = sqrt(c.x*c.x + c.y*c.y); | |
a41f334c | 124 | return lx.hsb(this.hue, 100, (1 - distance_to_circle / girth * fadeFromCoreParam.getValuef()) * 100); |
e28f168c AG |
125 | } |
126 | ||
127 | } | |
128 | ||
129 | } | |
130 | ||
131 | ||
132 | ||
133 | ||
134 | ||
135 | ||
136 | class Zebra extends SCPattern { | |
137 | ||
9fa29818 | 138 | private final LXProjection projection; |
e28f168c AG |
139 | SinLFO angleM = new SinLFO(0, PI * 2, 30000); |
140 | ||
141 | /* | |
142 | SinLFO x, y, z, dx, dy, dz; | |
143 | float cRad; | |
144 | _P size; | |
145 | */ | |
146 | ||
147 | Zebra(GLucose glucose) { | |
148 | super(glucose); | |
9fa29818 | 149 | projection = new LXProjection(model); |
e28f168c AG |
150 | |
151 | addModulator(angleM).trigger(); | |
152 | } | |
153 | ||
9fa29818 | 154 | color colorFor(LXVector c) { |
e28f168c AG |
155 | float hue = lx.getBaseHuef(); |
156 | ||
157 | ||
158 | ||
159 | ||
160 | /* SLIDE ALONG | |
161 | c.x = c.x + millis() / 100.f; | |
162 | */ | |
163 | ||
164 | ||
165 | ||
166 | int stripe_count = 12; | |
167 | float stripe_width = model.xMax / (float)stripe_count; | |
168 | if (Math.floor((c.x) / stripe_width) % 2 == 0) { | |
a41f334c | 169 | return lx.hsb(hue, 100, 100); |
e28f168c | 170 | } else { |
a41f334c | 171 | return lx.hsb((hue + 90) % 360, 100, 100); |
e28f168c AG |
172 | } |
173 | ||
174 | ||
175 | /* OCTANTS | |
176 | ||
177 | if ((isPositiveBit(c.x) + isPositiveBit(c.y) + isPositiveBit(c.z)) % 2 == 0) { | |
a41f334c | 178 | return lx.hsb(lx.getBaseHuef(), 100, 100); |
e28f168c | 179 | } else { |
a41f334c | 180 | return lx.hsb(0, 0, 0); |
e28f168c AG |
181 | } |
182 | */ | |
183 | } | |
184 | ||
185 | int isPositiveBit(float f) { | |
186 | return f > 0 ? 1 : 0; | |
187 | } | |
188 | ||
34327c96 | 189 | public void run(double deltaMs) { |
e28f168c AG |
190 | float a = (millis() / 1000.f) % (2 * PI); |
191 | float b = (millis() / 1200.f) % (2 * PI); | |
192 | float g = (millis() / 1600.f) % (2 * PI); | |
193 | ||
2bb56822 | 194 | projection.reset() |
e28f168c | 195 | // Translate so the center of the car is the origin |
2bb56822 | 196 | .center(); |
e28f168c | 197 | |
9fa29818 | 198 | for (LXVector c : projection) { |
e28f168c AG |
199 | // rotate3d(c, a, b, g); |
200 | colors[c.index] = colorFor(c); | |
201 | } | |
202 | ||
203 | first_run = false; | |
204 | } | |
205 | ||
206 | ||
207 | // Utility! | |
208 | boolean first_run = true; | |
209 | private void log(String s) { | |
210 | if (first_run) { | |
211 | println(s); | |
212 | } | |
213 | } | |
214 | ||
215 | ||
216 | } | |
217 | ||
9fa29818 | 218 | void rotate3d(LXVector c, float a /* roll */, float b /* pitch */, float g /* yaw */) { |
e28f168c AG |
219 | float cosa = cos(a); |
220 | float cosb = cos(b); | |
221 | float cosg = cos(g); | |
222 | float sina = sin(a); | |
223 | float sinb = sin(b); | |
224 | float sing = sin(g); | |
225 | ||
226 | float a1 = cosa*cosb; | |
227 | float a2 = cosa*sinb*sing - sina*cosg; | |
228 | float a3 = cosa*sinb*cosg + sina*sing; | |
229 | float b1 = sina*cosb; | |
230 | float b2 = sina*sinb*sing + cosa*cosg; | |
231 | float b3 = sina*sinb*cosg - cosa*sing; | |
232 | float c1 = -sinb; | |
233 | float c2 = cosb*sing; | |
234 | float c3 = cosb*cosg; | |
235 | ||
236 | float[] cArray = { c.x, c.y, c.z }; | |
237 | c.x = dotProduct(new float[] {a1, a2, a3}, cArray); | |
238 | c.y = dotProduct(new float[] {b1, b2, b3}, cArray); | |
239 | c.z = dotProduct(new float[] {c1, c2, c3}, cArray); | |
240 | } | |
241 | ||
242 | float dotProduct(float[] a, float[] b) { | |
243 | float ret = 0; | |
244 | for (int i = 0 ; i < a.length; ++i) { | |
245 | ret += a[i] * b[i]; | |
246 | } | |
247 | return ret; | |
248 | } | |
249 | ||
250 | color specialBlend(color c1, color c2, color c3) { | |
251 | float h1 = hue(c1); | |
252 | float h2 = hue(c2); | |
253 | float h3 = hue(c3); | |
254 | ||
255 | // force h1 < h2 < h3 | |
256 | while (h2 < h1) { | |
257 | h2 += 360; | |
258 | } | |
259 | while (h3 < h2) { | |
260 | h3 += 360; | |
261 | } | |
262 | ||
263 | float s1 = saturation(c1); | |
264 | float s2 = saturation(c2); | |
265 | float s3 = saturation(c3); | |
266 | ||
267 | float b1 = brightness(c1); | |
268 | float b2 = brightness(c2); | |
269 | float b3 = brightness(c3); | |
270 | float relative_b1 = b1 / (b1 + b2 + b3); | |
271 | float relative_b2 = b2 / (b1 + b2 + b3); | |
272 | float relative_b3 = b3 / (b1 + b2 + b3); | |
273 | ||
a41f334c | 274 | return lx.hsb( |
e28f168c AG |
275 | (h1 * relative_b1 + h2 * relative_b1 + h3 * relative_b3) % 360, |
276 | s1 * relative_b1 + s2 * relative_b2 + s3 * relative_b3, | |
277 | max(max(b1, b2), b3) | |
278 | ); | |
279 | } | |
280 |