Apat and added acos to spherycolor, not included in color yet but working
[SugarCubes.git] / TimBavaro.pde
index d0fc6e5f7c0863c35674da2cb2af46501318e5f2..3fcd5b160353986498ff01793cd8cf64c5d05bf9 100644 (file)
@@ -3,8 +3,9 @@
  */
 class TimSpheres extends SCPattern {
   private BasicParameter hueParameter = new BasicParameter("RAD", 1.0);
+  private BasicParameter periodParameter = new BasicParameter("PERIOD", 4000.0, 200.0, 10000.0);
   private final SawLFO lfo = new SawLFO(0, 1, 10000);
-  private final SinLFO sinLfo = new SinLFO(0, 1, 4000);
+  private final SinLFO sinLfo = new SinLFO(0, 1, periodParameter);
   private final float centerX, centerY, centerZ;
   
   class Sphere {
@@ -18,6 +19,7 @@ class TimSpheres extends SCPattern {
   public TimSpheres(GLucose glucose) {
     super(glucose);
     addParameter(hueParameter);
+    addParameter(periodParameter);
     addModulator(lfo).trigger();
     addModulator(sinLfo).trigger();
     centerX = (model.xMax + model.xMin) / 2;
@@ -41,7 +43,7 @@ class TimSpheres extends SCPattern {
     spheres[1].radius = 50;
   }
   
-  public void run(int deltaMs) {
+  public void run(double deltaMs) {
     // Access the core master hue via this method call
     float hv = hueParameter.getValuef();
     float lfoValue = lfo.getValuef();
@@ -53,16 +55,16 @@ class TimSpheres extends SCPattern {
     spheres[0].radius = 100 * hueParameter.getValuef();
     spheres[1].radius = 100 * hueParameter.getValuef();
     
-    for (Point p : model.points) {
+    for (LXPoint p : model.points) {
       float value = 0;
 
-      color c = color(0, 0, 0);      
+      color c = lx.hsb(0, 0, 0);      
       for (Sphere s : spheres) {
         float d = sqrt(pow(p.x - s.x, 2) + pow(p.y - s.y, 2) + pow(p.z - s.z, 2));
         float r = (s.radius); // * (sinLfoValue + 0.5));
         value = max(0, 1 - max(0, d - r) / 10);
         
-        c = blendColor(c, color(((s.hue + lfoValue) % 1) * 360, 100, min(1, value) * 100), ADD);
+        c = blendColor(c, lx.hsb(((s.hue + lfoValue) % 1) * 360, 100, min(1, value) * 100), ADD);
       }
       
       colors[p.index] = c;
@@ -124,8 +126,8 @@ class Vector3 {
     return distanceTo(v.x, v.y, v.z);
   }
   
-  float distanceTo(Point p) {
-    return distanceTo(p.fx, p.fy, p.fz);
+  float distanceTo(LXPoint p) {
+    return distanceTo(p.x, p.y, p.z);
   }
   
   void add(Vector3 other, float multiplier) {
@@ -224,8 +226,8 @@ class TimRaindrops extends SCPattern {
     }
     
     // returns TRUE when this should die
-    boolean age(int ms) {
-      p.add(v, ms / 1000.0);
+    boolean age(double ms) {
+      p.add(v, (float) (ms / 1000.0));
       return this.p.y < (0 - this.radius);
     }
   }
@@ -239,26 +241,26 @@ class TimRaindrops extends SCPattern {
     raindrops = new LinkedList<Raindrop>();
   }
   
-  public void run(int deltaMs) {
+  public void run(double deltaMs) {
     leftoverMs += deltaMs;
     while (leftoverMs > msPerRaindrop) {
       leftoverMs -= msPerRaindrop;
       raindrops.add(new Raindrop());
     }
     
-    for (Point p : model.points) {
+    for (LXPoint p : model.points) {
       color c = 
         blendColor(
-          color(210, 20, (float)Math.max(0, 1 - Math.pow((model.yMax - p.fy) / 10, 2)) * 50),
-          color(220, 60, (float)Math.max(0, 1 - Math.pow((p.fy - model.yMin) / 10, 2)) * 100),
+          lx.hsb(210, 20, (float)Math.max(0, 1 - Math.pow((model.yMax - p.y) / 10, 2)) * 50),
+          lx.hsb(220, 60, (float)Math.max(0, 1 - Math.pow((p.y - model.yMin) / 10, 2)) * 100),
           ADD);
       for (Raindrop raindrop : raindrops) {
-        if (p.fx >= (raindrop.p.x - raindrop.radius) && p.fx <= (raindrop.p.x + raindrop.radius) &&
-            p.fy >= (raindrop.p.y - raindrop.radius) && p.fy <= (raindrop.p.y + raindrop.radius)) {
+        if (p.x >= (raindrop.p.x - raindrop.radius) && p.x <= (raindrop.p.x + raindrop.radius) &&
+            p.y >= (raindrop.p.y - raindrop.radius) && p.y <= (raindrop.p.y + raindrop.radius)) {
           float d = raindrop.p.distanceTo(p) / raindrop.radius;
   //      float value = (float)Math.max(0, 1 - Math.pow(Math.min(0, d - raindrop.radius) / 5, 2)); 
           if (d < 1) {
-            c = blendColor(c, color(raindrop.hue, 80, (float)Math.pow(1 - d, 0.01) * 100), ADD);
+            c = blendColor(c, lx.hsb(raindrop.hue, 80, (float)Math.pow(1 - d, 0.01) * 100), ADD);
           }
         }
       }
@@ -300,16 +302,16 @@ class TimCubes extends SCPattern {
     }
     
     // returns TRUE if this should die
-    boolean age(int ms) {
+    boolean age(double ms) {
       if (!hasPeaked) {
-        value = value + (ms / 1000.0f * ((attackParameter.getValuef() + 0.01) * 5));
+        value = value + (float) (ms / 1000.0f * ((attackParameter.getValuef() + 0.01) * 5));
         if (value >= 1.0) {
           value = 1.0;
           hasPeaked = true;
         }
         return false;
       } else {
-        value = value - (ms / 1000.0f * ((decayParameter.getValuef() + 0.01) * 10));
+        value = value - (float) (ms / 1000.0f * ((decayParameter.getValuef() + 0.01) * 10));
         return value <= 0;
       }
     }
@@ -329,7 +331,7 @@ class TimCubes extends SCPattern {
     flashes = new LinkedList<CubeFlash>();
   }
   
-  public void run(int deltaMs) {
+  public void run(double deltaMs) {
     leftoverMs += deltaMs;
     float msPerFlash = 1000 / ((rateParameter.getValuef() + .01) * 100);
     while (leftoverMs > msPerFlash) {
@@ -337,14 +339,14 @@ class TimCubes extends SCPattern {
       flashes.add(new CubeFlash());
     }
     
-    for (Point p : model.points) {
+    for (LXPoint p : model.points) {
       colors[p.index] = 0;
     }
     
     for (CubeFlash flash : flashes) {
       float hue = (hueParameter.getValuef() + (hueVarianceParameter.getValuef() * flash.hue)) % 1.0;
-      color c = color(hue * 360, saturationParameter.getValuef() * 100, (flash.value) * 100);
-      for (Point p : flash.c.points) {
+      color c = lx.hsb(hue * 360, saturationParameter.getValuef() * 100, (flash.value) * 100);
+      for (LXPoint p : flash.c.points) {
         colors[p.index] = c;
       }
     }
@@ -416,7 +418,7 @@ class TimPlanes extends SCPattern {
   float prevRamp = 0;
   float[] wobbleSpeeds = { 1.0/8, 1.0/4, 1.0/2, 1.0 };
   
-  public void run(int deltaMs) {
+  public void run(double deltaMs) {
     float ramp = (float)lx.tempo.ramp();
     if (ramp < prevRamp) {
       beat = (beat + 1) % 32;
@@ -456,7 +458,7 @@ class TimPlanes extends SCPattern {
     
     Vector3 normalizedPoint = new Vector3();
 
-    for (Point p : model.points) {
+    for (LXPoint p : model.points) {
       if (random(1.0) < derez) {
         continue;
       }
@@ -464,19 +466,19 @@ class TimPlanes extends SCPattern {
       color c = 0;
       
       for (Plane plane : planes) {
-        normalizedPoint.x = p.fx - plane.center.x;
-        normalizedPoint.y = p.fy - plane.center.y;
-        normalizedPoint.z = p.fz - plane.center.z;
+        normalizedPoint.x = p.x - plane.center.x;
+        normalizedPoint.y = p.y - plane.center.y;
+        normalizedPoint.z = p.z - plane.center.z;
         
         float v = plane.rotation.rotatedY(normalizedPoint);
         float d = abs(v);
         
         final color planeColor;
         if (d <= thickness) {
-          planeColor = color(plane.hue, saturation, 100);
+          planeColor = lx.hsb(plane.hue, saturation, 100);
         } else if (d <= thickness * 2) {    
           float value = 1 - ((d - thickness) / thickness);
-          planeColor = color(plane.hue, saturation, value * 100);
+          planeColor = lx.hsb(plane.hue, saturation, value * 100);
         } else {
           planeColor = 0;
         }
@@ -496,15 +498,34 @@ class TimPlanes extends SCPattern {
 }
 
 /**
- * Not very flushed out but pretty.
+ * Two spinning wheels, basically XORed together, with a color palette that should
+ * be pretty easy to switch around.  Timed to the beat; also introduces "clickiness"
+ * which makes the movement non-linear throughout a given beat, giving it a nice
+ * dance feel.  I'm not 100% sure that it's actually going to look like it's _on_
+ * the beat, but that should be easy enough to adjust.
+ *
+ * It's particularly nice to turn down the clickiness and turn up derez during
+ * slow/beatless parts of the music and then revert them at the drop :)  But maybe
+ * I shouldn't be listening to so much shitty dubstep while making these...
  */
 class TimPinwheels extends SCPattern { 
+  private BasicParameter horizSpreadParameter = new BasicParameter("HSpr", 0.75);
+  private BasicParameter vertSpreadParameter = new BasicParameter("VSpr", 0.5);
+  private BasicParameter vertOffsetParameter = new BasicParameter("VOff", 1.0);
+  private BasicParameter zSlopeParameter = new BasicParameter("ZSlp", 0.6);
+  private BasicParameter sharpnessParameter = new BasicParameter("Shrp", 0.25);
+  private BasicParameter derezParameter = new BasicParameter("Drez", 0.25);
+  private BasicParameter clickinessParameter = new BasicParameter("Clic", 0.5);
+  private BasicParameter hueParameter = new BasicParameter("Hue", 0.667);
+  private BasicParameter hueSpreadParameter = new BasicParameter("HSpd", 0.667);
+
   float phase = 0;
-  private final int NUM_BLADES = 16;
+  private final int NUM_BLADES = 12;
   
   class Pinwheel {
     Vector2 center;
     int numBlades;
+    float realPhase;
     float phase;
     float speed;
     
@@ -514,15 +535,22 @@ class TimPinwheels extends SCPattern {
       this.speed = speed;
     }
     
-    void age(int deltaMs) {
-      phase = (phase + deltaMs / 1000.0 * speed) % 1.0;      
+    void age(float numBeats) {
+      int numSteps = numBlades;
+      
+      realPhase = (realPhase + numBeats / numSteps) % 2.0;
+      
+      float phaseStep = floor(realPhase * numSteps);
+      float phaseRamp = (realPhase * numSteps) % 1.0;
+      phase = (phaseStep + pow(phaseRamp, (clickinessParameter.getValuef() * 10) + 1)) / (numSteps * 2);
+//      phase = (phase + deltaMs / 1000.0 * speed) % 1.0;      
     }
     
     boolean isOnBlade(float x, float y) {
       x = x - center.x;
       y = y - center.y;
       
-      float normalizedAngle = (atan2(x, y) / (2 * PI) + 1.5 + phase) % 1;
+      float normalizedAngle = (atan2(x, y) / (2 * PI) + 1 + phase) % 1;
       float v = (normalizedAngle * 4 * numBlades);
       int blade_num = floor((v + 2) / 4);
       return (blade_num % 2) == 0;
@@ -530,35 +558,101 @@ class TimPinwheels extends SCPattern {
   }
   
   private final List<Pinwheel> pinwheels;
+  private final float[] values;
   
   TimPinwheels(GLucose glucose) {
     super(glucose);
     
+    addParameter(horizSpreadParameter);
+//    addParameter(vertSpreadParameter);
+    addParameter(vertOffsetParameter);
+    addParameter(zSlopeParameter);
+    addParameter(sharpnessParameter);
+    addParameter(derezParameter);
+    addParameter(clickinessParameter);
+    addParameter(hueParameter);
+    addParameter(hueSpreadParameter);
+    
+    pinwheels = new ArrayList();
+    pinwheels.add(new Pinwheel(0, 0, NUM_BLADES, 0.1));
+    pinwheels.add(new Pinwheel(0, 0, NUM_BLADES, -0.1));
+    
+    this.updateHorizSpread();
+    this.updateVertPositions();
+    
+    values = new float[model.points.size()];
+  }
+  
+  public void onParameterChanged(LXParameter parameter) {
+    if (parameter == horizSpreadParameter) {
+      updateHorizSpread();
+    } else if (parameter == vertSpreadParameter || parameter == vertOffsetParameter) {
+      updateVertPositions();
+    }
+  }
+  
+  private void updateHorizSpread() {
     float xDist = model.xMax - model.xMin;
     float xCenter = (model.xMin + model.xMax) / 2;
-    float yCenter = (model.yMin + model.yMax) / 2;
+    
+    float spread = horizSpreadParameter.getValuef() - 0.5;
+    pinwheels.get(0).center.x = xCenter - xDist * spread;
+    pinwheels.get(1).center.x = xCenter + xDist * spread; 
+  }
+  
+  private void updateVertPositions() {
+    float yDist = model.yMax - model.yMin;
+    float yCenter = model.yMin + yDist * vertOffsetParameter.getValuef();
 
-    pinwheels = new ArrayList();
-    pinwheels.add(new Pinwheel(xCenter - xDist * 0.4, yCenter, NUM_BLADES, 0.1));
-    pinwheels.add(new Pinwheel(xCenter + xDist * 0.4, yCenter, NUM_BLADES, -0.1));
+    float spread = vertSpreadParameter.getValuef() - 0.5;
+    pinwheels.get(0).center.y = yCenter - yDist * spread;
+    pinwheels.get(1).center.y = yCenter + yDist * spread;     
   }
   
-  public void run(int deltaMs) {
+  private float prevRamp = 0;
+  
+  public void run(double deltaMs) {
+    float ramp = lx.tempo.rampf();
+    float numBeats = (1 + ramp - prevRamp) % 1;
+    prevRamp = ramp;
+    
+    float hue = hueParameter.getValuef() * 360;
+    // 0 -> -180
+    // 0.5 -> 0
+    // 1 -> 180
+    float hueSpread = (hueSpreadParameter.getValuef() - 0.5) * 360;
+    
+    float fadeAmount = (float) (deltaMs / 1000.0) * pow(sharpnessParameter.getValuef() * 10, 1);
+    
     for (Pinwheel pw : pinwheels) {
-      pw.age(deltaMs);
+      pw.age(numBeats);
     }
     
-    for (Point p : model.points) {
+    float derez = derezParameter.getValuef();
+    
+    float zSlope = (zSlopeParameter.getValuef() - 0.5) * 2;
+    
+    int i = -1;
+    for (LXPoint p : model.points) {
+      ++i;
+      
       int value = 0;
       for (Pinwheel pw : pinwheels) {
-        value += (pw.isOnBlade(p.fx, p.fy) ? 1 : 0);
+        value += (pw.isOnBlade(p.x, p.y - p.z * zSlope) ? 1 : 0);
       }
       if (value == 1) {
-        colors[p.index] = color(120, 0, 100);
+        values[i] = 1;
+//        colors[p.index] = lx.hsb(120, 0, 100);
       } else {
-        color c = colors[p.index];
-        colors[p.index] = color(max(0, hue(c) - 10), min(100, saturation(c) + 10), brightness(c) - 5 );
+        values[i] = max(0, values[i] - fadeAmount);
+        //color c = colors[p.index];
+        //colors[p.index] = lx.hsb(max(0, lx.h(c) - 10), min(100, lx.s(c) + 10), lx.b(c) - 5 );
       }
+      
+      if (random(1.0) >= derez) {
+        float v = values[i];
+        colors[p.index] = lx.hsb((360 + hue + pow(v, 2) * hueSpread) % 360, 30 + pow(1 - v, 0.25) * 60, v * 100);
+      }      
     }
   }
 }
@@ -571,25 +665,25 @@ class TimPinwheels extends SCPattern {
  * it but there may be useful code here.
  */
 class TimTrace extends SCPattern {
-  private Map<Point, List<Point>> pointToNeighbors;
-  private Map<Point, Strip> pointToStrip;
+  private Map<LXPoint, List<LXPoint>> pointToNeighbors;
+  private Map<LXPoint, Strip> pointToStrip;
   //  private final Map<Strip, List<Strip>> stripToNearbyStrips;
   
   int extraMs;
   
   class MovingPoint {
-    Point currentPoint;
+    LXPoint currentPoint;
     float hue;
     private Strip currentStrip;
     private int currentStripIndex;
     private int direction; // +1 or -1
     
-    MovingPoint(Point p) {
+    MovingPoint(LXPoint p) {
       this.setPointOnNewStrip(p);
       hue = random(360);
     }
     
-    private void setPointOnNewStrip(Point p) {
+    private void setPointOnNewStrip(LXPoint p) {
       this.currentPoint = p;
       this.currentStrip = pointToStrip.get(p);
       for (int i = 0; i < this.currentStrip.points.size(); ++i) {
@@ -611,9 +705,9 @@ class TimTrace extends SCPattern {
     }
     
     void step() {
-      List<Point> neighborsOnOtherStrips = pointToNeighbors.get(this.currentPoint);
+      List<LXPoint> neighborsOnOtherStrips = pointToNeighbors.get(this.currentPoint);
 
-      Point nextPointOnCurrentStrip = null;      
+      LXPoint nextPointOnCurrentStrip = null;      
       this.currentStripIndex += this.direction;
       if (this.currentStripIndex >= 0 && this.currentStripIndex < this.currentStrip.points.size()) {
         nextPointOnCurrentStrip = this.currentStrip.points.get(this.currentStripIndex);
@@ -653,8 +747,8 @@ class TimTrace extends SCPattern {
     Map<Strip, Vector3> stripToCenter = new HashMap();
     for (Strip s : model.strips) {
       Vector3 v = new Vector3();
-      for (Point p : s.points) {
-        v.add(p.fx, p.fy, p.fz);
+      for (LXPoint p : s.points) {
+        v.add(p.x, p.y, p.z);
       }
       v.divide(s.points.size());
       stripToCenter.put(s, v);
@@ -678,24 +772,24 @@ class TimTrace extends SCPattern {
     return stripToNeighbors;
   }
   
-  private Map<Point, List<Point>> buildPointToNeighborsMap() {
-    Map<Point, List<Point>> m = new HashMap();
+  private Map<LXPoint, List<LXPoint>> buildPointToNeighborsMap() {
+    Map<LXPoint, List<LXPoint>> m = new HashMap();
     Map<Strip, List<Strip>> stripToNearbyStrips = this.buildStripToNearbyStripsMap();
     
     for (Strip s : model.strips) {
       List<Strip> nearbyStrips = stripToNearbyStrips.get(s);
       
-      for (Point p : s.points) {
-        Vector3 v = new Vector3(p.fx, p.fy, p.fz);
+      for (LXPoint p : s.points) {
+        Vector3 v = new Vector3(p.x, p.y, p.z);
         
-        List<Point> neighbors = new ArrayList();
+        List<LXPoint> neighbors = new ArrayList();
         
         for (Strip nearbyStrip : nearbyStrips) {
-          Point closestPoint = null;
+          LXPoint closestPoint = null;
           float closestPointDistance = 100000;
           
-          for (Point nsp : nearbyStrip.points) {
-            float distance = v.distanceTo(nsp.fx, nsp.fy, nsp.fz);
+          for (LXPoint nsp : nearbyStrip.points) {
+            float distance = v.distanceTo(nsp.x, nsp.y, nsp.z);
             if (closestPoint == null || distance < closestPointDistance) {
               closestPoint = nsp;
               closestPointDistance = distance;
@@ -714,25 +808,94 @@ class TimTrace extends SCPattern {
     return m;
   }
   
-  private Map<Point, Strip> buildPointToStripMap() {
-    Map<Point, Strip> m = new HashMap();
+  private Map<LXPoint, Strip> buildPointToStripMap() {
+    Map<LXPoint, Strip> m = new HashMap();
     for (Strip s : model.strips) {
-      for (Point p : s.points) {
+      for (LXPoint p : s.points) {
         m.put(p, s);
       }
     }
     return m;
   }
   
-  public void run(int deltaMs) {
-    for (Point p : model.points) {
+  public void run(double deltaMs) {
+    for (LXPoint p : model.points) {
       color c = colors[p.index];
-      colors[p.index] = color(hue(c), saturation(c), brightness(c) - 3);
+      colors[p.index] = lx.hsb(lx.h(c), lx.s(c), lx.b(c) - 3);
     }
     
     for (MovingPoint mp : movingPoints) {
       mp.step();
-      colors[mp.currentPoint.index] = blendColor(colors[mp.currentPoint.index], color(mp.hue, 10, 100), ADD);
+      colors[mp.currentPoint.index] = blendColor(colors[mp.currentPoint.index], lx.hsb(mp.hue, 10, 100), ADD);
+    }
+  }
+}
+
+class TimMetronome extends SCPattern {
+  private BasicParameter clickyParameter = new BasicParameter("CLICK", 0, 0, 10.0);
+  private BasicParameter derezParameter = new BasicParameter("DREZ", 0.5, 0, 1.0);
+  private BasicParameter driftParameter = new BasicParameter("DRIFT", 0, 0, 1.0);
+  private BasicParameter fadeParameter = new BasicParameter("FADE", 0.05, 0, 0.2);
+  private float modelWidth;
+  private int beatNum;
+  private float prevTempoRamp;
+  private LXProjection projection;
+  private float[] values;
+  private float[] hues;
+  
+  TimMetronome(GLucose glucose) {
+    super(glucose);
+    addParameter(clickyParameter);
+    addParameter(derezParameter);
+    addParameter(driftParameter);
+    addParameter(fadeParameter);
+    modelWidth = model.xMax - model.xMin;
+    projection = new LXProjection(model);
+    beatNum = 0;
+    prevTempoRamp = 0;
+    values = new float[model.points.size()];
+    hues = new float[model.points.size()];
+  }
+  
+  public void run(double deltaMs) {
+    float tempoRamp = lx.tempo.rampf();
+    if (tempoRamp < prevTempoRamp) {
+      beatNum = (beatNum + 1) % 1000;
+    }
+    prevTempoRamp = tempoRamp;
+    
+    float phase = beatNum + pow(tempoRamp, 1.0 + clickyParameter.getValuef());
+    
+    projection.reset();
+    projection.translateCenter(model.xMin, model.yMin, model.cz);
+    projection.rotate(phase * 0.5 * PI, 0, 0, 1);
+    
+    projection.translate(driftParameter.getValuef() * tempoRamp * modelWidth * 0.5, 0, 0);
+    
+    float derezCutoff = derezParameter.getValuef();
+    
+    float fadeMultiplier = (1.0 - fadeParameter.getValuef());
+    
+    float armRadius = modelWidth * 0.1;
+    for (LXVector p : projection) {
+      boolean onArm = false;
+      if (abs(p.x) < armRadius) {
+        onArm = (p.y > 0) || (sqrt(pow(p.x, 2) + pow(p.y, 2)) < armRadius);
+      }
+      if (onArm) {
+        values[p.index] = 1.0;
+        hues[p.index] = (floor(phase / 4) * 90) % 360;
+      } else {
+        values[p.index] *= fadeMultiplier;
+      }
+      
+      float saturation = pow(1 - values[p.index], 0.5) * 0.7 + 0.3;
+      float brightness = values[p.index];
+      
+      if (random(1.0) > derezCutoff) {
+        colors[p.index] = lx.hsb(hues[p.index], saturation * 100, brightness * 100);
+      }
     }
   }
 }
+