2 * Not very flushed out, but kind of fun nonetheless.
4 class TimSpheres extends SCPattern {
5 private BasicParameter hueParameter = new BasicParameter("RAD", 1.0);
6 private final SawLFO lfo = new SawLFO(0, 1, 10000);
7 private final SinLFO sinLfo = new SinLFO(0, 1, 4000);
8 private final float centerX, centerY, centerZ;
16 private final Sphere[] spheres;
18 public TimSpheres(GLucose glucose) {
20 addParameter(hueParameter);
21 addModulator(lfo).trigger();
22 addModulator(sinLfo).trigger();
23 centerX = (model.xMax + model.xMin) / 2;
24 centerY = (model.yMax + model.yMin) / 2;
25 centerZ = (model.zMax + model.zMin) / 2;
27 spheres = new Sphere[2];
29 spheres[0] = new Sphere();
30 spheres[0].x = model.xMin;
31 spheres[0].y = centerY;
32 spheres[0].z = centerZ;
34 spheres[0].radius = 50;
36 spheres[1] = new Sphere();
37 spheres[1].x = model.xMax;
38 spheres[1].y = centerY;
39 spheres[1].z = centerZ;
40 spheres[1].hue = 0.33;
41 spheres[1].radius = 50;
44 public void run(int deltaMs) {
45 // Access the core master hue via this method call
46 float hv = hueParameter.getValuef();
47 float lfoValue = lfo.getValuef();
48 float sinLfoValue = sinLfo.getValuef();
50 spheres[0].x = model.xMin + sinLfoValue * model.xMax;
51 spheres[1].x = model.xMax - sinLfoValue * model.xMax;
53 spheres[0].radius = 100 * hueParameter.getValuef();
54 spheres[1].radius = 100 * hueParameter.getValuef();
56 for (Point p : model.points) {
59 color c = color(0, 0, 0);
60 for (Sphere s : spheres) {
61 float d = sqrt(pow(p.x - s.x, 2) + pow(p.y - s.y, 2) + pow(p.z - s.z, 2));
62 float r = (s.radius); // * (sinLfoValue + 0.5));
63 value = max(0, 1 - max(0, d - r) / 10);
65 c = blendColor(c, color(((s.hue + lfoValue) % 1) * 360, 100, min(1, value) * 100), ADD);
80 Vector2(float x, float y) {
85 float distanceTo(float x, float y) {
86 return sqrt(pow(x - this.x, 2) + pow(y - this.y, 2));
89 float distanceTo(Vector2 v) {
90 return distanceTo(v.x, v.y);
93 Vector2 plus(float x, float y) {
94 return new Vector2(this.x + x, this.y + y);
97 Vector2 plus(Vector2 v) {
98 return plus(v.x, v.y);
101 Vector2 minus(Vector2 v) {
102 return plus(-1 * v.x, -1 * v.y);
113 Vector3(float x, float y, float z) {
119 float distanceTo(float x, float y, float z) {
120 return sqrt(pow(x - this.x, 2) + pow(y - this.y, 2) + pow(z - this.z, 2));
123 float distanceTo(Vector3 v) {
124 return distanceTo(v.x, v.y, v.z);
127 float distanceTo(Point p) {
128 return distanceTo(p.fx, p.fy, p.fz);
131 void add(Vector3 other, float multiplier) {
132 this.add(other.x * multiplier, other.y * multiplier, other.z * multiplier);
135 void add(float x, float y, float z) {
141 void divide(float factor) {
149 private float a, b, c, d, e, f, g, h, i;
151 Rotation(float yaw, float pitch, float roll) {
152 float cosYaw = cos(yaw);
153 float sinYaw = sin(yaw);
154 float cosPitch = cos(pitch);
155 float sinPitch = sin(pitch);
156 float cosRoll = cos(roll);
157 float sinRoll = sin(roll);
159 a = cosYaw * cosPitch;
160 b = cosYaw * sinPitch * sinRoll - sinYaw * cosRoll;
161 c = cosYaw * sinPitch * cosRoll + sinYaw * sinRoll;
162 d = sinYaw * cosPitch;
163 e = sinYaw * sinPitch * sinRoll + cosYaw * cosRoll;
164 f = sinYaw * sinPitch * cosRoll - cosYaw * sinRoll;
166 h = cosPitch * sinRoll;
167 i = cosPitch * cosRoll;
170 Vector3 rotated(Vector3 v) {
178 float rotatedX(Vector3 v) {
179 return a * v.x + b * v.y + c * v.z;
182 float rotatedY(Vector3 v) {
183 return d * v.x + e * v.y + f * v.z;
186 float rotatedZ(Vector3 v) {
187 return g * v.x + h * v.y + i * v.z;
192 * Very literal rain effect. Not that great as-is but some tweaking could make it nice.
194 * - changing hue and direction of "rain" could make a nice fire effect
195 * - knobs to change frequency and size of rain drops
196 * - sync somehow to tempo but maybe less frequently than every beat?
198 class TimRaindrops extends SCPattern {
199 Vector3 randomVector3() {
201 random(model.xMax - model.xMin) + model.xMin,
202 random(model.yMax - model.yMin) + model.yMin,
203 random(model.zMax - model.zMin) + model.zMin);
214 this.p = new Vector3(
215 random(model.xMax - model.xMin) + model.xMin,
216 model.yMax + this.radius,
217 random(model.zMax - model.zMin) + model.zMin);
218 float velMagnitude = 120;
219 this.v = new Vector3(
223 this.hue = random(40) + 200;
226 // returns TRUE when this should die
227 boolean age(int ms) {
228 p.add(v, ms / 1000.0);
229 return this.p.y < (0 - this.radius);
233 private float leftoverMs = 0;
234 private float msPerRaindrop = 40;
235 private List<Raindrop> raindrops;
237 public TimRaindrops(GLucose glucose) {
239 raindrops = new LinkedList<Raindrop>();
242 public void run(int deltaMs) {
243 leftoverMs += deltaMs;
244 while (leftoverMs > msPerRaindrop) {
245 leftoverMs -= msPerRaindrop;
246 raindrops.add(new Raindrop());
249 for (Point p : model.points) {
252 color(210, 20, (float)Math.max(0, 1 - Math.pow((model.yMax - p.fy) / 10, 2)) * 50),
253 color(220, 60, (float)Math.max(0, 1 - Math.pow((p.fy - model.yMin) / 10, 2)) * 100),
255 for (Raindrop raindrop : raindrops) {
256 if (p.fx >= (raindrop.p.x - raindrop.radius) && p.fx <= (raindrop.p.x + raindrop.radius) &&
257 p.fy >= (raindrop.p.y - raindrop.radius) && p.fy <= (raindrop.p.y + raindrop.radius)) {
258 float d = raindrop.p.distanceTo(p) / raindrop.radius;
259 // float value = (float)Math.max(0, 1 - Math.pow(Math.min(0, d - raindrop.radius) / 5, 2));
261 c = blendColor(c, color(raindrop.hue, 80, (float)Math.pow(1 - d, 0.01) * 100), ADD);
268 Iterator<Raindrop> i = raindrops.iterator();
269 while (i.hasNext()) {
270 Raindrop raindrop = i.next();
271 boolean dead = raindrop.age(deltaMs);
280 class TimCubes extends SCPattern {
281 private BasicParameter rateParameter = new BasicParameter("RATE", 0.125);
282 private BasicParameter attackParameter = new BasicParameter("ATTK", 0.5);
283 private BasicParameter decayParameter = new BasicParameter("DECAY", 0.5);
284 private BasicParameter hueParameter = new BasicParameter("HUE", 0.5);
285 private BasicParameter hueVarianceParameter = new BasicParameter("H.V.", 0.25);
286 private BasicParameter saturationParameter = new BasicParameter("SAT", 0.5);
295 c = model.cubes.get(floor(random(model.cubes.size())));
297 boolean infiniteAttack = (attackParameter.getValuef() > 0.999);
298 hasPeaked = infiniteAttack;
299 value = (infiniteAttack ? 1 : 0);
302 // returns TRUE if this should die
303 boolean age(int ms) {
305 value = value + (ms / 1000.0f * ((attackParameter.getValuef() + 0.01) * 5));
312 value = value - (ms / 1000.0f * ((decayParameter.getValuef() + 0.01) * 10));
318 private float leftoverMs = 0;
319 private List<CubeFlash> flashes;
321 public TimCubes(GLucose glucose) {
323 addParameter(rateParameter);
324 addParameter(attackParameter);
325 addParameter(decayParameter);
326 addParameter(hueParameter);
327 addParameter(hueVarianceParameter);
328 addParameter(saturationParameter);
329 flashes = new LinkedList<CubeFlash>();
332 public void run(int deltaMs) {
333 leftoverMs += deltaMs;
334 float msPerFlash = 1000 / ((rateParameter.getValuef() + .01) * 100);
335 while (leftoverMs > msPerFlash) {
336 leftoverMs -= msPerFlash;
337 flashes.add(new CubeFlash());
340 for (Point p : model.points) {
344 for (CubeFlash flash : flashes) {
345 float hue = (hueParameter.getValuef() + (hueVarianceParameter.getValuef() * flash.hue)) % 1.0;
346 color c = color(hue * 360, saturationParameter.getValuef() * 100, (flash.value) * 100);
347 for (Point p : flash.c.points) {
352 Iterator<CubeFlash> i = flashes.iterator();
353 while (i.hasNext()) {
354 CubeFlash flash = i.next();
355 boolean dead = flash.age(deltaMs);
364 * This one is the best but you need to play with all the knobs. It's synced to
365 * the tempo, with the WSpd knob letting you pick 4 discrete multipliers for
368 * Basically it's just 3 planes all rotating to the beat, but also rotated relative
369 * to one another. The intersection of the planes and the cubes over time makes
370 * for a nice abstract effect.
372 class TimPlanes extends SCPattern {
373 private BasicParameter wobbleParameter = new BasicParameter("Wob", 0.166);
374 private BasicParameter wobbleSpreadParameter = new BasicParameter("WSpr", 0.25);
375 private BasicParameter wobbleSpeedParameter = new BasicParameter("WSpd", 0.375);
376 private BasicParameter wobbleOffsetParameter = new BasicParameter("WOff", 0);
377 private BasicParameter derezParameter = new BasicParameter("Drez", 0.5);
378 private BasicParameter thicknessParameter = new BasicParameter("Thick", 0.4);
379 private BasicParameter ySpreadParameter = new BasicParameter("ySpr", 0.2);
380 private BasicParameter hueParameter = new BasicParameter("Hue", 0.75);
381 private BasicParameter hueSpreadParameter = new BasicParameter("HSpr", 0.68);
383 final float centerX, centerY, centerZ;
391 Plane(Vector3 center, Rotation rotation, float hue) {
392 this.center = center;
393 this.rotation = rotation;
398 TimPlanes(GLucose glucose) {
400 centerX = (model.xMin + model.xMax) / 2;
401 centerY = (model.yMin + model.yMax) / 2;
402 centerZ = (model.zMin + model.zMax) / 2;
404 addParameter(wobbleParameter);
405 addParameter(wobbleSpreadParameter);
406 addParameter(wobbleSpeedParameter);
407 // addParameter(wobbleOffsetParameter);
408 addParameter(derezParameter);
409 addParameter(thicknessParameter);
410 addParameter(ySpreadParameter);
411 addParameter(hueParameter);
412 addParameter(hueSpreadParameter);
417 float[] wobbleSpeeds = { 1.0/8, 1.0/4, 1.0/2, 1.0 };
419 public void run(int deltaMs) {
420 float ramp = (float)lx.tempo.ramp();
421 if (ramp < prevRamp) {
422 beat = (beat + 1) % 32;
426 float wobbleSpeed = wobbleSpeeds[floor(wobbleSpeedParameter.getValuef() * wobbleSpeeds.length * 0.9999)];
428 phase = (((beat + ramp) * wobbleSpeed + wobbleOffsetParameter.getValuef()) % 1) * 2 * PI;
430 float ySpread = ySpreadParameter.getValuef() * 50;
431 float wobble = wobbleParameter.getValuef() * PI;
432 float wobbleSpread = wobbleSpreadParameter.getValuef() * PI;
433 float hue = hueParameter.getValuef() * 360;
434 float hueSpread = (hueSpreadParameter.getValuef() - 0.5) * 360;
436 float saturation = 10 + 60.0 * pow(ramp, 0.25);
438 float derez = derezParameter.getValuef();
442 new Vector3(centerX, centerY + ySpread, centerZ),
443 new Rotation(wobble - wobbleSpread, phase, 0),
444 (hue + 360 - hueSpread) % 360),
446 new Vector3(centerX, centerY, centerZ),
447 new Rotation(wobble, phase, 0),
450 new Vector3(centerX, centerY - ySpread, centerZ),
451 new Rotation(wobble + wobbleSpread, phase, 0),
452 (hue + 360 + hueSpread) % 360)
455 float thickness = (thicknessParameter.getValuef() * 25 + 1);
457 Vector3 normalizedPoint = new Vector3();
459 for (Point p : model.points) {
460 if (random(1.0) < derez) {
466 for (Plane plane : planes) {
467 normalizedPoint.x = p.fx - plane.center.x;
468 normalizedPoint.y = p.fy - plane.center.y;
469 normalizedPoint.z = p.fz - plane.center.z;
471 float v = plane.rotation.rotatedY(normalizedPoint);
474 final color planeColor;
475 if (d <= thickness) {
476 planeColor = color(plane.hue, saturation, 100);
477 } else if (d <= thickness * 2) {
478 float value = 1 - ((d - thickness) / thickness);
479 planeColor = color(plane.hue, saturation, value * 100);
484 if (planeColor != 0) {
488 c = blendColor(c, planeColor, ADD);
499 * Two spinning wheels, basically XORed together, with a color palette that should
500 * be pretty easy to switch around. Timed to the beat; also introduces "clickiness"
501 * which makes the movement non-linear throughout a given beat, giving it a nice
502 * dance feel. I'm not 100% sure that it's actually going to look like it's _on_
503 * the beat, but that should be easy enough to adjust.
505 * It's particularly nice to turn down the clickiness and turn up derez during
506 * slow/beatless parts of the music and then revert them at the drop :) But maybe
507 * I shouldn't be listening to so much shitty dubstep while making these...
509 class TimPinwheels extends SCPattern {
510 private BasicParameter horizSpreadParameter = new BasicParameter("HSpr", 0.75);
511 private BasicParameter vertSpreadParameter = new BasicParameter("VSpr", 0.5);
512 private BasicParameter vertOffsetParameter = new BasicParameter("VOff", 1.0);
513 private BasicParameter zSlopeParameter = new BasicParameter("ZSlp", 0.6);
514 private BasicParameter sharpnessParameter = new BasicParameter("Shrp", 0.25);
515 private BasicParameter derezParameter = new BasicParameter("Drez", 0.25);
516 private BasicParameter clickinessParameter = new BasicParameter("Clic", 0.5);
517 private BasicParameter hueParameter = new BasicParameter("Hue", 0.667);
518 private BasicParameter hueSpreadParameter = new BasicParameter("HSpd", 0.667);
521 private final int NUM_BLADES = 12;
530 Pinwheel(float xCenter, float yCenter, int numBlades, float speed) {
531 this.center = new Vector2(xCenter, yCenter);
532 this.numBlades = numBlades;
536 void age(float numBeats) {
537 int numSteps = numBlades;
539 realPhase = (realPhase + numBeats / numSteps) % 2.0;
541 float phaseStep = floor(realPhase * numSteps);
542 float phaseRamp = (realPhase * numSteps) % 1.0;
543 phase = (phaseStep + pow(phaseRamp, (clickinessParameter.getValuef() * 10) + 1)) / (numSteps * 2);
544 // phase = (phase + deltaMs / 1000.0 * speed) % 1.0;
547 boolean isOnBlade(float x, float y) {
551 float normalizedAngle = (atan2(x, y) / (2 * PI) + 1 + phase) % 1;
552 float v = (normalizedAngle * 4 * numBlades);
553 int blade_num = floor((v + 2) / 4);
554 return (blade_num % 2) == 0;
558 private final List<Pinwheel> pinwheels;
559 private final float[] values;
561 TimPinwheels(GLucose glucose) {
564 addParameter(horizSpreadParameter);
565 // addParameter(vertSpreadParameter);
566 addParameter(vertOffsetParameter);
567 addParameter(zSlopeParameter);
568 addParameter(sharpnessParameter);
569 addParameter(derezParameter);
570 addParameter(clickinessParameter);
571 addParameter(hueParameter);
572 addParameter(hueSpreadParameter);
574 pinwheels = new ArrayList();
575 pinwheels.add(new Pinwheel(0, 0, NUM_BLADES, 0.1));
576 pinwheels.add(new Pinwheel(0, 0, NUM_BLADES, -0.1));
578 this.updateHorizSpread();
579 this.updateVertPositions();
581 values = new float[model.points.size()];
584 public void onParameterChanged(LXParameter parameter) {
585 if (parameter == horizSpreadParameter) {
587 } else if (parameter == vertSpreadParameter || parameter == vertOffsetParameter) {
588 updateVertPositions();
592 private void updateHorizSpread() {
593 float xDist = model.xMax - model.xMin;
594 float xCenter = (model.xMin + model.xMax) / 2;
596 float spread = horizSpreadParameter.getValuef() - 0.5;
597 pinwheels.get(0).center.x = xCenter - xDist * spread;
598 pinwheels.get(1).center.x = xCenter + xDist * spread;
601 private void updateVertPositions() {
602 float yDist = model.yMax - model.yMin;
603 float yCenter = model.yMin + yDist * vertOffsetParameter.getValuef();
605 float spread = vertSpreadParameter.getValuef() - 0.5;
606 pinwheels.get(0).center.y = yCenter - yDist * spread;
607 pinwheels.get(1).center.y = yCenter + yDist * spread;
610 private float prevRamp = 0;
612 public void run(int deltaMs) {
613 float ramp = lx.tempo.rampf();
614 float numBeats = (1 + ramp - prevRamp) % 1;
617 float hue = hueParameter.getValuef() * 360;
621 float hueSpread = (hueSpreadParameter.getValuef() - 0.5) * 360;
623 float fadeAmount = (deltaMs / 1000.0) * pow(sharpnessParameter.getValuef() * 10, 1);
625 for (Pinwheel pw : pinwheels) {
629 float derez = derezParameter.getValuef();
631 float zSlope = (zSlopeParameter.getValuef() - 0.5) * 2;
634 for (Point p : model.points) {
638 for (Pinwheel pw : pinwheels) {
639 value += (pw.isOnBlade(p.fx, p.fy - p.fz * zSlope) ? 1 : 0);
643 // colors[p.index] = color(120, 0, 100);
645 values[i] = max(0, values[i] - fadeAmount);
646 //color c = colors[p.index];
647 //colors[p.index] = color(max(0, hue(c) - 10), min(100, saturation(c) + 10), brightness(c) - 5 );
650 if (random(1.0) >= derez) {
652 colors[p.index] = color((360 + hue + pow(v, 2) * hueSpread) % 360, 30 + pow(1 - v, 0.25) * 60, v * 100);
659 * This tries to figure out neighboring pixels from one cube to another to
660 * let you have a bunch of moving points tracing all over the structure.
661 * Adds a couple seconds of startup time to do the calculation, and in the
662 * end just comes out looking a lot like a screensaver. Probably not worth
663 * it but there may be useful code here.
665 class TimTrace extends SCPattern {
666 private Map<Point, List<Point>> pointToNeighbors;
667 private Map<Point, Strip> pointToStrip;
668 // private final Map<Strip, List<Strip>> stripToNearbyStrips;
675 private Strip currentStrip;
676 private int currentStripIndex;
677 private int direction; // +1 or -1
679 MovingPoint(Point p) {
680 this.setPointOnNewStrip(p);
684 private void setPointOnNewStrip(Point p) {
685 this.currentPoint = p;
686 this.currentStrip = pointToStrip.get(p);
687 for (int i = 0; i < this.currentStrip.points.size(); ++i) {
688 if (this.currentStrip.points.get(i) == p) {
689 this.currentStripIndex = i;
693 if (this.currentStripIndex == 0) {
694 // we are at the beginning of the strip; go forwards
696 } else if (this.currentStripIndex == this.currentStrip.points.size()) {
697 // we are at the end of the strip; go backwards
700 // we are in the middle of a strip; randomly go one way or another
701 this.direction = ((random(1.0) < 0.5) ? -1 : 1);
706 List<Point> neighborsOnOtherStrips = pointToNeighbors.get(this.currentPoint);
708 Point nextPointOnCurrentStrip = null;
709 this.currentStripIndex += this.direction;
710 if (this.currentStripIndex >= 0 && this.currentStripIndex < this.currentStrip.points.size()) {
711 nextPointOnCurrentStrip = this.currentStrip.points.get(this.currentStripIndex);
714 // pick which option to take; if we can keep going on the current strip then
715 // add that as another option
716 int option = floor(random(neighborsOnOtherStrips.size() + (nextPointOnCurrentStrip == null ? 0 : 100)));
718 if (option < neighborsOnOtherStrips.size()) {
719 this.setPointOnNewStrip(neighborsOnOtherStrips.get(option));
721 this.currentPoint = nextPointOnCurrentStrip;
726 List<MovingPoint> movingPoints;
728 TimTrace(GLucose glucose) {
733 pointToNeighbors = this.buildPointToNeighborsMap();
734 pointToStrip = this.buildPointToStripMap();
736 int numMovingPoints = 1000;
737 movingPoints = new ArrayList();
738 for (int i = 0; i < numMovingPoints; ++i) {
739 movingPoints.add(new MovingPoint(model.points.get(floor(random(model.points.size())))));
744 private Map<Strip, List<Strip>> buildStripToNearbyStripsMap() {
745 Map<Strip, Vector3> stripToCenter = new HashMap();
746 for (Strip s : model.strips) {
747 Vector3 v = new Vector3();
748 for (Point p : s.points) {
749 v.add(p.fx, p.fy, p.fz);
751 v.divide(s.points.size());
752 stripToCenter.put(s, v);
755 Map<Strip, List<Strip>> stripToNeighbors = new HashMap();
756 for (Strip s : model.strips) {
757 List<Strip> neighbors = new ArrayList();
758 Vector3 sCenter = stripToCenter.get(s);
759 for (Strip potentialNeighbor : model.strips) {
760 if (s != potentialNeighbor) {
761 float distance = sCenter.distanceTo(stripToCenter.get(potentialNeighbor));
763 neighbors.add(potentialNeighbor);
767 stripToNeighbors.put(s, neighbors);
770 return stripToNeighbors;
773 private Map<Point, List<Point>> buildPointToNeighborsMap() {
774 Map<Point, List<Point>> m = new HashMap();
775 Map<Strip, List<Strip>> stripToNearbyStrips = this.buildStripToNearbyStripsMap();
777 for (Strip s : model.strips) {
778 List<Strip> nearbyStrips = stripToNearbyStrips.get(s);
780 for (Point p : s.points) {
781 Vector3 v = new Vector3(p.fx, p.fy, p.fz);
783 List<Point> neighbors = new ArrayList();
785 for (Strip nearbyStrip : nearbyStrips) {
786 Point closestPoint = null;
787 float closestPointDistance = 100000;
789 for (Point nsp : nearbyStrip.points) {
790 float distance = v.distanceTo(nsp.fx, nsp.fy, nsp.fz);
791 if (closestPoint == null || distance < closestPointDistance) {
793 closestPointDistance = distance;
797 if (closestPointDistance < 15) {
798 neighbors.add(closestPoint);
809 private Map<Point, Strip> buildPointToStripMap() {
810 Map<Point, Strip> m = new HashMap();
811 for (Strip s : model.strips) {
812 for (Point p : s.points) {
819 public void run(int deltaMs) {
820 for (Point p : model.points) {
821 color c = colors[p.index];
822 colors[p.index] = color(hue(c), saturation(c), brightness(c) - 3);
825 for (MovingPoint mp : movingPoints) {
827 colors[mp.currentPoint.index] = blendColor(colors[mp.currentPoint.index], color(mp.hue, 10, 100), ADD);