Optimized gimbal -- now at 60fps
authorJustin Rosenstein <jr@asana.com>
Mon, 2 Dec 2013 02:48:21 +0000 (18:48 -0800)
committerJustin Rosenstein <jr@asana.com>
Mon, 2 Dec 2013 02:48:21 +0000 (18:48 -0800)
JR.pde
SugarCubes.pde

diff --git a/JR.pde b/JR.pde
index ba9498b57598321b9a4f3ddb843b4cf420228ca6..025d74f259cc0b95ecfd56695a3e7320fe563f6b 100644 (file)
--- a/JR.pde
+++ b/JR.pde
@@ -2,12 +2,14 @@ color BLACK = #000000;
 
 class Gimbal extends SCPattern {
 
 
 class Gimbal extends SCPattern {
 
+  private final PerfTimer perf = new PerfTimer();
+  
   private final boolean DEBUG_MANUAL_ABG = false;
   private final int MAXIMUM_BEATS_PER_REVOLUTION = 100;
   
   private boolean first_run = true;
   private final LXProjection projection;
   private final boolean DEBUG_MANUAL_ABG = false;
   private final int MAXIMUM_BEATS_PER_REVOLUTION = 100;
   
   private boolean first_run = true;
   private final LXProjection projection;
-  private final BasicParameter beatsPerRevolutionParam = new BasicParameter("SLOW", 20./MAXIMUM_BEATS_PER_REVOLUTION);
+  private final BasicParameter beatsPerRevolutionParam = new BasicParameter("SLOW", 25./MAXIMUM_BEATS_PER_REVOLUTION);
   private final BasicParameter hueDeltaParam = new BasicParameter("HUED", 60./360);
   private final BasicParameter fadeFromCoreParam = new BasicParameter("FADE", 1);
   private final BasicParameter girthParam = new BasicParameter("GRTH", .18);
   private final BasicParameter hueDeltaParam = new BasicParameter("HUED", 60./360);
   private final BasicParameter fadeFromCoreParam = new BasicParameter("FADE", 1);
   private final BasicParameter girthParam = new BasicParameter("GRTH", .18);
@@ -82,34 +84,46 @@ class Gimbal extends SCPattern {
     for (LXVector c : projection) {
       //if (first_run) println(c.x + "," + c.y + "," + c.z);
 
     for (LXVector c : projection) {
       //if (first_run) println(c.x + "," + c.y + "," + c.z);
 
-      rotate3d(c, a, 0, 0);
-      rotate3d(c, PI/4, PI/4, PI/4);
+      rotate3dA(c, a);
+      rotate3dPiOver4(c);
       color color1 = ring1.colorFor(c);
 
       color color1 = ring1.colorFor(c);
 
-      rotate3d(c, 0, b, 0);
+      rotate3dB(c, b);
       color color2 = ring2.colorFor(c);
 
       color color2 = ring2.colorFor(c);
 
-      rotate3d(c, 0, 0, g);
+      rotate3dG(c, g);
       color color3 = ring3.colorFor(c);
             
       colors[c.index] = specialBlend(color1, color2, color3);      
     }
 
       color color3 = ring3.colorFor(c);
             
       colors[c.index] = specialBlend(color1, color2, color3);      
     }
 
-    first_run = false;
+    first_run = false;    
   }
 
   class Ring {
 
     float hue;
     float radius, girth;
   }
 
   class Ring {
 
     float hue;
     float radius, girth;
+    float _multiplier;
 
     public Ring(float hue, float radius, float girth) {
       this.hue = hue;
       this.radius = radius;
       this.girth = girth;
 
     public Ring(float hue, float radius, float girth) {
       this.hue = hue;
       this.radius = radius;
       this.girth = girth;
+      this._multiplier = 100. / girth * fadeFromCoreParam.getValuef();
     }
 
     public color colorFor(LXVector c) {
     }
 
     public color colorFor(LXVector c) {
+      float xy_distance_to_circle = sqrt(c.x * c.x + c.y * c.y) - radius;
+      float z_distance_to_circle = c.z * ringExtendParam.getValuef();
+      
+      float distance_to_circle
+          = sqrt(xy_distance_to_circle * xy_distance_to_circle
+               + z_distance_to_circle * z_distance_to_circle); 
+
+      return lx.hsb(this.hue, 100, 100. - distance_to_circle * _multiplier);
+
+      /* PRE-OPTIMIZED IMPLEMENTATION
       float theta = atan2(c.y, c.x);
       float nearest_circle_x = cos(theta) * radius;
       float nearest_circle_y = sin(theta) * radius;
       float theta = atan2(c.y, c.x);
       float nearest_circle_x = cos(theta) * radius;
       float nearest_circle_y = sin(theta) * radius;
@@ -122,13 +136,60 @@ class Gimbal extends SCPattern {
 
       float xy_distance = sqrt(c.x*c.x + c.y*c.y);
       return lx.hsb(this.hue, 100, (1 - distance_to_circle / girth * fadeFromCoreParam.getValuef()) * 100);
 
       float xy_distance = sqrt(c.x*c.x + c.y*c.y);
       return lx.hsb(this.hue, 100, (1 - distance_to_circle / girth * fadeFromCoreParam.getValuef()) * 100);
+      */
     }
     }
-
+    
   }
 
 }
 
 
   }
 
 }
 
 
