2 import java.util.TreeMap;
5 class Gimbal extends SCPattern {
7 private final PerfTimer perf = new PerfTimer();
9 private final boolean DEBUG_MANUAL_ABG = false;
10 private final int MAXIMUM_BEATS_PER_REVOLUTION = 100;
12 private boolean first_run = true;
13 private final LXProjection projection;
14 private final BasicParameter beatsPerRevolutionParam = new BasicParameter("SLOW", 25./MAXIMUM_BEATS_PER_REVOLUTION);
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);
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);
28 projection = new LXProjection(model);
29 addParameter(beatsPerRevolutionParam);
30 addParameter(hueDeltaParam);
31 addParameter(fadeFromCoreParam);
32 addParameter(girthParam);
33 addParameter(ringExtendParam);
34 addParameter(relativeSpeedParam);
35 addParameter(sizeParam);
37 if (DEBUG_MANUAL_ABG) {
44 float a = 0, b = 0, g = 0;
46 public void run(double deltaMs) {
48 if (DEBUG_MANUAL_ABG) {
49 a = aP.getValuef() * (2 * PI);
50 b = bP.getValuef() * (2 * PI);
51 g = gP.getValuef() * (2 * PI);
53 float relativeSpeed = relativeSpeedParam.getValuef();
54 float time = millis() / 1000.f;
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
63 a += deltaMs * radiansPerMs * pow(relativeSpeed, 0);
64 b += deltaMs * radiansPerMs * pow(relativeSpeed, 1);
65 g += deltaMs * radiansPerMs * pow(relativeSpeed, 2);
71 float hue = lx.getBaseHuef();
72 float hue_delta = hueDeltaParam.getValuef() * 360;
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);
83 // Translate so the center of the car is the origin
86 for (LXVector c : projection) {
87 //if (first_run) println(c.x + "," + c.y + "," + c.z);
91 color color1 = ring1.colorFor(c);
94 color color2 = ring2.colorFor(c);
97 color color3 = ring3.colorFor(c);
99 colors[c.index] = specialBlend(color1, color2, color3);
111 public Ring(float hue, float radius, float girth) {
113 this.radius = radius;
115 this._multiplier = 100. / girth * fadeFromCoreParam.getValuef();
118 public color colorFor(LXVector c) {
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();
122 float distance_to_circle
123 = sqrt(xy_distance_to_circle * xy_distance_to_circle
124 + z_distance_to_circle * z_distance_to_circle);
126 return lx.hsb(this.hue, 100, 100. - distance_to_circle * _multiplier);
128 /* PRE-OPTIMIZED IMPLEMENTATION
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;
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));
139 float xy_distance = sqrt(c.x*c.x + c.y*c.y);
140 return lx.hsb(this.hue, 100, (1 - distance_to_circle / girth * fadeFromCoreParam.getValuef()) * 100);
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;
156 public void start(String phase_name) {
157 if (current_phase != null) {
160 current_phase = phase_name;
161 current_phase_start_time = millis();
165 int current_time = millis();
167 assert(current_phase != null);
168 assert(current_phase_start_time != 0);
169 int time_ellapsed = current_time - current_phase_start_time;
171 (m.containsKey(current_phase) ? m.get(current_phase) : 0)
174 current_phase = null;
175 current_phase_start_time = 0;
176 total_time += time_ellapsed;
179 public void report() {
180 if (random(0, 60 * 4) < 1) {
181 println("~~~~~~~~~~~~~~~~~~");
182 for (String phase_name : m.keySet()) {
184 for (int i = phase_name.length(); i < 30; ++i) {
187 println("" + (float) m.get(phase_name) / total_time);
199 class Zebra extends SCPattern {
201 private final LXProjection projection;
202 SinLFO angleM = new SinLFO(0, PI * 2, 30000);
205 SinLFO x, y, z, dx, dy, dz;
212 projection = new LXProjection(model);
214 addModulator(angleM).trigger();
217 color colorFor(LXVector c) {
218 float hue = lx.getBaseHuef();
224 c.x = c.x + millis() / 100.f;
229 int stripe_count = 12;
230 float stripe_width = model.xMax / (float)stripe_count;
231 if (Math.floor((c.x) / stripe_width) % 2 == 0) {
232 return lx.hsb(hue, 100, 100);
234 return lx.hsb((hue + 90) % 360, 100, 100);
240 if ((isPositiveBit(c.x) + isPositiveBit(c.y) + isPositiveBit(c.z)) % 2 == 0) {
241 return lx.hsb(lx.getBaseHuef(), 100, 100);
243 return lx.hsb(0, 0, 0);
248 int isPositiveBit(float f) {
249 return f > 0 ? 1 : 0;
252 public void run(double deltaMs) {
253 float a = (millis() / 1000.f) % (2 * PI);
254 float b = (millis() / 1200.f) % (2 * PI);
255 float g = (millis() / 1600.f) % (2 * PI);
258 // Translate so the center of the car is the origin
261 for (LXVector c : projection) {
262 // rotate3d(c, a, b, g);
263 colors[c.index] = colorFor(c);
271 boolean first_run = true;
272 private void log(String s) {
281 float HALF_OF_ONE_OVER_SQRT2_MINUS_1 = (1./sqrt(2) - 1) / 2;
282 float HALF_OF_ONE_OVER_SQRT2_PLUS_1 = (1./sqrt(2) + 1) / 2;
283 float SQRT2 = sqrt(2);
285 /** Equivalent to rotate3d(c, a, 0, 0); */
286 void rotate3dA(LXVector c, float a) {
287 float ox = c.x, oy = c.y;
290 c.x = ox * cosa - oy * sina;
291 c.y = ox * sina + oy * cosa;
293 /** Equivalent to rotate3d(c, 0, b, 0); */
294 void rotate3dB(LXVector c, float b) {
295 float ox = c.x, oz = c.z;
298 c.x = ox * cosb + oz * sinb;
299 c.z = oz * cosb - ox * sinb;
301 /** Equivalent to rotate3d(c, 0, 0, g); */
302 void rotate3dG(LXVector c, float g) {
303 float oy = c.y, oz = c.z;
306 c.y = oy * cosg - oz * sing;
307 c.z = oz * cosg + oy * sing;
309 /** Equivalent to rotate3d(c, PI/4, PI/4, PI/4); */
310 void 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;
317 void rotate3d(LXVector c, float a /* roll */, float b /* pitch */, float g /* yaw */) {
318 float ox = c.x, oy = c.y, oz = c.z;
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;
334 float c2 = cosb*sing;
335 float c3 = cosb*cosg;
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;
342 float dotProduct(float[] a, float[] b) {
344 for (int i = 0 ; i < a.length; ++i) {
350 color specialBlend(color c1, color c2, color c3) {
355 // force h1 < h2 < h3
363 float s1 = saturation(c1);
364 float s2 = saturation(c2);
365 float s3 = saturation(c3);
367 float b1 = brightness(c1);
368 float b2 = brightness(c2);
369 float b3 = brightness(c3);
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;
376 (h1 * relative_b1 + h2 * relative_b1 + h3 * relative_b3) % 360,
377 s1 * relative_b1 + s2 * relative_b2 + s3 * relative_b3,