3 class Gimbal extends SCPattern {
5 private final PerfTimer perf = new PerfTimer();
7 private final boolean DEBUG_MANUAL_ABG = false;
8 private final int MAXIMUM_BEATS_PER_REVOLUTION = 100;
10 private boolean first_run = true;
11 private final LXProjection projection;
12 private final BasicParameter beatsPerRevolutionParam = new BasicParameter("SLOW", 25./MAXIMUM_BEATS_PER_REVOLUTION);
13 private final BasicParameter hueDeltaParam = new BasicParameter("HUED", 60./360);
14 private final BasicParameter fadeFromCoreParam = new BasicParameter("FADE", 1);
15 private final BasicParameter girthParam = new BasicParameter("GRTH", .18);
16 private final BasicParameter ringExtendParam = new BasicParameter("XTND", 1);
17 private final BasicParameter relativeSpeedParam = new BasicParameter("RLSP", .83);
18 private final BasicParameter sizeParam = new BasicParameter("SIZE", .9);
20 private final BasicParameter aP = new BasicParameter("a", 0);
21 private final BasicParameter bP = new BasicParameter("b", 0);
22 private final BasicParameter gP = new BasicParameter("g", 0);
24 Gimbal(GLucose glucose) {
26 projection = new LXProjection(model);
27 addParameter(beatsPerRevolutionParam);
28 addParameter(hueDeltaParam);
29 addParameter(fadeFromCoreParam);
30 addParameter(girthParam);
31 addParameter(ringExtendParam);
32 addParameter(relativeSpeedParam);
33 addParameter(sizeParam);
35 if (DEBUG_MANUAL_ABG) {
42 float a = 0, b = 0, g = 0;
44 public void run(double deltaMs) {
46 if (DEBUG_MANUAL_ABG) {
47 a = aP.getValuef() * (2 * PI);
48 b = bP.getValuef() * (2 * PI);
49 g = gP.getValuef() * (2 * PI);
51 float relativeSpeed = relativeSpeedParam.getValuef();
52 float time = millis() / 1000.f;
54 int beatsPerRevolution = (int) (beatsPerRevolutionParam.getValuef() * MAXIMUM_BEATS_PER_REVOLUTION) + 1;
55 float radiansPerMs = 2 * PI // radians / revolution
56 / beatsPerRevolution // beats / revolution
57 * lx.tempo.bpmf() // BPM beats / min
61 a += deltaMs * radiansPerMs * pow(relativeSpeed, 0);
62 b += deltaMs * radiansPerMs * pow(relativeSpeed, 1);
63 g += deltaMs * radiansPerMs * pow(relativeSpeed, 2);
69 float hue = lx.getBaseHuef();
70 float hue_delta = hueDeltaParam.getValuef() * 360;
72 float radius1 = model.xMax / 2 * sizeParam.getValuef();
73 float radius2 = ((model.xMax + model.yMax) / 2) / 2 * sizeParam.getValuef();
74 float radius3 = model.yMax / 2 * sizeParam.getValuef();
75 float girth = model.xMax * girthParam.getValuef();
76 Ring ring1 = new Ring((hue + hue_delta * 0) % 360, radius1, girth);
77 Ring ring2 = new Ring((hue + hue_delta * 1) % 360, radius2, girth);
78 Ring ring3 = new Ring((hue + hue_delta * 2) % 360, radius3, girth);
81 // Translate so the center of the car is the origin
84 for (LXVector c : projection) {
85 //if (first_run) println(c.x + "," + c.y + "," + c.z);
89 color color1 = ring1.colorFor(c);
92 color color2 = ring2.colorFor(c);
95 color color3 = ring3.colorFor(c);
97 colors[c.index] = specialBlend(color1, color2, color3);
109 public Ring(float hue, float radius, float girth) {
111 this.radius = radius;
113 this._multiplier = 100. / girth * fadeFromCoreParam.getValuef();
116 public color colorFor(LXVector c) {
117 float xy_distance_to_circle = sqrt(c.x * c.x + c.y * c.y) - radius;
118 float z_distance_to_circle = c.z * ringExtendParam.getValuef();
120 float distance_to_circle
121 = sqrt(xy_distance_to_circle * xy_distance_to_circle
122 + z_distance_to_circle * z_distance_to_circle);
124 return lx.hsb(this.hue, 100, 100. - distance_to_circle * _multiplier);
126 /* PRE-OPTIMIZED IMPLEMENTATION
127 float theta = atan2(c.y, c.x);
128 float nearest_circle_x = cos(theta) * radius;
129 float nearest_circle_y = sin(theta) * radius;
130 float nearest_circle_z = 0;
132 float distance_to_circle
133 = sqrt(pow(nearest_circle_x - c.x, 2)
134 + pow(nearest_circle_y - c.y, 2)
135 + pow(nearest_circle_z - c.z * ringExtendParam.getValuef(), 2));
137 float xy_distance = sqrt(c.x*c.x + c.y*c.y);
138 return lx.hsb(this.hue, 100, (1 - distance_to_circle / girth * fadeFromCoreParam.getValuef()) * 100);
149 private final Map<String, Integer> m = new TreeMap<String, Integer>();
150 private String current_phase = null;
151 private int current_phase_start_time = 0;
152 private int total_time = 0;
154 public void start(String phase_name) {
155 if (current_phase != null) {
158 current_phase = phase_name;
159 current_phase_start_time = millis();
163 int current_time = millis();
165 assert(current_phase != null);
166 assert(current_phase_start_time != 0);
167 int time_ellapsed = current_time - current_phase_start_time;
169 (m.containsKey(current_phase) ? m.get(current_phase) : 0)
172 current_phase = null;
173 current_phase_start_time = 0;
174 total_time += time_ellapsed;
177 public void report() {
178 if (random(0, 60 * 4) < 1) {
179 println("~~~~~~~~~~~~~~~~~~");
180 for (String phase_name : m.keySet()) {
182 for (int i = phase_name.length(); i < 30; ++i) {
185 println("" + (float) m.get(phase_name) / total_time);
197 class Zebra extends SCPattern {
199 private final LXProjection projection;
200 SinLFO angleM = new SinLFO(0, PI * 2, 30000);
203 SinLFO x, y, z, dx, dy, dz;
208 Zebra(GLucose glucose) {
210 projection = new LXProjection(model);
212 addModulator(angleM).trigger();
215 color colorFor(LXVector c) {
216 float hue = lx.getBaseHuef();
222 c.x = c.x + millis() / 100.f;
227 int stripe_count = 12;
228 float stripe_width = model.xMax / (float)stripe_count;
229 if (Math.floor((c.x) / stripe_width) % 2 == 0) {
230 return lx.hsb(hue, 100, 100);
232 return lx.hsb((hue + 90) % 360, 100, 100);
238 if ((isPositiveBit(c.x) + isPositiveBit(c.y) + isPositiveBit(c.z)) % 2 == 0) {
239 return lx.hsb(lx.getBaseHuef(), 100, 100);
241 return lx.hsb(0, 0, 0);
246 int isPositiveBit(float f) {
247 return f > 0 ? 1 : 0;
250 public void run(double deltaMs) {
251 float a = (millis() / 1000.f) % (2 * PI);
252 float b = (millis() / 1200.f) % (2 * PI);
253 float g = (millis() / 1600.f) % (2 * PI);
256 // Translate so the center of the car is the origin
259 for (LXVector c : projection) {
260 // rotate3d(c, a, b, g);
261 colors[c.index] = colorFor(c);
269 boolean first_run = true;
270 private void log(String s) {
279 float HALF_OF_ONE_OVER_SQRT2_MINUS_1 = (1./sqrt(2) - 1) / 2;
280 float HALF_OF_ONE_OVER_SQRT2_PLUS_1 = (1./sqrt(2) + 1) / 2;
281 float SQRT2 = sqrt(2);
283 /** Equivalent to rotate3d(c, a, 0, 0); */
284 void rotate3dA(LXVector c, float a) {
285 float ox = c.x, oy = c.y;
288 c.x = ox * cosa - oy * sina;
289 c.y = ox * sina + oy * cosa;
291 /** Equivalent to rotate3d(c, 0, b, 0); */
292 void rotate3dB(LXVector c, float b) {
293 float ox = c.x, oz = c.z;
296 c.x = ox * cosb + oz * sinb;
297 c.z = oz * cosb - ox * sinb;
299 /** Equivalent to rotate3d(c, 0, 0, g); */
300 void rotate3dG(LXVector c, float g) {
301 float oy = c.y, oz = c.z;
304 c.y = oy * cosg - oz * sing;
305 c.z = oz * cosg + oy * sing;
307 /** Equivalent to rotate3d(c, PI/4, PI/4, PI/4); */
308 void rotate3dPiOver4(LXVector c) {
309 float ox = c.x, oy = c.y, oz = c.z;
310 c.x = ox / 2 + oy * HALF_OF_ONE_OVER_SQRT2_MINUS_1 + oz * HALF_OF_ONE_OVER_SQRT2_PLUS_1;
311 c.y = ox / 2 + oy * HALF_OF_ONE_OVER_SQRT2_PLUS_1 + oz * HALF_OF_ONE_OVER_SQRT2_MINUS_1;
312 c.z = - ox / SQRT2 + oy / 2 + oz / 2;
315 void rotate3d(LXVector c, float a /* roll */, float b /* pitch */, float g /* yaw */) {
316 float ox = c.x, oy = c.y, oz = c.z;
325 float a1 = cosa*cosb;
326 float a2 = cosa*sinb*sing - sina*cosg;
327 float a3 = cosa*sinb*cosg + sina*sing;
328 float b1 = sina*cosb;
329 float b2 = sina*sinb*sing + cosa*cosg;
330 float b3 = sina*sinb*cosg - cosa*sing;
332 float c2 = cosb*sing;
333 float c3 = cosb*cosg;
335 c.x = ox * a1 + oy * a2 + oz * a3;
336 c.y = ox * b1 + oy * b2 + oz * b3;
337 c.z = ox * c1 + oy * c2 + oz * c3;
340 float dotProduct(float[] a, float[] b) {
342 for (int i = 0 ; i < a.length; ++i) {
348 color specialBlend(color c1, color c2, color c3) {
353 // force h1 < h2 < h3
361 float s1 = saturation(c1);
362 float s2 = saturation(c2);
363 float s3 = saturation(c3);
365 float b1 = brightness(c1);
366 float b2 = brightness(c2);
367 float b3 = brightness(c3);
368 float b_denominator = b1 + b2 + b3;
369 float relative_b1 = b1 / b_denominator;
370 float relative_b2 = b2 / b_denominator;
371 float relative_b3 = b3 / b_denominator;
374 (h1 * relative_b1 + h2 * relative_b1 + h3 * relative_b3) % 360,
375 s1 * relative_b1 + s2 * relative_b2 + s3 * relative_b3,