New LX fixes FlashEffect bug
[SugarCubes.git] / JR.pde
CommitLineData
cc9e8c8b
BM
1import java.util.Map;
2import java.util.TreeMap;
a41f334c 3color BLACK = #000000;
e28f168c
AG
4
5class Gimbal extends SCPattern {
6
58888c69
JR
7 private final PerfTimer perf = new PerfTimer();
8
e28f168c
AG
9 private final boolean DEBUG_MANUAL_ABG = false;
10 private final int MAXIMUM_BEATS_PER_REVOLUTION = 100;
11
12 private boolean first_run = true;
9fa29818 13 private final LXProjection projection;
58888c69 14 private final BasicParameter beatsPerRevolutionParam = new BasicParameter("SLOW", 25./MAXIMUM_BEATS_PER_REVOLUTION);
e28f168c
AG
15 private final BasicParameter hueDeltaParam = new BasicParameter("HUED", 60./360);
16 private final BasicParameter fadeFromCoreParam = new BasicParameter("FADE", 1);
17 private final BasicParameter girthParam = new BasicParameter("GRTH", .18);
18 private final BasicParameter ringExtendParam = new BasicParameter("XTND", 1);
19 private final BasicParameter relativeSpeedParam = new BasicParameter("RLSP", .83);
20 private final BasicParameter sizeParam = new BasicParameter("SIZE", .9);
21
22 private final BasicParameter aP = new BasicParameter("a", 0);
23 private final BasicParameter bP = new BasicParameter("b", 0);
24 private final BasicParameter gP = new BasicParameter("g", 0);
25
dde75983
MS
26 Gimbal(LX lx) {
27 super(lx);
9fa29818 28 projection = new LXProjection(model);
e28f168c
AG
29 addParameter(beatsPerRevolutionParam);
30 addParameter(hueDeltaParam);
31 addParameter(fadeFromCoreParam);
32 addParameter(girthParam);
33 addParameter(ringExtendParam);
34 addParameter(relativeSpeedParam);
35 addParameter(sizeParam);
36
37 if (DEBUG_MANUAL_ABG) {
38 addParameter(aP);
39 addParameter(bP);
40 addParameter(gP);
41 }
42 }
43
44 float a = 0, b = 0, g = 0;
45
34327c96 46 public void run(double deltaMs) {
e28f168c
AG
47
48 if (DEBUG_MANUAL_ABG) {
49 a = aP.getValuef() * (2 * PI);
50 b = bP.getValuef() * (2 * PI);
51 g = gP.getValuef() * (2 * PI);
52 } else {
53 float relativeSpeed = relativeSpeedParam.getValuef();
54 float time = millis() / 1000.f;
55
56 int beatsPerRevolution = (int) (beatsPerRevolutionParam.getValuef() * MAXIMUM_BEATS_PER_REVOLUTION) + 1;
57 float radiansPerMs = 2 * PI // radians / revolution
58 / beatsPerRevolution // beats / revolution
59 * lx.tempo.bpmf() // BPM beats / min
60 / 60 // sec / min
61 / 1000; // ms / sec
62
63 a += deltaMs * radiansPerMs * pow(relativeSpeed, 0);
64 b += deltaMs * radiansPerMs * pow(relativeSpeed, 1);
65 g += deltaMs * radiansPerMs * pow(relativeSpeed, 2);
66 a %= 2 * PI;
67 b %= 2 * PI;
68 g %= 2 * PI;
69 }
70
71 float hue = lx.getBaseHuef();
72 float hue_delta = hueDeltaParam.getValuef() * 360;
73
74 float radius1 = model.xMax / 2 * sizeParam.getValuef();
75 float radius2 = ((model.xMax + model.yMax) / 2) / 2 * sizeParam.getValuef();
76 float radius3 = model.yMax / 2 * sizeParam.getValuef();
77 float girth = model.xMax * girthParam.getValuef();
78 Ring ring1 = new Ring((hue + hue_delta * 0) % 360, radius1, girth);
79 Ring ring2 = new Ring((hue + hue_delta * 1) % 360, radius2, girth);
80 Ring ring3 = new Ring((hue + hue_delta * 2) % 360, radius3, girth);
81
2bb56822 82 projection.reset()
e28f168c 83 // Translate so the center of the car is the origin
2bb56822 84 .center();
e28f168c 85
9fa29818 86 for (LXVector c : projection) {
e28f168c
AG
87 //if (first_run) println(c.x + "," + c.y + "," + c.z);
88
58888c69
JR
89 rotate3dA(c, a);
90 rotate3dPiOver4(c);
e28f168c
AG
91 color color1 = ring1.colorFor(c);
92
58888c69 93 rotate3dB(c, b);
e28f168c
AG
94 color color2 = ring2.colorFor(c);
95
58888c69 96 rotate3dG(c, g);
e28f168c
AG
97 color color3 = ring3.colorFor(c);
98
99 colors[c.index] = specialBlend(color1, color2, color3);
100 }
101
58888c69 102 first_run = false;
e28f168c
AG
103 }
104
105 class Ring {
106
107 float hue;
108 float radius, girth;
58888c69 109 float _multiplier;
e28f168c
AG
110
111 public Ring(float hue, float radius, float girth) {
112 this.hue = hue;
113 this.radius = radius;
114 this.girth = girth;
58888c69 115 this._multiplier = 100. / girth * fadeFromCoreParam.getValuef();
e28f168c
AG
116 }
117
9fa29818 118 public color colorFor(LXVector c) {
58888c69
JR
119 float xy_distance_to_circle = sqrt(c.x * c.x + c.y * c.y) - radius;
120 float z_distance_to_circle = c.z * ringExtendParam.getValuef();
121
122 float distance_to_circle
123 = sqrt(xy_distance_to_circle * xy_distance_to_circle
124 + z_distance_to_circle * z_distance_to_circle);
125
126 return lx.hsb(this.hue, 100, 100. - distance_to_circle * _multiplier);
127
128 /* PRE-OPTIMIZED IMPLEMENTATION
e28f168c
AG
129 float theta = atan2(c.y, c.x);
130 float nearest_circle_x = cos(theta) * radius;
131 float nearest_circle_y = sin(theta) * radius;
132 float nearest_circle_z = 0;
133
134 float distance_to_circle
135 = sqrt(pow(nearest_circle_x - c.x, 2)
136 + pow(nearest_circle_y - c.y, 2)
137 + pow(nearest_circle_z - c.z * ringExtendParam.getValuef(), 2));
138
139 float xy_distance = sqrt(c.x*c.x + c.y*c.y);
a41f334c 140 return lx.hsb(this.hue, 100, (1 - distance_to_circle / girth * fadeFromCoreParam.getValuef()) * 100);
58888c69 141 */
e28f168c 142 }
58888c69 143
e28f168c
AG
144 }
145
146}
147
148
58888c69
JR
149class PerfTimer {
150
151 private final Map<String, Integer> m = new TreeMap<String, Integer>();
152 private String current_phase = null;
153 private int current_phase_start_time = 0;
154 private int total_time = 0;
155
156 public void start(String phase_name) {
157 if (current_phase != null) {
158 stop();
159 }
160 current_phase = phase_name;
161 current_phase_start_time = millis();
162 }
163
164 public void stop() {
165 int current_time = millis();
166
167 assert(current_phase != null);
168 assert(current_phase_start_time != 0);
169 int time_ellapsed = current_time - current_phase_start_time;
170 m.put(current_phase,
171 (m.containsKey(current_phase) ? m.get(current_phase) : 0)
172 + time_ellapsed);
173
174 current_phase = null;
175 current_phase_start_time = 0;
176 total_time += time_ellapsed;
177 }
178
179 public void report() {
180 if (random(0, 60 * 4) < 1) {
181 println("~~~~~~~~~~~~~~~~~~");
182 for (String phase_name : m.keySet()) {
183 print(phase_name);
184 for (int i = phase_name.length(); i < 30; ++i) {
185 print(" ");
186 }
187 println("" + (float) m.get(phase_name) / total_time);
188 }
189 }
190 }
191
192}
193
194
e28f168c
AG
195
196
197
198
199class Zebra extends SCPattern {
200
9fa29818 201 private final LXProjection projection;
e28f168c
AG
202 SinLFO angleM = new SinLFO(0, PI * 2, 30000);
203
204/*
205 SinLFO x, y, z, dx, dy, dz;
206 float cRad;
207 _P size;
208 */
209
dde75983
MS
210 Zebra(LX lx) {
211 super(lx);
9fa29818 212 projection = new LXProjection(model);
e28f168c
AG
213
214 addModulator(angleM).trigger();
215 }
216
9fa29818 217 color colorFor(LXVector c) {
e28f168c
AG
218 float hue = lx.getBaseHuef();
219
220
221
222
223/* SLIDE ALONG
224 c.x = c.x + millis() / 100.f;
225 */
226
227
228
229 int stripe_count = 12;
230 float stripe_width = model.xMax / (float)stripe_count;
231 if (Math.floor((c.x) / stripe_width) % 2 == 0) {
a41f334c 232 return lx.hsb(hue, 100, 100);
e28f168c 233 } else {
a41f334c 234 return lx.hsb((hue + 90) % 360, 100, 100);
e28f168c
AG
235 }
236
237
238 /* OCTANTS
239
240 if ((isPositiveBit(c.x) + isPositiveBit(c.y) + isPositiveBit(c.z)) % 2 == 0) {
a41f334c 241 return lx.hsb(lx.getBaseHuef(), 100, 100);
e28f168c 242 } else {
a41f334c 243 return lx.hsb(0, 0, 0);
e28f168c
AG
244 }
245 */
246 }
247
248 int isPositiveBit(float f) {
249 return f > 0 ? 1 : 0;
250 }
251
34327c96 252 public void run(double deltaMs) {
e28f168c
AG
253 float a = (millis() / 1000.f) % (2 * PI);
254 float b = (millis() / 1200.f) % (2 * PI);
255 float g = (millis() / 1600.f) % (2 * PI);
256
2bb56822 257 projection.reset()
e28f168c 258 // Translate so the center of the car is the origin
2bb56822 259 .center();
e28f168c 260
9fa29818 261 for (LXVector c : projection) {
e28f168c
AG
262// rotate3d(c, a, b, g);
263 colors[c.index] = colorFor(c);
264 }
265
266 first_run = false;
267 }
268
269
270 // Utility!
271 boolean first_run = true;
272 private void log(String s) {
273 if (first_run) {
274 println(s);
275 }
276 }
277
278
279}
280
58888c69
JR
281float HALF_OF_ONE_OVER_SQRT2_MINUS_1 = (1./sqrt(2) - 1) / 2;
282float HALF_OF_ONE_OVER_SQRT2_PLUS_1 = (1./sqrt(2) + 1) / 2;
283float SQRT2 = sqrt(2);
284
285/** Equivalent to rotate3d(c, a, 0, 0); */
286void rotate3dA(LXVector c, float a) {
287 float ox = c.x, oy = c.y;
288 float cosa = cos(a);
289 float sina = sin(a);
290 c.x = ox * cosa - oy * sina;
291 c.y = ox * sina + oy * cosa;
292}
293/** Equivalent to rotate3d(c, 0, b, 0); */
294void rotate3dB(LXVector c, float b) {
295 float ox = c.x, oz = c.z;
296 float cosb = cos(b);
297 float sinb = sin(b);
298 c.x = ox * cosb + oz * sinb;
299 c.z = oz * cosb - ox * sinb;
300}
301/** Equivalent to rotate3d(c, 0, 0, g); */
302void rotate3dG(LXVector c, float g) {
303 float oy = c.y, oz = c.z;
304 float cosg = cos(g);
305 float sing = sin(g);
306 c.y = oy * cosg - oz * sing;
307 c.z = oz * cosg + oy * sing;
308}
309/** Equivalent to rotate3d(c, PI/4, PI/4, PI/4); */
310void rotate3dPiOver4(LXVector c) {
311 float ox = c.x, oy = c.y, oz = c.z;
312 c.x = ox / 2 + oy * HALF_OF_ONE_OVER_SQRT2_MINUS_1 + oz * HALF_OF_ONE_OVER_SQRT2_PLUS_1;
313 c.y = ox / 2 + oy * HALF_OF_ONE_OVER_SQRT2_PLUS_1 + oz * HALF_OF_ONE_OVER_SQRT2_MINUS_1;
314 c.z = - ox / SQRT2 + oy / 2 + oz / 2;
315}
316
9fa29818 317void rotate3d(LXVector c, float a /* roll */, float b /* pitch */, float g /* yaw */) {
58888c69
JR
318 float ox = c.x, oy = c.y, oz = c.z;
319
e28f168c
AG
320 float cosa = cos(a);
321 float cosb = cos(b);
322 float cosg = cos(g);
323 float sina = sin(a);
324 float sinb = sin(b);
325 float sing = sin(g);
326
327 float a1 = cosa*cosb;
328 float a2 = cosa*sinb*sing - sina*cosg;
329 float a3 = cosa*sinb*cosg + sina*sing;
330 float b1 = sina*cosb;
331 float b2 = sina*sinb*sing + cosa*cosg;
332 float b3 = sina*sinb*cosg - cosa*sing;
333 float c1 = -sinb;
334 float c2 = cosb*sing;
335 float c3 = cosb*cosg;
58888c69
JR
336
337 c.x = ox * a1 + oy * a2 + oz * a3;
338 c.y = ox * b1 + oy * b2 + oz * b3;
339 c.z = ox * c1 + oy * c2 + oz * c3;
e28f168c
AG
340}
341
342float dotProduct(float[] a, float[] b) {
343 float ret = 0;
344 for (int i = 0 ; i < a.length; ++i) {
345 ret += a[i] * b[i];
346 }
347 return ret;
348}
349
350color specialBlend(color c1, color c2, color c3) {
351 float h1 = hue(c1);
352 float h2 = hue(c2);
353 float h3 = hue(c3);
354
355 // force h1 < h2 < h3
356 while (h2 < h1) {
357 h2 += 360;
358 }
359 while (h3 < h2) {
360 h3 += 360;
361 }
362
363 float s1 = saturation(c1);
364 float s2 = saturation(c2);
365 float s3 = saturation(c3);
366
367 float b1 = brightness(c1);
368 float b2 = brightness(c2);
369 float b3 = brightness(c3);
58888c69
JR
370 float b_denominator = b1 + b2 + b3;
371 float relative_b1 = b1 / b_denominator;
372 float relative_b2 = b2 / b_denominator;
373 float relative_b3 = b3 / b_denominator;
e28f168c 374
a41f334c 375 return lx.hsb(
e28f168c
AG
376 (h1 * relative_b1 + h2 * relative_b1 + h3 * relative_b3) % 360,
377 s1 * relative_b1 + s2 * relative_b2 + s3 * relative_b3,
378 max(max(b1, b2), b3)
379 );
380}
381