+class PerfTimer {
+  
+  private final Map<String, Integer> m = new TreeMap<String, Integer>();
+  private String current_phase = null;
+  private int current_phase_start_time = 0;
+  private int total_time = 0;
+  
+  public void start(String phase_name) {
+    if (current_phase != null) {
+      stop();
+    }
+    current_phase = phase_name;
+    current_phase_start_time = millis();
+  }
+  
+  public void stop() {
+    int current_time = millis();
+    
+    assert(current_phase != null);
+    assert(current_phase_start_time != 0);
+    int time_ellapsed = current_time - current_phase_start_time;
+    m.put(current_phase,
+        (m.containsKey(current_phase) ? m.get(current_phase) : 0)
+        + time_ellapsed);
+
+    current_phase = null;
+    current_phase_start_time = 0;
+    total_time += time_ellapsed;
+  }
+  
+  public void report() {
+    if (random(0, 60 * 4) < 1) {
+      println("~~~~~~~~~~~~~~~~~~");
+      for (String phase_name : m.keySet()) {
+        print(phase_name);
+        for (int i = phase_name.length(); i < 30; ++i) {
+          print(" ");
+        }
+        println("" + (float) m.get(phase_name) / total_time);
+      }
+    }
+  }
+  
+}
+
+
 
 
 
 
 
 
@@ -215,7 +276,45 @@ class Zebra extends SCPattern {
 
 }
 
 
 }
 
