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 * Not very flushed out but pretty.
501 class TimPinwheels extends SCPattern {
503 private final int NUM_BLADES = 16;
511 Pinwheel(float xCenter, float yCenter, int numBlades, float speed) {
512 this.center = new Vector2(xCenter, yCenter);
513 this.numBlades = numBlades;
517 void age(int deltaMs) {
518 phase = (phase + deltaMs / 1000.0 * speed) % 1.0;
521 boolean isOnBlade(float x, float y) {
525 float normalizedAngle = (atan2(x, y) / (2 * PI) + 1.5 + phase) % 1;
526 float v = (normalizedAngle * 4 * numBlades);
527 int blade_num = floor((v + 2) / 4);
528 return (blade_num % 2) == 0;
532 private final List<Pinwheel> pinwheels;
534 TimPinwheels(GLucose glucose) {
537 float xDist = model.xMax - model.xMin;
538 float xCenter = (model.xMin + model.xMax) / 2;
539 float yCenter = (model.yMin + model.yMax) / 2;
541 pinwheels = new ArrayList();
542 pinwheels.add(new Pinwheel(xCenter - xDist * 0.4, yCenter, NUM_BLADES, 0.1));
543 pinwheels.add(new Pinwheel(xCenter + xDist * 0.4, yCenter, NUM_BLADES, -0.1));
546 public void run(int deltaMs) {
547 for (Pinwheel pw : pinwheels) {
551 for (Point p : model.points) {
553 for (Pinwheel pw : pinwheels) {
554 value += (pw.isOnBlade(p.fx, p.fy) ? 1 : 0);
557 colors[p.index] = color(120, 0, 100);
559 color c = colors[p.index];
560 colors[p.index] = color(max(0, hue(c) - 10), min(100, saturation(c) + 10), brightness(c) - 5 );
567 * This tries to figure out neighboring pixels from one cube to another to
568 * let you have a bunch of moving points tracing all over the structure.
569 * Adds a couple seconds of startup time to do the calculation, and in the
570 * end just comes out looking a lot like a screensaver. Probably not worth
571 * it but there may be useful code here.
573 class TimTrace extends SCPattern {
574 private Map<Point, List<Point>> pointToNeighbors;
575 private Map<Point, Strip> pointToStrip;
576 // private final Map<Strip, List<Strip>> stripToNearbyStrips;
583 private Strip currentStrip;
584 private int currentStripIndex;
585 private int direction; // +1 or -1
587 MovingPoint(Point p) {
588 this.setPointOnNewStrip(p);
592 private void setPointOnNewStrip(Point p) {
593 this.currentPoint = p;
594 this.currentStrip = pointToStrip.get(p);
595 for (int i = 0; i < this.currentStrip.points.size(); ++i) {
596 if (this.currentStrip.points.get(i) == p) {
597 this.currentStripIndex = i;
601 if (this.currentStripIndex == 0) {
602 // we are at the beginning of the strip; go forwards
604 } else if (this.currentStripIndex == this.currentStrip.points.size()) {
605 // we are at the end of the strip; go backwards
608 // we are in the middle of a strip; randomly go one way or another
609 this.direction = ((random(1.0) < 0.5) ? -1 : 1);
614 List<Point> neighborsOnOtherStrips = pointToNeighbors.get(this.currentPoint);
616 Point nextPointOnCurrentStrip = null;
617 this.currentStripIndex += this.direction;
618 if (this.currentStripIndex >= 0 && this.currentStripIndex < this.currentStrip.points.size()) {
619 nextPointOnCurrentStrip = this.currentStrip.points.get(this.currentStripIndex);
622 // pick which option to take; if we can keep going on the current strip then
623 // add that as another option
624 int option = floor(random(neighborsOnOtherStrips.size() + (nextPointOnCurrentStrip == null ? 0 : 100)));
626 if (option < neighborsOnOtherStrips.size()) {
627 this.setPointOnNewStrip(neighborsOnOtherStrips.get(option));
629 this.currentPoint = nextPointOnCurrentStrip;
634 List<MovingPoint> movingPoints;
636 TimTrace(GLucose glucose) {
641 pointToNeighbors = this.buildPointToNeighborsMap();
642 pointToStrip = this.buildPointToStripMap();
644 int numMovingPoints = 1000;
645 movingPoints = new ArrayList();
646 for (int i = 0; i < numMovingPoints; ++i) {
647 movingPoints.add(new MovingPoint(model.points.get(floor(random(model.points.size())))));
652 private Map<Strip, List<Strip>> buildStripToNearbyStripsMap() {
653 Map<Strip, Vector3> stripToCenter = new HashMap();
654 for (Strip s : model.strips) {
655 Vector3 v = new Vector3();
656 for (Point p : s.points) {
657 v.add(p.fx, p.fy, p.fz);
659 v.divide(s.points.size());
660 stripToCenter.put(s, v);
663 Map<Strip, List<Strip>> stripToNeighbors = new HashMap();
664 for (Strip s : model.strips) {
665 List<Strip> neighbors = new ArrayList();
666 Vector3 sCenter = stripToCenter.get(s);
667 for (Strip potentialNeighbor : model.strips) {
668 if (s != potentialNeighbor) {
669 float distance = sCenter.distanceTo(stripToCenter.get(potentialNeighbor));
671 neighbors.add(potentialNeighbor);
675 stripToNeighbors.put(s, neighbors);
678 return stripToNeighbors;
681 private Map<Point, List<Point>> buildPointToNeighborsMap() {
682 Map<Point, List<Point>> m = new HashMap();
683 Map<Strip, List<Strip>> stripToNearbyStrips = this.buildStripToNearbyStripsMap();
685 for (Strip s : model.strips) {
686 List<Strip> nearbyStrips = stripToNearbyStrips.get(s);
688 for (Point p : s.points) {
689 Vector3 v = new Vector3(p.fx, p.fy, p.fz);
691 List<Point> neighbors = new ArrayList();
693 for (Strip nearbyStrip : nearbyStrips) {
694 Point closestPoint = null;
695 float closestPointDistance = 100000;
697 for (Point nsp : nearbyStrip.points) {
698 float distance = v.distanceTo(nsp.fx, nsp.fy, nsp.fz);
699 if (closestPoint == null || distance < closestPointDistance) {
701 closestPointDistance = distance;
705 if (closestPointDistance < 15) {
706 neighbors.add(closestPoint);
717 private Map<Point, Strip> buildPointToStripMap() {
718 Map<Point, Strip> m = new HashMap();
719 for (Strip s : model.strips) {
720 for (Point p : s.points) {
727 public void run(int deltaMs) {
728 for (Point p : model.points) {
729 color c = colors[p.index];
730 colors[p.index] = color(hue(c), saturation(c), brightness(c) - 3);
733 for (MovingPoint mp : movingPoints) {
735 colors[mp.currentPoint.index] = blendColor(colors[mp.currentPoint.index], color(mp.hue, 10, 100), ADD);