+float HALF_OF_ONE_OVER_SQRT2_MINUS_1 = (1./sqrt(2) - 1) / 2;
+float HALF_OF_ONE_OVER_SQRT2_PLUS_1  = (1./sqrt(2) + 1) / 2;
+float SQRT2 = sqrt(2);
+
+/** Equivalent to rotate3d(c, a, 0, 0); */
+void rotate3dA(LXVector c, float a) {
+  float ox = c.x, oy = c.y;
+  float cosa = cos(a);
+  float sina = sin(a);
+  c.x = ox * cosa - oy * sina;
+  c.y = ox * sina + oy * cosa;
+}
+/** Equivalent to rotate3d(c, 0, b, 0); */
+void rotate3dB(LXVector c, float b) {
+  float ox = c.x, oz = c.z;
+  float cosb = cos(b);
+  float sinb = sin(b);
+  c.x = ox * cosb + oz * sinb;
+  c.z = oz * cosb - ox * sinb;
+}
+/** Equivalent to rotate3d(c, 0, 0, g); */
+void rotate3dG(LXVector c, float g) {
+  float oy = c.y, oz = c.z;
+  float cosg = cos(g);
+  float sing = sin(g);
+  c.y = oy * cosg - oz * sing;
+  c.z = oz * cosg + oy * sing;
+}
+/** Equivalent to rotate3d(c, PI/4, PI/4, PI/4); */
+void rotate3dPiOver4(LXVector c) {
+  float ox = c.x, oy = c.y, oz = c.z;
+  c.x = ox / 2 + oy * HALF_OF_ONE_OVER_SQRT2_MINUS_1 + oz * HALF_OF_ONE_OVER_SQRT2_PLUS_1;
+  c.y = ox / 2 + oy * HALF_OF_ONE_OVER_SQRT2_PLUS_1 + oz * HALF_OF_ONE_OVER_SQRT2_MINUS_1;
+  c.z = - ox / SQRT2 + oy / 2 + oz / 2;
+}
+
 void rotate3d(LXVector c, float a /* roll */, float b /* pitch */, float g /* yaw */) {
 void rotate3d(LXVector c, float a /* roll */, float b /* pitch */, float g /* yaw */) {
+  float ox = c.x, oy = c.y, oz = c.z;
+
   float cosa = cos(a);
   float cosb = cos(b);
   float cosg = cos(g);
   float cosa = cos(a);
   float cosb = cos(b);
   float cosg = cos(g);
@@ -232,11 +331,10 @@ void rotate3d(LXVector c, float a /* roll */, float b /* pitch */, float g /* ya
   float c1 = -sinb;
   float c2 = cosb*sing;
   float c3 = cosb*cosg;
   float c1 = -sinb;
   float c2 = cosb*sing;
   float c3 = cosb*cosg;
-
-  float[] cArray = { c.x, c.y, c.z };
-  c.x = dotProduct(new float[] {a1, a2, a3}, cArray);
-  c.y = dotProduct(new float[] {b1, b2, b3}, cArray);
-  c.z = dotProduct(new float[] {c1, c2, c3}, cArray);
+  
+  c.x = ox * a1 + oy * a2 + oz * a3;
+  c.y = ox * b1 + oy * b2 + oz * b3;
+  c.z = ox * c1 + oy * c2 + oz * c3;  
 }
 
 float dotProduct(float[] a, float[] b) {
 }
 
 float dotProduct(float[] a, float[] b) {
@@ -267,9 +365,10 @@ color specialBlend(color c1, color c2, color c3) {
   float b1 = brightness(c1); 
   float b2 = brightness(c2);
   float b3 = brightness(c3);
   float b1 = brightness(c1); 
   float b2 = brightness(c2);
   float b3 = brightness(c3);
-  float relative_b1 = b1 / (b1 + b2 + b3);
-  float relative_b2 = b2 / (b1 + b2 + b3);
-  float relative_b3 = b3 / (b1 + b2 + b3);
+  float b_denominator = b1 + b2 + b3;
+  float relative_b1 = b1 / b_denominator;
+  float relative_b2 = b2 / b_denominator;
+  float relative_b3 = b3 / b_denominator;
   
   return lx.hsb(
     (h1 * relative_b1 + h2 * relative_b1 + h3 * relative_b3) % 360,
   
   return lx.hsb(
     (h1 * relative_b1 + h2 * relative_b1 + h3 * relative_b3) % 360,
index 71f846e5926e8bb58eaf12658f529a1d45d2cbb9..ff85065f00b8519e045ab2025606a0d76aa09d6d 100644 (file)
@@ -60,6 +60,9 @@ LXPattern[] patterns(GLucose glucose) {
     new Pong (glucose),
     new Worms(glucose),
 
     new Pong (glucose),
     new Worms(glucose),
 
+    // JR
+    new Gimbal(glucose),
+    
     // Alex G
      
      // Tim
     // Alex G
      
      // Tim
@@ -107,9 +110,6 @@ LXPattern[] patterns(GLucose glucose) {
     // Shaheen
     //new HelixPattern(glucose).setEligible(false),
     
     // Shaheen
     //new HelixPattern(glucose).setEligible(false),
     
-    //JR
-    new Gimbal(glucose),
-    
     // Sam
     new JazzRainbow(glucose),
     
     // Sam
     new JazzRainbow(glucose),