--- /dev/null
+import processing.core.*;
+import processing.data.*;
+import processing.event.*;
+import processing.opengl.*;
+
+import netP5.*;
+import oscP5.*;
+import processing.serial.*;
+import java.util.LinkedHashMap;
+import toxi.geom.Vec3D;
+import toxi.geom.Matrix4x4;
+
+import heronarts.lx.font.*;
+import heronarts.lx.transition.*;
+import glucose.transform.*;
+import netP5.*;
+import heronarts.lx.pattern.*;
+import glucose.pattern.*;
+import heronarts.lx.model.*;
+import toxi.geom.mesh2d.*;
+import heronarts.lx.client.*;
+import glucose.*;
+import toxi.util.datatypes.*;
+import toxi.math.waves.*;
+import heronarts.lx.kinet.*;
+import oscP5.*;
+import toxi.geom.*;
+import toxi.util.events.*;
+import heronarts.lx.modulator.*;
+import rwmidi.*;
+import glucose.transition.*;
+import glucose.effect.*;
+import glucose.model.*;
+import toxi.math.conversion.*;
+import heronarts.lx.effect.*;
+import heronarts.lx.control.*;
+import glucose.control.*;
+import toxi.math.noise.*;
+import toxi.util.*;
+import heronarts.lx.*;
+import toxi.math.*;
+import heronarts.lx.audio.*;
+
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.PrintWriter;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+public class SugarCubes extends PApplet {
+
+/**
+ * +-+-+-+-+-+ +-+-+-+-+-+
+ * / /| |\ \
+ * / / + + \ \
+ * +-+-+-+-+-+ | +-+-+-+-+ | +-+-+-+-+-+
+ * | | + / \ + | |
+ * + THE + / / \ \ + CUBES +
+ * | |/ +-+-+-+-+-+-+-+ \| |
+ * +-+-+-+-+-+ | | +-+-+-+-+-+
+ * + +
+ * | SUGAR |
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+
+ *
+ * Welcome to the Sugar Cubes! This Processing sketch is a fun place to build
+ * animations, effects, and interactions for the platform. Most of the icky
+ * code guts are embedded in the GLucose library extension. If you're an
+ * artist, you shouldn't need to worry about any of that.
+ *
+ * Below, you will find definitions of the Patterns, Effects, and Interactions.
+ * If you're an artist, create a new tab in the Processing environment with
+ * your name. Implement your classes there, and add them to the list below.
+ */
+
+public LXPattern[] patterns(GLucose glucose) {
+ return new LXPattern[] {
+
+
+ // Slee
+ new Cathedrals(glucose),
+ new MidiMusic(glucose),
+ new Pulley(glucose),
+ new Swarm(glucose),
+ new ViolinWave(glucose),
+ new BouncyBalls(glucose),
+ new SpaceTime(glucose),
+ new ShiftingPlane(glucose),
+ new AskewPlanes(glucose),
+ new Blinders(glucose),
+ new CrossSections(glucose),
+ new Psychedelia(glucose),
+
+ new Traktor(glucose).setEligible(false),
+ new BassPod(glucose).setEligible(false),
+ new CubeEQ(glucose).setEligible(false),
+ new PianoKeyPattern(glucose).setEligible(false),
+
+ // DanH
+ new Noise(glucose),
+ new Play (glucose),
+ new Pong (glucose),
+ new Worms(glucose),
+
+ // Alex G
+ new SineSphere(glucose),
+// new CubeCurl(glucose),
+
+ // Shaheen
+ new HelixPattern(glucose).setEligible(false),
+
+ // Toby
+ new GlitchPlasma(glucose),
+ new FireEffect(glucose).setEligible(false),
+ new StripBounce(glucose),
+ new SoundRain(glucose).setEligible(false),
+ new SoundSpikes(glucose).setEligible(false),
+ new FaceSync(glucose),
+
+ // Jack
+ new Swim(glucose),
+ new Balance(glucose),
+
+ // Tim
+ new TimPlanes(glucose),
+ new TimPinwheels(glucose),
+ new TimRaindrops(glucose),
+ new TimCubes(glucose),
+ // new TimTrace(glucose),
+ new TimSpheres(glucose),
+
+ // Ben
+ // new Sandbox(glucose),
+ new TowerParams(glucose),
+ new DriveableCrossSections(glucose),
+ new GranimTestPattern2(glucose),
+
+ //JR
+ new Gimbal(glucose),
+
+ // Sam
+ new JazzRainbow(glucose),
+
+ // Arjun
+ new TelevisionStatic(glucose),
+ new AbstractPainting(glucose),
+ new Spirality(glucose),
+
+ // Basic test patterns for reference, not art
+ new TestCubePattern(glucose),
+ new TestTowerPattern(glucose),
+ new TestProjectionPattern(glucose),
+ new TestStripPattern(glucose),
+ new TestBassMapping(glucose),
+ new TestFloorMapping(glucose),
+ new TestSpeakerMapping(glucose),
+ new TestPerformancePattern(glucose),
+ // new TestHuePattern(glucose),
+ // new TestXPattern(glucose),
+ // new TestYPattern(glucose),
+ // new TestZPattern(glucose),
+
+ };
+}
+
+public LXTransition[] transitions(GLucose glucose) {
+ return new LXTransition[] {
+ new DissolveTransition(lx),
+ new AddTransition(glucose),
+ new MultiplyTransition(glucose),
+ new OverlayTransition(glucose),
+ new DodgeTransition(glucose),
+ new SwipeTransition(glucose),
+ new FadeTransition(lx),
+// new SubtractTransition(glucose), // similar to multiply - dh
+// new BurnTransition(glucose), // similar to multiply - dh
+// new ScreenTransition(glucose), // same as add -dh
+// new SoftLightTransition(glucose), // same as overlay -dh
+ };
+}
+
+// Handles to globally triggerable effects
+class Effects {
+ FlashEffect flash = new FlashEffect(lx);
+ BoomEffect boom = new BoomEffect(glucose);
+ BlurEffect blur = new BlurEffect(glucose);
+ QuantizeEffect quantize = new QuantizeEffect(glucose);
+ ColorFuckerEffect colorFucker = new ColorFuckerEffect(glucose);
+
+ Effects() {
+ blur.enable();
+ quantize.enable();
+ colorFucker.enable();
+ }
+}
+
+class SineSphere extends SCPattern {
+ private SinLFO yrot = new SinLFO(0, TWO_PI, 2000);
+ public final Projection sinespin;
+ float modelrad = sqrt((model.xMax)*(model.xMax) + (model.yMax)*(model.yMax) + (model.zMax)*(model.zMax));
+ Pick Sshape;
+
+ class Sphery {
+ float f1xcenter, f1ycenter, f1zcenter, f2xcenter , f2ycenter, f2zcenter; //second three are for an ellipse with two foci
+ private SinLFO vibration;
+ private SinLFO surface;
+ private SinLFO vx;
+ private SinLFO xbounce;
+ public SinLFO ybounce;
+ private SinLFO zbounce;
+ float vibration_min, vibration_max, vperiod;
+ public BasicParameter widthparameter;
+ public BasicParameter huespread;
+ public BasicParameter bouncerate;
+ public BasicParameter bounceamp;
+
+
+
+ public Sphery(float f1xcenter, float f1ycenter, float f1zcenter, float vibration_min, float vibration_max, float vperiod)
+ {
+ this.f1xcenter = f1xcenter;
+ this.f1ycenter = f1ycenter;
+ this.f1zcenter = f1zcenter;
+ this.vibration_min = vibration_min;
+ this.vibration_max = vibration_max;
+ this.vperiod = vperiod;
+ addParameter(bounceamp = new BasicParameter("Amp", .5f));
+ addParameter(bouncerate = new BasicParameter("Rate", .5f)); //ybounce.modulateDurationBy(bouncerate);
+ addParameter(widthparameter = new BasicParameter("Width", .1f));
+ addParameter(huespread = new BasicParameter("Hue", .2f));
+
+ addModulator( vx = new SinLFO(-4000, 10000, 100000)).trigger() ;
+ //addModulator(xbounce = new SinLFO(model.xMax/3, 2*model.yMax/3, 2000)).trigger();
+ addModulator(ybounce= new SinLFO(model.yMax/3, 2*model.yMax/3, 240000.f/lx.tempo.bpm())).trigger(); //ybounce.modulateDurationBy
+
+ //addModulator(bounceamp); //ybounce.setMagnitude(bouncerate);
+ addModulator( vibration = new SinLFO(vibration_min , vibration_max, 240000.f/lx.tempo.bpm())).trigger(); //vibration.modulateDurationBy(vx);
+
+ }
+ public Sphery(float f1xcenter, float f1ycenter, float f1zcenter, float f2xcenter, float f2ycenter, float f2zcenter,
+ float vibration_min, float vibration_max, float vperiod)
+ {
+ this.f1xcenter = f1xcenter;
+ this.f1ycenter = f1ycenter;
+ this.f1zcenter = f1zcenter;
+ this.f2xcenter = f2xcenter;
+ this.f2ycenter = f2ycenter;
+ this.f2zcenter = f2zcenter;
+ this.vibration_min = vibration_min;
+ this.vibration_max = vibration_max;
+ this.vperiod = vperiod;
+ //addModulator(xbounce = new SinLFO(model.xMax/3, 2*model.yMax/3, 2000)).trigger();
+ addModulator(ybounce).trigger();
+ addModulator( vibration = new SinLFO(vibration_min , vibration_max, lx.tempo.rampf())).trigger(); //vibration.modulateDurationBy(vx);
+ addParameter(widthparameter = new BasicParameter("Width", .1f));
+ addParameter(huespread = new BasicParameter("Hue", .2f));
+
+}
+
+
+
+
+
+public float distfromcirclecenter(float px, float py, float pz, float f1x, float f1y, float f1z)
+{
+ return dist(px, py, pz, f1x, f1y, f1z);
+ }
+ //void updatespherey(deltaMs, )
+ public int spheryvalue (float px, float py, float pz , float f1xc, float f1yc, float f1zc)
+ {
+//switch(sShpape.cur() ) {}
+ return lx.hsb(constrain(huespread.getValuef()*5*px, 0, 360) , dist(px, py, pz, f1xc, f1yc, f1zc) ,
+ max(0, 100 - 100*widthparameter.getValuef()*abs(dist(px, py, pz, f1xcenter, ybounce.getValuef(), f1zcenter)
+ - vibration.getValuef() ) ) );
+ }
+ public int ellipsevalue(float px, float py, float pz , float f1xc, float f1yc, float f1zc, float f2xc, float f2yc, float f2zc)
+ {
+//switch(sShpape.cur() ) {}
+ return lx.hsb(huespread.getValuef()*5*px, dist(model.xMax-px, model.yMax-py, model.zMax-pz, f1xc, f1yc, f1zc) ,
+ max(0, 100 - 100*widthparameter.getValuef() *
+ abs( (dist(px, py, pz, f1xc, ybounce.getValuef(), f1zc) +
+ (dist(px, py , pz, f2xc, ybounce.getValuef(), f2zc) ) )/2
+ - 1.2f*vibration.getValuef() ) ) ) ;
+ }
+
+public void run(double deltaMs) {
+ float vv = vibration.getValuef();
+ float ybv = ybounce.getValuef();
+
+ }
+
+}
+
+
+final Sphery[] spherys;
+ SineSphere(GLucose glucose)
+ {
+ super(glucose);
+ sinespin = new Projection(model);
+ addModulator(yrot).trigger();
+ //Sshape = addPick("Shape", , 1);
+ spherys = new Sphery[] {
+ new Sphery(model.xMax/4, model.yMax/2, model.zMax/2, modelrad/16, modelrad/8, 3000),
+ new Sphery(.75f*model.xMax, model.yMax/2, model.zMax/2, modelrad/20, modelrad/10, 2000),
+ new Sphery(model.xMax/2, model.yMax/2, model.zMax/2, modelrad/4, modelrad/8, 2300),
+ };
+
+ }
+
+// public void onParameterChanged(LXParameter parameter)
+// {
+
+
+// for (Sphery s : spherys) {
+// if (s == null) continue;
+// double bampv = s.bounceamp.getValue();
+// double brv = s.bouncerate.getValue();
+// double tempobounce = lx.tempo.bpm();
+// if (parameter == s.bounceamp)
+// {
+// s.ybounce.setRange(bampv*model.yMax/3 , bampv*2*model.yMax/3, brv);
+// }
+// else if ( parameter == s.bouncerate )
+// {
+// s.ybounce.setDuration(120000./tempobounce);
+// }
+// }
+// }
+
+ public void run( double deltaMs) {
+ float t = lx.tempo.rampf();
+ float bpm = lx.tempo.bpmf();
+ //spherys[1].run(deltaMs);
+ //spherys[2].run(deltaMs);
+ //spherys[3].run(deltaMs);]
+ sinespin.reset(model)
+
+ // Translate so the center of the car is the origin, offset by yPos
+ .translateCenter(model, 0, 0, 0)
+
+ // Rotate around the origin (now the center of the car) about an X-vector
+ .rotate(yrot.getValuef(), 0, 1, 0);
+
+
+
+ for (Point p: model.points){
+ int c = 0;
+ c = blendColor(c, spherys[1].spheryvalue(p.x, p.y, p.z, .75f*model.xMax, model.yMax/2, model.zMax/2), ADD);
+ c = blendColor(c, spherys[0].spheryvalue(p.x, p.y, p.z, model.xMax/4, model.yMax/4, model.zMax/2), ADD);
+ c = blendColor(c, spherys[2].spheryvalue(p.x, p.y, p.z, model.xMax/2, model.yMax/2, model.zMax/2),ADD);
+
+ colors[p.index] = lx.hsb(lx.h(c), lx.s(c), lx.b(c));
+
+ }
+
+
+
+ }
+ int spheremode = 0;
+
+ // void keyPressed() {
+ // spheremode++;
+ // }
+
+ // color CalcPoint(PVector Px)
+ // {
+ // // if (spheremode == 0 )
+ //{
+
+ //}
+ // else if (spheremode == 1)
+ // {
+
+ // color c = 0;
+ // c = blendColor(c, spherys[3].ellipsevalue(Px.x, Px.y, Px.z, model.xMax/4, model.yMax/4, model.zMax/4, 3*model.xMax/4, 3*model.yMax/4, 3*model.zMax/4),ADD);
+ // return c;
+ // }
+ // return lx.hsb(0,0,0);
+ // // else if(spheremode ==2)
+ // { color c = 0;
+ // return lx.hsb(CalcCone( (xyz by = new xyz(0,spherys[2].ybounce.getValuef(),0) ), Px, mid) );
+
+ // }
+
+
+ // }
+
+ }
+
+class CubeCurl extends SCPattern{
+float CH, CW, diag;
+ArrayList<PVector> cubeorigin = new ArrayList<PVector>();
+ArrayList<PVector> centerlist = new ArrayList<PVector>();
+private SinLFO curl = new SinLFO(0, Cube.EDGE_HEIGHT, 5000 );
+
+private SinLFO bg = new SinLFO(180, 220, 3000);
+
+CubeCurl(GLucose glucose){
+super(glucose);
+addModulator(curl).trigger();
+addModulator(bg).trigger();
+ this.CH = Cube.EDGE_HEIGHT;
+ this.CW = Cube.EDGE_WIDTH;
+ this.diag = sqrt(CW*CW + CW*CW);
+
+
+ArrayList<PVector> centerlistrelative = new ArrayList<PVector>();
+for (int i = 0; i < model.cubes.size(); i++){
+ Cube a = model.cubes.get(i);
+ cubeorigin.add(new PVector(a.x, a.y, a.z));
+ centerlist.add(centerofcube(i));
+
+}
+
+}
+//there is definitely a better way of doing this!
+public PVector centerofcube(int i) {
+Cube c = model.cubes.get(i);
+
+println(" cube #: " + i + " c.x " + c.x + " c.y " + c.y + " c.z " + c.z );
+PVector cubeangle = new PVector(c.rx, c.ry, c.rz);
+//println("raw x" + cubeangle.x + "raw y" + cubeangle.y + "raw z" + cubeangle.z);
+PVector cubecenter = new PVector(c.x + CW/2, c.y + CH/2, c.z + CW/2);
+println("cubecenter unrotated: " + cubecenter.x + " " +cubecenter.y + " " +cubecenter.z );
+PVector centerrot = new PVector(cos(c.rx)*CW/2 - sin(c.rx)*CW/2, 0, cos(c.rz)*CW/2 + sin(c.rz)*CW/2);
+ // nCos*(y-o.y) - nSin*(z-o.z) + o.y
+cubecenter = PVector.add(cubecenter, centerrot);
+println( " cubecenter.x " + cubecenter.x + " cubecenter.y " + cubecenter.y + " cubecenter.z " + cubecenter.z + " ");
+
+
+return cubecenter;
+}
+
+
+public void run(double deltaMs){
+for (int i =0; i < model.cubes.size(); i++) {
+Cube c = model.cubes.get(i);
+float cfloor = c.y;
+
+// if (i%3 == 0){
+
+// for (Point p : c.points ){
+// // colors[p.index]=color(0,0,0);
+// //float dif = (p.y - c.y);
+// //colors[p.index] = color( bg.getValuef() , 80 , dif < curl.getValuef() ? 80 : 0, ADD);
+// }
+// }
+
+// else if (i%3 == 1) {
+
+// for (Point p: c.points){
+// colors[p.index]=color(0,0,0);
+// float dif = (p.y - c.y);
+// // colors[p.index] =
+// // color(bg.getValuef(),
+// // map(curl.getValuef(), 0, Cube.EDGE_HEIGHT, 20, 100),
+// // 100 - 10*abs(dif - curl.getValuef()), ADD );
+// }
+// }
+// else if (i%3 == 2){
+ // centerlist[i].sub(cubeorigin(i);
+ for (Point p: c.points) {
+ PVector pv = new PVector(p.x, p.y, p.z);
+ colors[p.index] =color( constrain(4* pv.dist(centerlist.get(i)), 0, 360) , 50, 100 );
+ // colors[p.index] =color(constrain(centerlist[i].x, 0, 360), constrain(centerlist[i].y, 0, 100), );
+
+
+ }
+
+
+ //}
+
+ }
+ }
+ }
+
+ class HueTestHSB extends SCPattern{
+ BasicParameter HueT = new BasicParameter("Hue", .5f);
+ BasicParameter SatT = new BasicParameter("Sat", .5f);
+ BasicParameter BriT = new BasicParameter("Bright", .5f);
+
+HueTestHSB(GLucose glucose) {
+ super(glucose);
+ addParameter(HueT);
+ addParameter(SatT);
+ addParameter(BriT);
+}
+ public void run(double deltaMs){
+
+ for (Point p : model.points) {
+ int c = 0;
+ c = blendColor(c, lx.hsb(360*HueT.getValuef(), 100*SatT.getValuef(), 100*BriT.getValuef()), ADD);
+ colors[p.index]= c;
+ }
+ int now= millis();
+ if (now % 1000 <= 20)
+ {
+ println("Hue: " + 360*HueT.getValuef() + "Sat: " + 100*SatT.getValuef() + "Bright: " + 100*BriT.getValuef());
+ }
+ }
+
+ }
+
+class TelevisionStatic extends SCPattern {
+ BasicParameter brightParameter = new BasicParameter("BRIGHT", 1.0f);
+ BasicParameter saturationParameter = new BasicParameter("SAT", 1.0f);
+ BasicParameter hueParameter = new BasicParameter("HUE", 1.0f);
+ SinLFO direction = new SinLFO(0, 10, 3000);
+
+ public TelevisionStatic(GLucose glucose) {
+ super(glucose);
+ addModulator(direction).trigger();
+ addParameter(brightParameter);
+ addParameter(saturationParameter);
+ addParameter(hueParameter);
+ }
+
+ public void run(double deltaMs) {
+ boolean d = direction.getValuef() > 5.0f;
+ for (Point p : model.points) {
+ colors[p.index] = lx.hsb((lx.getBaseHuef() + random(hueParameter.getValuef() * 360))%360, random(saturationParameter.getValuef() * 100), random(brightParameter.getValuef() * 100));
+ }
+ }
+}
+
+class AbstractPainting extends SCPattern {
+
+ PImage img;
+
+ SinLFO colorMod = new SinLFO(0, 360, 5000);
+ SinLFO brightMod = new SinLFO(0, model.zMax, 2000);
+
+ public AbstractPainting(GLucose glucose) {
+ super(glucose);
+ addModulator(colorMod).trigger();
+ addModulator(brightMod).trigger();
+
+ img = loadImage("abstract.jpg");
+ img.loadPixels();
+ }
+
+ public void run(double deltaMs) {
+ for (Point p : model.points) {
+ int c = img.get((int)((p.x / model.xMax) * img.width), img.height - (int)((p.y / model.yMax) * img.height));
+ colors[p.index] = lx.hsb(hue(c) + colorMod.getValuef()%360, saturation(c), brightness(c) - ((p.z - brightMod.getValuef())/p.z));
+ }
+ }
+}
+
+class Spirality extends SCPattern {
+ final BasicParameter r = new BasicParameter("RADIUS", 0.5f);
+
+ float angle = 0;
+ float rad = 0;
+ int direction = 1;
+
+ Spirality(GLucose glucose) {
+ super(glucose);
+ addParameter(r);
+ for (Point p : model.points) {
+ colors[p.index] = lx.hsb(0, 0, 0);
+ }
+ }
+
+ public void run(double deltaMs) {
+ angle += deltaMs * 0.007f;
+ rad += deltaMs * .025f * direction;
+ float x = model.xMax / 2 + cos(angle) * rad;
+ float y = model.yMax / 2 + sin(angle) * rad;
+ for (Point p : model.points) {
+ float b = dist(x,y,p.x,p.y);
+ if (b < 90) {
+ colors[p.index] = blendColor(
+ colors[p.index],
+ lx.hsb(lx.getBaseHuef() + 25, 10, map(b, 0, 10, 100, 0)),
+ ADD);
+ } else {
+ colors[p.index] = blendColor(
+ colors[p.index],
+ lx.hsb(25, 10, map(b, 0, 10, 0, 15)),
+ SUBTRACT);
+ }
+ }
+ if (rad > model.xMax / 2 || rad <= .001f) {
+ direction *= -1;
+ }
+ }
+}
+
+
+
+
+/**
+ * This is a reusable equalizer class that lets you get averaged
+ * bands with dB scaling and smoothing.
+ */
+public static class GraphicEQ {
+
+ private final LX lx;
+
+ public final BasicParameter level = new BasicParameter("LVL", 0.5f);
+ public final BasicParameter range = new BasicParameter("RNGE", 0.5f);
+ public final BasicParameter slope = new BasicParameter("SLOP", 0.5f);
+ public final BasicParameter attack = new BasicParameter("ATK", 0.5f);
+ public final BasicParameter release = new BasicParameter("REL", 0.5f);
+
+ private final FFT fft;
+ private final int numBands;
+
+ private final LinearEnvelope[] bandVals;
+
+ public final static int DEFAULT_NUM_BANDS = 16;
+
+ public GraphicEQ(LX lx) {
+ this(lx, DEFAULT_NUM_BANDS);
+ }
+
+ /**
+ * Note that the number of bands is a suggestion. Due to the FFT implementation
+ * the actual number may be slightly different.
+ */
+ public GraphicEQ(LX lx, int num) {
+ this.lx = lx;
+ fft = new FFT(lx.audioInput().bufferSize(), lx.audioInput().sampleRate());
+ fft.window(FFT.HAMMING);
+ fft.logAverages(50, num/8);
+ numBands = this.fft.avgSize();
+ bandVals = new LinearEnvelope[numBands];
+ for (int i = 0; i < bandVals.length; ++i) {
+ (bandVals[i] = new LinearEnvelope(0, 0, 500)).trigger();
+ }
+ }
+
+ static final float logTen = log(10);
+ public static float log10(float val) {
+ return log(val) / logTen;
+ }
+
+ public float getLevel(int band) {
+ return bandVals[band].getValuef();
+ }
+
+ public float getAverageLevel(int minBand, int numBands) {
+ float avg = 0;
+ for (int i = minBand; i < minBand + numBands; ++i) {
+ avg += bandVals[i].getValuef();
+ }
+ avg /= numBands;
+ return avg;
+ }
+
+ public void run(double deltaMs) {
+ fft.forward(lx.audioInput().mix);
+ float zeroDBReference = pow(10, 100*(1-level.getValuef())/20.f);
+ float decibelRange = 12 + range.getValuef() * 60;
+ float decibelSlope = slope.getValuef() * 60.f / numBands;
+ for (int i = 0; i < numBands; ++i) {
+ float raw = fft.getAvg(i);
+ float decibels = 20*log10(raw / zeroDBReference);
+ float positiveDecibels = decibels + decibelRange;
+ positiveDecibels += i*decibelSlope;
+ float value = constrain(positiveDecibels / decibelRange, 0, 1);
+
+ if (value > bandVals[i].getValuef()) {
+ bandVals[i].setRangeFromHereTo(value, attack.getValuef() * 20).trigger();
+ }
+ }
+ for (LinearEnvelope band : bandVals) {
+ band.run(deltaMs);
+ if (!band.isRunning() && band.getValuef() > 0) {
+ band.setRangeFromHereTo(0, release.getValuef() * 1600).trigger();
+ }
+ }
+ }
+}
+
+
+class TowerParams extends SCPattern
+{
+ BasicParameter hueoff = new BasicParameter("Hueoff", 0.0f);
+ BasicParameter hueSpan = new BasicParameter("HueRange", 0.0f);
+ BasicParameter t1 = new BasicParameter("T1", 0.0f);
+ BasicParameter t2 = new BasicParameter("T2", 0.0f);
+ BasicParameter t3 = new BasicParameter("T3", 0.0f);
+ BasicParameter t4 = new BasicParameter("T4", 0.0f);
+ BasicParameter t5 = new BasicParameter("T5", 0.0f);
+ BasicParameter t6 = new BasicParameter("T6", 0.0f);
+ BasicParameter t7 = new BasicParameter("T7", 0.0f);
+ BasicParameter t8 = new BasicParameter("T8", 0.0f);
+ BasicParameter t9 = new BasicParameter("T9", 0.0f);
+ BasicParameter t10 = new BasicParameter("T10", 0.0f);
+ BasicParameter t11 = new BasicParameter("T11", 0.0f);
+ BasicParameter t12 = new BasicParameter("T12", 0.0f);
+ BasicParameter t13 = new BasicParameter("T13", 0.0f);
+ BasicParameter t14 = new BasicParameter("T14", 0.0f);
+ BasicParameter t15 = new BasicParameter("T15", 0.0f);
+ BasicParameter t16 = new BasicParameter("T16", 0.0f);
+
+ ArrayList<BasicParameter> towerParams;
+ int towerSize;
+ int colorSpan;
+ TowerParams(GLucose glucose) {
+ super(glucose);
+
+ towerParams = new ArrayList<BasicParameter>();
+ addParameter(hueoff);
+ addParameter(hueSpan);
+ towerParams.add(t1);
+ towerParams.add(t2);
+ towerParams.add(t3);
+ towerParams.add(t4);
+ towerParams.add(t5);
+ towerParams.add(t6);
+ towerParams.add(t7);
+ towerParams.add(t8);
+ towerParams.add(t9);
+ towerParams.add(t10);
+ towerParams.add(t11);
+ towerParams.add(t12);
+ towerParams.add(t13);
+ towerParams.add(t14);
+ towerParams.add(t15);
+ towerParams.add(t16);
+ for(BasicParameter p : towerParams)
+ {
+ addParameter(p);
+ }
+ towerSize = model.towers.size();
+ colorSpan = 255 / towerSize;
+ }
+
+ public void run(double deltaMs)
+ {
+ clearALL();
+ Tower t;
+ for(int i=0; i<towerSize ;i++)
+ {
+ t= model.towers.get(i);
+ for(Point p : t.points)
+ {
+ if(p.y<towerParams.get(i).getValuef()*200)
+ {
+ colors[p.index]=lx.hsb(255 * hueoff.getValuef()+colorSpan * hueSpan.getValuef() * i, 255, 255);
+ }
+ }
+ }
+
+ }
+
+ public void clearALL()
+ {
+ for(Point p : model.points)
+ {
+ colors[p.index] = 0;
+ }
+ }
+
+}
+class Sandbox extends SCPattern
+{
+ int c=0;
+ int prevC=0;
+ int huerange=255;
+ int pointrange= model.points.size();
+ int striprange= model.strips.size();
+ int facerange= model.faces.size();
+ int cuberange = model.cubes.size();
+ int towerrange = model.towers.size();
+ int counter=0;
+
+ Sandbox(GLucose glucose) {
+ super(glucose);
+ println("points "+pointrange);
+ println("strips "+striprange);
+ println("faces "+facerange);
+ println("cubes "+cuberange);
+ println("towers "+towerrange);
+ }
+
+ public void run(double deltaMs) {
+
+
+ if(counter % 10 ==0)
+ {
+ doDraw(c,0);
+ c = (c + 1) % towerrange;
+ long col = lx.hsb(Math.round(Math.random()*255),255,255) ;
+ doDraw(c,col);
+ }
+ counter++;
+
+ }
+
+ public void doDraw(int c,long col)
+ {
+ Tower t= model.towers.get((int) c);
+ for(Point p : t.points)
+ {
+ colors[p.index] = (int) col;
+ }
+ }
+};
+
+class GranimTestPattern extends GranimPattern
+{
+ GranimTestPattern(GLucose glucose)
+ {
+ super(glucose);
+ addGraphic("myReds",new RedsGraphic(100));
+ int[] dots = {0,128,0,128,0,128,0,128,0,128,0,128};
+ addGraphic("myOtherColors",new ColorDotsGraphic(dots));
+
+ getGraphicByName("myOtherColors").position=100;
+ }
+ int counter=0;
+ public void run(double deltaMs)
+ {
+ clearALL();
+ super.run(deltaMs);
+
+ if(counter % 3 ==0)
+ {
+ Graphic reds = getGraphicByName("myReds");
+ Graphic others = getGraphicByName("myOtherColors");
+ reds.position = reds.position + 1 % 19000;
+ others.position = others.position + 10 % 19000;
+ }
+ }
+ public void clearALL()
+ {
+ for(int i = 0; i < colors.length; i++)
+ {
+ colors[i] = 0;
+ }
+ }
+
+
+}
+
+class GranimTestPattern2 extends GranimPattern
+{
+ GranimTestPattern2(GLucose glucose)
+ {
+ super(glucose);
+ /*for(int i = 0;i < 100; i++)
+ {
+ Graphic g = addGraphic("myReds_"+i,new RedsGraphic(Math.round(Math.random() * 100)));
+
+ }*/
+ Graphic g = addGraphic("myRandoms",new RandomsGranim(50));
+ g.position = 200;
+
+ }
+ int counter=0;
+ float count=0;
+ public void run(double deltaMs)
+ {
+ clearALL();
+ super.run(deltaMs);
+ Graphic randomsGraphic = getGraphicByName("myRandoms");
+ randomsGraphic.position = Math.round(sin(count)*1000)+5000;
+ count+= 0.005f;
+ }
+ public void clearALL()
+ {
+ for(Point p : model.points)
+ {
+ colors[p.index] = 0;
+ }
+ }
+
+
+};
+
+class DriveableCrossSections extends CrossSections
+{
+ BasicParameter xd;
+ BasicParameter yd;
+ BasicParameter zd;
+ BasicParameter mode;
+
+ DriveableCrossSections(GLucose glucose) {
+ super(glucose);
+ }
+
+ public void addParams()
+ {
+ mode = new BasicParameter("Mode", 0.0f);
+ xd = new BasicParameter("XD", 0.0f);
+ yd = new BasicParameter("YD", 0.0f);
+ zd = new BasicParameter("ZD", 0.0f);
+ addParameter(mode);
+ addParameter(xd);
+ addParameter(yd);
+ addParameter(zd);
+
+ super.addParams();
+ }
+
+ public void onParameterChanged(LXParameter p) {
+ if(p == mode)
+ {
+ if(interactive())
+ {
+ copyValuesToKnobs();
+ }else{
+ copyKnobsToValues();
+ }
+ }
+ }
+
+ public void copyValuesToKnobs()
+ {
+ xd.setValue(x.getValue()/200);
+ yd.setValue(y.getValue()/115);
+ zd.setValue(z.getValue()/100);
+ }
+
+ public void copyKnobsToValues()
+ {
+ x.setValue(xd.getValue()*200);
+ y.setValue(yd.getValue()*115);
+ z.setValue(zd.getValue()*100);
+ }
+
+ public boolean interactive()
+ {
+ return Math.round(mode.getValuef())>0.5f;
+ }
+
+ public void updateXYZVals()
+ {
+ if(interactive())
+ {
+ xv = xd.getValuef()*200;
+ yv = yd.getValuef()*115;
+ zv = zd.getValuef()*100;
+ }else{
+ super.updateXYZVals();
+ copyValuesToKnobs();
+ }
+ }
+
+}
+//----------------------------------------------------------------------------------------------------------------------------------
+public class Pong extends DPat {
+ SinLFO x,y,z,dx,dy,dz;
+ float cRad; BasicParameter pSize;
+ Pick pChoose;
+ PVector v = new PVector(), vMir = new PVector();
+
+ Pong(GLucose glucose) {
+ super(glucose);
+ cRad = mMax.x/10;
+ addModulator(dx = new SinLFO(6000, 500, 30000 )).trigger();
+ addModulator(dy = new SinLFO(3000, 500, 22472 )).trigger();
+ addModulator(dz = new SinLFO(1000, 500, 18420 )).trigger();
+ addModulator(x = new SinLFO(cRad, mMax.x - cRad, 0)).trigger(); x.modulateDurationBy(dx);
+ addModulator(y = new SinLFO(cRad, mMax.y - cRad, 0)).trigger(); y.modulateDurationBy(dy);
+ addModulator(z = new SinLFO(cRad, mMax.z - cRad, 0)).trigger(); z.modulateDurationBy(dz);
+ pSize = addParam ("Size" , 0.4f );
+ pChoose = addPick ("Animiation" , 2, 2, new String[] {"Pong", "Ball", "Cone"} );
+ }
+
+ public void StartRun(double deltaMs) { cRad = mMax.x*val(pSize)/6; }
+ public int CalcPoint(PVector p) {
+ v.set(x.getValuef(), y.getValuef(), z.getValuef());
+ v.z=0;p.z=0;// ignore z dimension
+ switch(pChoose.Cur()) {
+ case 0: vMir.set(mMax); vMir.sub(p);
+ return lx.hsb(lxh(),100,c1c(1 - min(v.dist(p), v.dist(vMir))*.5f/cRad)); // balls
+ case 1: return lx.hsb(lxh(),100,c1c(1 - v.dist(p)*.5f/cRad)); // ball
+ case 2: vMir.set(mMax.x/2,0,mMax.z/2);
+ return lx.hsb(lxh(),100,c1c(1 - calcCone(p,v,vMir) * max(.02f,.45f-val(pSize)))); // spot
+ }
+ return lx.hsb(0,0,0);
+ }
+}
+//----------------------------------------------------------------------------------------------------------------------------------
+public class NDat {
+ float xz, yz, zz, hue, speed, angle, den;
+ float xoff,yoff,zoff;
+ float sinAngle, cosAngle;
+ boolean isActive;
+ NDat () { isActive=false; }
+ public boolean Active() { return isActive; }
+ public void set (float _hue, float _xz, float _yz, float _zz, float _den, float _speed, float _angle) {
+ isActive = true;
+ hue=_hue; xz=_xz; yz=_yz; zz =_zz; den=_den; speed=_speed; angle=_angle;
+ xoff = random(100e3f); yoff = random(100e3f); zoff = random(100e3f);
+ }
+}
+
+public class Noise extends DPat
+{
+ int CurAnim, iSymm;
+ int XSym=1,YSym=2,RadSym=3;
+ float zTime , zTheta=0, zSin, zCos, rtime, ttime;
+ BasicParameter pSpeed , pDensity, pSharp;
+ Pick pChoose, pSymm;
+ int _ND = 4;
+ NDat N[] = new NDat[_ND];
+
+ Noise(GLucose glucose) {
+ super(glucose);
+ pSpeed = addParam("Fast" , .55f);
+ pDensity = addParam("Dens" , .5f);
+ pSharp = addParam("Shrp" , 0);
+ pSymm = addPick("Symmetry" , 0, 3, new String[] {"None", "X", "Y", "Radial"} );
+ pChoose = addPick("Animation", 6, 7, new String[] {"Drip", "Cloud", "Rain", "Fire", "Machine", "Spark","VWave", "Wave"} );
+ for (int i=0; i<_ND; i++) N[i] = new NDat();
+ }
+
+ public void onActive() { zTime = random(500); zTheta=0; rtime = 0; ttime = 0; }
+
+ public void StartRun(double deltaMs) {
+ zTime += deltaMs*(val(pSpeed)-.5f)*.002f ;
+ zTheta += deltaMs*(spin()-.5f)*.01f ;
+ rtime += deltaMs;
+ iSymm = pSymm.Cur();
+ zSin = sin(zTheta);
+ zCos = cos(zTheta);
+
+ if (pChoose.Cur() != CurAnim) {
+ CurAnim = pChoose.Cur(); ttime = rtime;
+ pSpin .reset(); zTheta = 0;
+ pDensity .reset(); pSpeed .reset();
+ for (int i=0; i<_ND; i++) { N[i].isActive = false; }
+
+ switch(CurAnim) {
+ // hue xz yz zz den mph angle
+ case 0: N[0].set(0 ,75 ,75 ,150,45 ,3 ,0 ); pSharp.setValue(1 ); break; // drip
+ case 1: N[0].set(0 ,100,100,200,45 ,3 ,180); pSharp.setValue(0 ); break; // clouds
+ case 2: N[0].set(0 ,2 ,400,2 ,20 ,3 ,0 ); pSharp.setValue(.5f); break; // rain
+ case 3: N[0].set(40 ,100,100,200,10 ,1 ,180);
+ N[1].set(0 ,100,100,200,10 ,5 ,180); pSharp.setValue(0 ); break; // fire 1
+ case 4: N[0].set(0 ,40 ,40 ,40 ,15 ,2.5f,180);
+ N[1].set(20 ,40 ,40 ,40 ,15 ,4 ,0 );
+ N[2].set(40 ,40 ,40 ,40 ,15 ,2 ,90 );
+ N[3].set(60 ,40 ,40 ,40 ,15 ,3 ,-90); pSharp.setValue(.5f); break; // machine
+ case 5: N[0].set(0 ,400,100,2 ,15 ,3 ,90 );
+ N[1].set(20 ,400,100,2 ,15 ,2.5f,0 );
+ N[2].set(40 ,100,100,2 ,15 ,2 ,180);
+ N[3].set(60 ,100,100,2 ,15 ,1.5f,270); pSharp.setValue(.5f); break; // spark
+ }
+ }
+
+ for (int i=0; i<_ND; i++) if (N[i].Active()) {
+ N[i].sinAngle = sin(radians(N[i].angle));
+ N[i].cosAngle = cos(radians(N[i].angle));
+ }
+ }
+
+ public int CalcPoint(PVector p) {
+ int c = 0;
+ rotateZ(p, mCtr, zSin, zCos);
+
+ if (CurAnim == 6 || CurAnim == 7) {
+ setNorm(p);
+ return lx.hsb(lxh(),100, 100 * (
+ constrain(1-50*(1-val(pDensity))*abs(p.y-sin(zTime*10 + p.x*(300))*.5f - .5f),0,1) +
+ (CurAnim == 7 ? constrain(1-50*(1-val(pDensity))*abs(p.x-sin(zTime*10 + p.y*(300))*.5f - .5f),0,1) : 0))
+ );
+ }
+
+ if (iSymm == XSym && p.x > mMax.x/2) p.x = mMax.x-p.x;
+ if (iSymm == YSym && p.y > mMax.y/2) p.y = mMax.y-p.y;
+
+ for (int i=0;i<_ND; i++) if (N[i].Active()) {
+ NDat n = N[i];
+ float zx = zTime * n.speed * n.sinAngle,
+ zy = zTime * n.speed * n.cosAngle;
+
+ float b = (iSymm==RadSym ? noise(zTime*n.speed+n.xoff-p.dist(mCtr)/n.xz)
+ : noise(p.x/n.xz+zx+n.xoff,p.y/n.yz+zy+n.yoff,p.z/n.zz+n.zoff))
+ *1.8f;
+
+ b += n.den/100 -.4f + val(pDensity) -1;
+ c = blendColor(c,lx.hsb(lxh()+n.hue,100,c1c(b)),ADD);
+ }
+ return c;
+ }
+}
+//----------------------------------------------------------------------------------------------------------------------------------
+public class Play extends DPat
+{
+ public class rAngle {
+ float prvA, dstA, c;
+ float prvR, dstR, r;
+ float _cos, _sin, x, y;
+ public float fixAngle (float a, float b) { return a<b ?
+ (abs(a-b) > abs(a+2*PI-b) ? a : a+2*PI) :
+ (abs(a-b) > abs(a-2*PI-b) ? a : a-2*PI) ; }
+ public float getX(float r) { return mCtr.x + _cos*r; }
+ public float getY(float r) { return mCtr.y + _sin*r; }
+ public void move() { c = interp(t,prvA,dstA);
+ r = interp(t,prvR,dstR);
+ _cos = cos(c); _sin = sin(c);
+ x = getX(r); y = getY(r); }
+ public void set() { prvA = dstA; dstA = random(2*PI); prvA = fixAngle(prvA, dstA);
+ prvR = dstR; dstR = random(mCtr.y); }
+ }
+
+ BasicParameter pAmp, pRadius, pBounce;
+ Pick pTimePattern, pTempoMult, pShape;
+
+ ArrayList<rWave> waves = new ArrayList<rWave>(10);
+
+ int nBeats = 0;
+ float t,amp,rad,bnc,zTheta=0;
+
+ rAngle a1 = new rAngle(), a2 = new rAngle(),
+ a3 = new rAngle(), a4 = new rAngle();
+ PVector cPrev = new PVector(), cRand = new PVector(),
+ cMid = new PVector(), V = new PVector(),
+ theta = new PVector(), tSin = new PVector(),
+ tCos = new PVector(), cMidNorm = new PVector(),
+ Pn = new PVector();
+ float LastBeat=3, LastMeasure=3;
+ int curRandTempo = 1, curRandTPat = 1;
+
+ Play(GLucose glucose) {
+ super(glucose);
+ pRadius = addParam("Rad" , .1f );
+ pBounce = addParam("Bnc" , .2f );
+ pAmp = addParam("Amp" , .2f );
+ pTempoMult = addPick ("TMult" , 5 , 5 , new String[] {"1x", "2x", "4x", "8x", "16x", "Rand" } );
+ pTimePattern= addPick ("TPat" , 7 , 7 , new String[] {"Bounce", "Sin", "Roll", "Quant", "Accel", "Deccel", "Slide", "Rand"} );
+ pShape = addPick ("Shape" , 7 , 15 , new String[] {"Line", "Tap", "V", "RandV",
+ "Pyramid", "Wings", "W2", "Clock",
+ "Triangle", "Quad", "Sphere", "Cone",
+ "Noise", "Wave", "?", "?"} );
+ }
+
+ public class rWave {
+ float v0, a0, x0, t,damp,a;
+ boolean bDone=false;
+ final float len=8;
+ rWave(float _x0, float _a0, float _v0, float _damp) { x0=_x0*len; a0=_a0; v0=_v0; t=0; damp = _damp; }
+ public void move(double deltaMs) {
+ t += deltaMs*.001f;
+ if (t>4) bDone=true;
+ }
+ public float val(float _x) {
+ _x*=len;
+ float dist = t*v0 - abs(_x-x0);
+ if (dist<0) { a=1; return 0; }
+ a = a0*exp(-dist*damp) * exp(-abs(_x-x0)/(.2f*len)); // * max(0,1-t/dur)
+ return -a*sin(dist);
+ }
+ }
+
+ public void onReset() { zTheta=0; super.onReset(); }
+ public void onActive() {
+ zTheta=0;
+ while (lx.tempo.bpm() > 40) lx.tempo.setBpm(lx.tempo.bpm()/2);
+ }
+
+ int KeyPressed = -1;
+ public boolean noteOn(Note note) {
+ int row = note.getPitch(), col = note.getChannel();
+ if (row == 57) {KeyPressed = col; return true; }
+ return super.noteOn(note);
+ }
+
+ public void StartRun(double deltaMs) {
+ t = lx.tempo.rampf();
+ amp = pAmp .getValuef();
+ rad = pRadius .getValuef();
+ bnc = pBounce .getValuef();
+ zTheta += deltaMs*(val(pSpin)-.5f)*.01f;
+
+ theta .set(val(pRotX)*PI*2, val(pRotY)*PI*2, val(pRotZ)*PI*2 + zTheta);
+ tSin .set(sin(theta.x), sin(theta.y), sin(theta.z));
+ tCos .set(cos(theta.x), cos(theta.y), cos(theta.z));
+
+ if (t<LastMeasure) {
+ if (random(3) < 1) { curRandTempo = PApplet.parseInt(random(4)); if (curRandTempo == 3) curRandTempo = PApplet.parseInt(random(4)); }
+ if (random(3) < 1) { curRandTPat = pShape.Cur() > 6 ? 2+PApplet.parseInt(random(5)) : PApplet.parseInt(random(7)); }
+ } LastMeasure = t;
+
+ int nTempo = pTempoMult .Cur(); if (nTempo == 5) nTempo = curRandTempo;
+ int nTPat = pTimePattern.Cur(); if (nTPat == 7) nTPat = curRandTPat ;
+
+ switch (nTempo) {
+ case 0: t = t; break;
+ case 1: t = (t*2.f )%1.f; break;
+ case 2: t = (t*4.f )%1.f; break;
+ case 3: t = (t*8.f )%1.f; break;
+ case 4: t = (t*16.f)%1.f; break;
+ }
+
+ int i=0; while (i< waves.size()) {
+ rWave w = waves.get(i);
+ w.move(deltaMs); if (w.bDone) waves.remove(i); else i++;
+ }
+
+ if ((t<LastBeat && pShape.Cur()!=14) || KeyPressed>-1) {
+ waves.add(new rWave(
+ KeyPressed>-1 ? map(KeyPressed,0,7,0,1) : random(1), // location
+ bnc*10, // bounciness
+ 7, // velocity
+ 2*(1-amp))); // dampiness
+ KeyPressed=-1;
+ if (waves.size() > 5) waves.remove(0);
+ }
+
+ if (t<LastBeat) {
+ cPrev.set(cRand); setRand(cRand);
+ a1.set(); a2.set(); a3.set(); a4.set();
+ } LastBeat = t;
+
+ switch (nTPat) {
+ case 0: t = sin(PI*t); break; // bounce
+ case 1: t = norm(sin(2*PI*(t+PI/2)),-1,1); break; // sin
+ case 2: t = t; break; // roll
+ case 3: t = constrain(PApplet.parseInt(t*8)/7.f,0,1); break; // quant
+ case 4: t = t*t*t; break; // accel
+ case 5: t = sin(PI*t*.5f); break; // deccel
+ case 6: t = .5f*(1-cos(PI*t)); break; // slide
+ }
+
+ cMid.set (cPrev); interpolate(t,cMid,cRand);
+ cMidNorm.set (cMid); setNorm(cMidNorm);
+ a1.move(); a2.move(); a3.move(); a4.move();
+ }
+
+ public int CalcPoint(PVector Px) {
+ if (theta.x != 0) rotateX(Px, mCtr, tSin.x, tCos.x);
+ if (theta.y != 0) rotateY(Px, mCtr, tSin.y, tCos.y);
+ if (theta.z != 0) rotateZ(Px, mCtr, tSin.z, tCos.z);
+
+ Pn.set(Px); setNorm(Pn);
+
+ float mp = min(Pn.x, Pn.z);
+ float yt = map(t,0,1,.5f-bnc/2,.5f+bnc/2);
+ float r,d;
+
+ switch (pShape.Cur()) {
+ case 0: V.set(Pn.x, yt , Pn.z); break; // bouncing line
+ case 1: V.set(Pn.x, map(cos(PI*t * Pn.x),-1,1,0,1) , Pn.z); break; // top tap
+ case 2: V.set(Pn.x, bnc*map(Pn.x<.5f?Pn.x:1-Pn.x,0,.5f ,0,t-.5f)+.5f, Pn.z); break; // V shape
+ case 3: V.set(Pn.x, Pn.x < cMidNorm.x ? map(Pn.x,0,cMidNorm.x, .5f,yt) :
+ map(Pn.x,cMidNorm.x,1, yt,.5f), Pn.z); break; // Random V shape
+
+ case 4: V.set(Pn.x, .5f*(Pn.x < cMidNorm.x ? map(Pn.x,0,cMidNorm.x, .5f,yt) :
+ map(Pn.x,cMidNorm.x,1, yt,.5f)) +
+ .5f*(Pn.z < cMidNorm.z ? map(Pn.z,0,cMidNorm.z, .5f,yt) :
+ map(Pn.z,cMidNorm.z,1, yt,.5f)), Pn.z); break; // Random Pyramid shape
+
+ case 5: V.set(Pn.x, bnc*map((Pn.x-.5f)*(Pn.x-.5f),0,.25f,0,t-.5f)+.5f, Pn.z); break; // wings
+ case 6: V.set(Pn.x, bnc*map((mp -.5f)*(mp -.5f),0,.25f,0,t-.5f)+.5f, Pn.z); break; // wings
+
+ case 7: d = min(
+ distToSeg(Px.x, Px.y, a1.getX(70),a1.getY(70), mCtr.x, mCtr.y),
+ distToSeg(Px.x, Px.y, a2.getX(40),a2.getY(40), mCtr.x, mCtr.y));
+ d = constrain(30*(rad*40-d),0,100);
+ return lx.hsb(lxh(),100, d); // clock
+
+ case 8: r = amp*200 * map(bnc,0,1,1,sin(PI*t));
+ d = min(
+ distToSeg(Px.x, Px.y, a1.getX(r),a1.getY(r), a2.getX(r),a2.getY(r)),
+ distToSeg(Px.x, Px.y, a2.getX(r),a2.getY(r), a3.getX(r),a3.getY(r)),
+ distToSeg(Px.x, Px.y, a3.getX(r),a3.getY(r), a1.getX(r),a1.getY(r)) // triangle
+ );
+ d = constrain(30*(rad*40-d),0,100);
+ return lx.hsb(lxh(),100, d); // clock
+
+ case 9: r = amp*200 * map(bnc,0,1,1,sin(PI*t));
+ d = min(
+ distToSeg(Px.x, Px.y, a1.getX(r),a1.getY(r), a2.getX(r),a2.getY(r)),
+ distToSeg(Px.x, Px.y, a2.getX(r),a2.getY(r), a3.getX(r),a3.getY(r)),
+ distToSeg(Px.x, Px.y, a3.getX(r),a3.getY(r), a4.getX(r),a4.getY(r)),
+ distToSeg(Px.x, Px.y, a4.getX(r),a4.getY(r), a1.getX(r),a1.getY(r)) // quad
+ );
+ d = constrain(30*(rad*40-d),0,100);
+ return lx.hsb(lxh(),100, d); // clock
+
+ case 10:
+ r = map(bnc,0,1,a1.r,amp*200*sin(PI*t));
+ return lx.hsb(lxh(),100,c1c(.9f+2*rad - dist(Px.x,Px.y,a1.getX(r),a1.getY(r))*.03f) ); // sphere
+
+ case 11:
+ Px.z=mCtr.z; cMid.z=mCtr.z;
+ return lx.hsb(lxh(),100,c1c(1 - calcCone(Px,cMid,mCtr) * 0.02f > .5f?1:0)); // cone
+
+ case 12: return lx.hsb(lxh() + noise(Pn.x,Pn.y,Pn.z + (NoiseMove+50000)/1000.f)*200,
+ 85,c1c(Pn.y < noise(Pn.x + NoiseMove/2000.f,Pn.z)*(1+amp)-amp/2.f-.1f ? 1 : 0)); // noise
+
+ case 13:
+ case 14: float y=0; for (rWave w : waves) y += .5f*w.val(Pn.x); // wave
+ V.set(Pn.x, .7f+y, Pn.z);
+ break;
+
+ default: return lx.hsb(0,0,0);
+ }
+
+ return lx.hsb(lxh(), 100, c1c(1 - V.dist(Pn)/rad));
+ }
+}
+//----------------------------------------------------------------------------------------------------------------------------------
+boolean dDebug = false;
+class dCursor {
+ dVertex vCur, vNext, vDest;
+ float destSpeed;
+ int posStop, pos,posNext; // 0 - 65535
+ int clr;
+
+ dCursor() {}
+
+ public boolean isDone () { return pos==posStop; }
+ public boolean atDest () { return vCur.s==vDest.s ||
+ xyDist(vCur.getPoint(0), vDest.getPoint(0)) < 12 ||
+ xyDist(vCur.getPoint(0), vDest.getPoint(15))< 12;}
+ public void setCur (dVertex _v, int _p) { p2=null; vCur=_v; pos=_p; pickNext(); }
+ public void setCur (dPixel _p) { setCur(_p.v, _p.pos); }
+ public void setNext (dVertex _v, int _p, int _s) { vNext = _v; posNext = _p<<12; posStop = _s<<12; }
+ public void setDest (dVertex _v, float _speed) { vDest = _v; destSpeed = _speed; }
+ public void onDone () { setCur(vNext, posNext); pickNext(); }
+
+ float minDist;
+ int nTurns;
+ boolean bRandEval;
+
+ public void evaluate(dVertex v, int p, int s) {
+ if (v == null) return; ++nTurns;
+ if (bRandEval) {
+ if (random(nTurns) < 1) setNext(v,p,s); return; }
+ else {
+ float d = xyDist(v.getPoint(15), vDest.getPoint(0));
+ if (d < minDist) { minDist=d; setNext(v,p,s); }
+ if (d == minDist && random(2)<1) { minDist=d; setNext(v,p,s); }
+ }
+ }
+
+ public void evalTurn(dTurn t) {
+ if (t == null || t.pos0<<12 <= pos) return;
+ evaluate(t.v , t.pos1, t.pos0);
+ evaluate(t.v.opp, 16-t.pos1, t.pos0);
+ }
+
+ public void pickNext() {
+ bRandEval = random(.05f+destSpeed) < .05f; minDist=500; nTurns=0;
+ evaluate(vCur.c0, 0, 16); evaluate(vCur.c1, 0, 16);
+ evaluate(vCur.c2, 0, 16); evaluate(vCur.c3, 0, 16);
+ evalTurn(vCur.t0); evalTurn(vCur.t1);
+ evalTurn(vCur.t2); evalTurn(vCur.t3);
+ }
+
+ Point p1, p2; int i2;
+
+ public int draw(int nAmount, SCPattern pat) {
+ int nFrom = (pos ) >> 12;
+ int nMv = min(nAmount, posStop-pos);
+ int nTo = min(15,(pos+nMv) >> 12);
+ dVertex v = vCur;
+
+ if (dDebug) { p1 = v.getPoint(nFrom); float d = (p2 == null ? 0 : pointDist(p1,p2)); if (d>5) { println("too wide! quitting: " + d); exit(); }}
+ for (int i = nFrom; i <= nTo; i++) { pat.getColors()[v.ci + v.dir*i ] = clr; }
+ if (v.same != null) for (int i = nFrom; i <= nTo; i++) { pat.getColors()[v.same.ci + v.same.dir*i] = clr; }
+
+ if (dDebug) { p2 = v.getPoint(nTo); i2 = nTo; }
+
+ pos += nMv; return nAmount - nMv;
+ }
+}
+
+//----------------------------------------------------------------------------------------------------------------------------------
+class Worms extends SCPattern {
+ float StripsPerSec = 10;
+ float TrailTime = 3000;
+ int numCursors = 50;
+ ArrayList<dCursor> cur = new ArrayList<dCursor>(30);
+
+ private GraphicEQ eq = null;
+
+ private BasicParameter pBeat = new BasicParameter("BEAT", 0);
+ private BasicParameter pSpeed = new BasicParameter("FAST", .2f);
+ private BasicParameter pBlur = new BasicParameter("BLUR", .3f);
+ private BasicParameter pWorms = new BasicParameter("WRMS", .3f);
+ private BasicParameter pConfusion = new BasicParameter("CONF", .1f);
+ private BasicParameter pEQ = new BasicParameter("EQ" , 0);
+ private BasicParameter pSpawn = new BasicParameter("DIR" , 0);
+ private BasicParameter pColor = new BasicParameter("CLR" , .1f);
+
+ float zMidLat = 82.f;
+ float nConfusion;
+ private final Click moveChase = new Click(1000);
+
+ PVector middle;
+ public int AnimNum() { return floor(pSpawn.getValuef()*(4-.01f)); }
+ public float randX() { return random(model.xMax-model.xMin)+model.xMin; }
+ public float randY() { return random(model.yMax-model.yMin)+model.yMin; }
+ public PVector randEdge() {
+ return random(2) < 1 ? new PVector(random(2)<1 ? model.xMin:model.xMax, randY(), zMidLat) :
+ new PVector(randX(), random(2)<1 ? model.yMin:model.yMax, zMidLat) ;
+ }
+
+ Worms(GLucose glucose) {
+ super(glucose);
+ addModulator(moveChase).start();
+ addParameter(pBeat); addParameter(pSpeed);
+ addParameter(pBlur); addParameter(pWorms);
+ addParameter(pEQ); addParameter(pConfusion);
+ addParameter(pSpawn); addParameter(pColor);
+
+ middle = new PVector(1.5f*model.cx, 1.5f*model.cy, 71);
+ if (lattice == null) lattice = new dLattice();
+ for (int i=0; i<numCursors; i++) { dCursor c = new dCursor(); reset(c); cur.add(c); }
+ onParameterChanged(pEQ); setNewDest();
+ }
+
+ public void onParameterChanged(LXParameter parameter) {
+ super.onParameterChanged(parameter);
+ nConfusion = 1-pConfusion.getValuef();
+ for (int i=0; i<numCursors; i++) {
+ if (parameter==pSpawn) reset(cur.get(i));
+ cur.get(i).destSpeed = nConfusion;
+ }
+ }
+
+ public float getClr() { return lx.getBaseHuef() + random(pColor.getValuef()*300); }
+ public void reset(dCursor c) {
+ switch(AnimNum()) {
+ case 0: c.clr = lx.hsb(getClr(),100,100); // middle to edges
+ c.setDest(lattice.getClosest(randEdge()).v, nConfusion);
+ c.setCur (lattice.getClosest(middle));
+ break;
+
+ case 1: c.clr = lx.hsb(getClr(),100,100); // top to bottom
+ float xLin = randX();
+ c.setDest(lattice.getClosest(new PVector(xLin, 0 , zMidLat)).v, nConfusion);
+ c.setCur (lattice.getClosest(new PVector(xLin, model.yMax, zMidLat)));
+ break;
+
+ case 2: c.clr = lx.hsb(getClr(),100,100); break; // chase a point around
+
+ case 3: boolean bLeft = random(2)<1;
+ c.clr = lx.hsb(getClr()+random(120),100,100); // sideways
+ float yLin = randX();
+ c.setDest(lattice.getClosest(new PVector(bLeft ? 0 : model.xMax,yLin,zMidLat)).v, nConfusion);
+ c.setCur (lattice.getClosest(new PVector(bLeft ? model.xMax : 0,yLin,zMidLat)));
+ break;
+ }
+ if (pBlur.getValuef() == 1 && random(2)<1) c.clr = lx.hsb(0,0,0);
+ }
+
+ public void setNewDest() {
+ if (AnimNum() != 2) return;
+ PVector dest = new PVector(randX(), randY(), zMidLat);
+ for (int i=0; i<numCursors; i++) {
+ cur.get(i).setDest(lattice.getClosest(dest).v, nConfusion);
+ cur.get(i).clr = lx.hsb(getClr()+75,100,100); // chase a point around
+ }
+ }
+
+ public void run(double deltaMs) {
+ if (deltaMs > 100) return;
+ if (moveChase.click()) setNewDest();
+
+ float fBass=0, fTreble=0;
+ if (pEQ.getValuef()>0) { // EQ
+ eq.run(deltaMs);
+ fBass = eq.getAverageLevel(0, 4);
+ fTreble = eq.getAverageLevel(eq.numBands-7, 7);
+ }
+
+ if (pBlur.getValuef() < 1) { // trails
+ for (int i=0,s=model.points.size(); i<s; i++) {
+ int c = colors[i]; float b = lx.b(c);
+ if (b>0) colors[i] = lx.hsb(lx.h(c), lx.s(c), constrain((float)(b-100*deltaMs/(pBlur.getValuef()*TrailTime)),0,100));
+ }
+ }
+
+ int nWorms = floor(pWorms.getValuef() * numCursors *
+ map(pEQ.getValuef(),0,1,1,constrain(2*fTreble,0,1)));
+
+ for (int i=0; i<nWorms; i++) {
+ dCursor c = cur.get(i);
+ int nLeft = floor((float)deltaMs*.001f*StripsPerSec * 65536 * (5*pSpeed.getValuef()));
+ nLeft *= (1 - lx.tempo.rampf()*pBeat.getValuef());
+ while(nLeft > 0) {
+ nLeft = c.draw(nLeft,this); if (!c.isDone()) continue;
+ c.onDone(); if (c.atDest()) reset(c);
+ }
+ }
+ }
+
+
+ public void onActive() { if (eq == null) {
+ eq = new GraphicEQ(lx, 16); eq.slope.setValue(0.6f);
+ eq.level.setValue(0.65f); eq.range.setValue(0.35f);
+ eq.release.setValue(0.4f);
+ }}
+}
+//----------------------------------------------------------------------------------------------------------------------------------
+class GenericController {
+ GenericController(){}
+ public void RotateKnob(int type, int num, float val){
+ LXParameter p = null;
+ if(type==0) {
+ p = glucose.patternKnobs.get(num);
+ if(p!=null) { p.setValue(val); }
+ }
+ if(type==1) {
+ p = glucose.transitionKnobs.get(num);
+ if(p!=null) { p.setValue(val); }
+ }
+ if(type==2) {
+ p = glucose.effectKnobs.get(num);
+ if(p!=null) { p.setValue(val); }
+ }
+ }
+}
+
+class MidiController extends GenericController {
+ MidiController() {
+ super();
+ }
+}
+//PApplet xparent; // be sure to set
+
+
+
+OscP5 listener;
+// Setup OSC
+//listener = new OscP5(this,7022);
+
+//boolean[] noteState = new boolean[16];
+//
+//void controllerChangeReceived(rwmidi.Controller cc) {
+// if (debugMode) {
+// println("CC: " + cc.toString());
+// }
+// if(cc.getCC()==1){
+// for(int i=0; i<16; i++){
+// if(noteState[i] && i<8) { LXParameter p = glucose.patternKnobs.get(i); p.setValue(cc.getValue()/127.0); }
+// else if(noteState[i] && i<12) { LXParameter p = glucose.transitionKnobs.get(i-8); p.setValue(cc.getValue()/127.0); }
+// else if(noteState[i] && i<16) { LXParameter p = glucose.effectKnobs.get(i-12); p.setValue(cc.getValue()/127.0); }
+// }
+// }
+//}
+//
+//void noteOnReceived(Note note) {
+// if (debugMode) {
+// println("Note On: " + note.toString());
+// }
+// int pitch = note.getPitch();
+// if(pitch>=36 && pitch <36+16){
+// noteState[pitch-36]=true;
+// }
+//}
+//
+//void noteOffReceived(Note note) {
+// if (debugMode) {
+// println("Note Off: " + note.toString());
+// }
+// int pitch = note.getPitch();
+// if(pitch>=36 && pitch <36+16){
+// noteState[pitch-36]=false;
+// }
+//}
+//
+//void oscEvent(OscMessage theOscMessage) {
+// println(theOscMessage);
+// LXPattern currentPattern = lx.getPattern();
+// if (currentPattern instanceof OSCPattern) {
+// ((OSCPattern)currentPattern).oscEvent(theOscMessage);
+// }
+//}
+//
+
+
+class ObjectMuckerEffect extends SCEffect {
+ ObjectMuckerEffect(GLucose glucose) {
+ super(glucose);
+ }
+ public void apply(int[] colors){
+ /*for(Strip s: model.strips){
+ for(int i=0; i<s.points.size(); i++){
+ int index = s.points.get(i).index;
+ color c = colors[index];
+ colors[index] = lx.hsb((i*22.5), saturation(c), brightness(c));
+ }
+ }*/
+ }
+}
+
+class BlendFrames extends SCEffect {
+ int fcount;
+ int frames[][];
+ int maxfbuf;
+ int blendfactor;
+ BlendFrames(GLucose glucose) {
+ super(glucose);
+ maxfbuf = 30;
+ blendfactor=30;
+ fcount=0;
+ frames = new int[maxfbuf][];
+ for(int i=0; i<maxfbuf; i++){
+ frames[i] = new int[model.points.size()];
+ }
+ }
+ public void apply(int[] colors) {
+ if(fcount<maxfbuf){
+ for(int i=0; i<colors.length; i++){
+ frames[(maxfbuf-1)-fcount][i]=colors[i];
+ }
+ fcount++;
+ return;
+ } else {
+ for(int i=maxfbuf-1; i>0; i--){
+ frames[i] = frames[i-1];
+ }
+ frames[0] = new int[model.points.size()];
+
+ for(int i=0; i<colors.length; i++){
+ int r,g,b;
+ r=g=b=0;
+ for(int j=0; j<blendfactor; j++){
+ if(j==0) { frames[0][i] = colors[i]; }
+ r += ((frames[j][i] >> 16) & 0xFF);
+ g += ((frames[j][i] >> 8) & 0xFF);
+ b += ((frames[j][i] >> 0) & 0xFF);
+ }
+ r/=blendfactor;
+ g/=blendfactor;
+ b/=blendfactor;
+ colorMode(ARGB);
+ colors[i] = (0xFF << 24) | (r << 16) | (g << 8) | b;
+ colorMode(HSB);
+ }
+
+ }
+ }
+}
+
+
+
+
+
+
+
+abstract class OSCPattern extends SCPattern {
+ public OSCPattern(GLucose glucose){super(glucose);}
+ public abstract void oscEvent(OscMessage msg);
+}
+
+class Ball {
+ public int lastSeen;
+ public float x,y;
+ public Ball(){
+ x=y=lastSeen=0;
+ }
+}
+
+class OSC_Balls extends OSCPattern {
+ Ball[] balls;
+ public OSC_Balls(GLucose glucose){
+ super(glucose);
+ balls = new Ball[20];
+ for(int i=0; i<balls.length; i++) { balls[i] = new Ball(); }
+ }
+ public void oscEvent(OscMessage msg){
+ String pattern[] = split(msg.addrPattern(), "/");
+ int ballnum = PApplet.parseInt(pattern[3]);
+ balls[ballnum].lastSeen=millis();
+ balls[ballnum].x = msg.get(0).floatValue();
+ balls[ballnum].y = msg.get(1).floatValue();
+ }
+
+ public void run(double deltaMs){
+ for(Point p: model.points){ colors[p.index]=0; }
+ for(int i=1; i<balls.length; i++){
+ if(millis() - balls[i].lastSeen < 1000) {
+ for(Point p: model.points){
+ int x = PApplet.parseInt(balls[i].x * 255.0f);
+ int y = PApplet.parseInt(balls[i].y * 127.0f);
+ if(p.x < x+4 && p.x > x-4 && p.y < y+4 && p.y > y-4) { colors[p.index] = 0xffFF0000; }
+ }
+ }
+ }
+ }
+}
+
+
+
+
+/*class ScreenScrape extends SCPattern {
+ PImage pret;
+ ScreenShot ss;
+ public ScreenScrape(GLucose glucose) {
+ super(glucose);
+ System.loadLibrary("ScreenShot");
+ pret = new PImage(8, 128, ARGB);
+ ss = new ScreenShot();
+ }
+ void run(double deltaMs){
+ int x=(1366/2)+516;
+ int y=768-516;
+ int w=8;
+ int h=128;
+ pret.pixels = ss.getScreenShotJNI2(x, y, w, h);
+ //for(int i=0; i<px.length; i++){ pret.pixels[i] = px[i]; }
+ //println(pret.get(10,10));
+ for(Point p: model.points){
+ colors[p.index] = pret.get((int(p.x)/8)*8, 128-int(p.y));
+ }
+ }
+}*/
+
+//----------------------------------------------------------------------------------------------------------------------------------
+int NumApcRows=4, NumApcCols=8;
+
+public boolean btwn (int a,int b,int c) { return a >= b && a <= c; }
+public boolean btwn (double a,double b,double c) { return a >= b && a <= c; }
+public float interp (float a, float b, float c) { return (1-a)*b + a*c; }
+public float randctr (float a) { return random(a) - a*.5f; }
+public float min (float a, float b, float c, float d) { return min(min(a,b),min(c,d)); }
+public float pointDist(Point p1, Point p2) { return dist(p1.x,p1.y,p1.z,p2.x,p2.y,p2.z); }
+public float xyDist (Point p1, Point p2) { return dist(p1.x,p1.y,p2.x,p2.y); }
+public float distToSeg(float x, float y, float x1, float y1, float x2, float y2) {
+ float A = x - x1, B = y - y1, C = x2 - x1, D = y2 - y1;
+ float dot = A * C + B * D, len_sq = C * C + D * D;
+ float xx, yy,param = dot / len_sq;
+
+ if (param < 0 || (x1 == x2 && y1 == y2)) { xx = x1; yy = y1; }
+ else if (param > 1) { xx = x2; yy = y2; }
+ else { xx = x1 + param * C;
+ yy = y1 + param * D; }
+ float dx = x - xx, dy = y - yy;
+ return sqrt(dx * dx + dy * dy);
+}
+
+public class Pick {
+ int NumPicks, Default ,
+ CurRow , CurCol ,
+ StartRow, EndRow ;
+ String tag , Desc[] ;
+
+ Pick (String label, int _Def, int _Num, int nStart, String d[]) {
+ NumPicks = _Num; Default = _Def;
+ StartRow = nStart; EndRow = StartRow + floor((NumPicks-1) / NumApcCols);
+ tag = label; Desc = d;
+ reset();
+ }
+
+ public int Cur() { return (CurRow-StartRow)*NumApcCols + CurCol; }
+ public String CurDesc() { return Desc[Cur()]; }
+ public void reset() { CurCol = Default % NumApcCols; CurRow = StartRow + Default / NumApcCols; }
+
+ public boolean set(int r, int c) {
+ if (!btwn(r,StartRow,EndRow) || !btwn(c,0,NumApcCols-1) ||
+ !btwn((r-StartRow)*NumApcCols + c,0,NumPicks-1)) return false;
+ CurRow=r; CurCol=c; return true;
+ }
+}
+
+public class DBool {
+ boolean def, b;
+ String tag;
+ int row, col;
+ public void reset() { b = def; }
+ public boolean set (int r, int c, boolean val) { if (r != row || c != col) return false; b = val; return true; }
+ DBool(String _tag, boolean _def, int _row, int _col) {
+ def = _def; b = _def; tag = _tag; row = _row; col = _col;
+ }
+}
+//----------------------------------------------------------------------------------------------------------------------------------
+public class DPat extends SCPattern
+{
+ ArrayList<Pick> picks = new ArrayList<Pick> ();
+ ArrayList<DBool> bools = new ArrayList<DBool> ();
+
+ PVector mMax, mCtr, mHalf;
+
+ MidiOutput APCOut;
+ int nMaxRow = 53;
+ float LastJog = -1;
+ float[] xWaveNz, yWaveNz;
+ int nPoint , nPoints;
+ PVector xyzJog = new PVector(), modmin;
+
+ float NoiseMove = random(10000);
+ BasicParameter pSpark, pWave, pRotX, pRotY, pRotZ, pSpin, pTransX, pTransY;
+ DBool pXsym, pYsym, pRsym, pXdup, pXtrip, pJog, pGrey;
+
+ public float lxh () { return lx.getBaseHuef(); }
+ public int c1c (float a) { return round(100*constrain(a,0,1)); }
+ public float interpWv(float i, float[] vals) { return interp(i-floor(i), vals[floor(i)], vals[ceil(i)]); }
+ public void setNorm (PVector vec) { vec.set(vec.x/mMax.x, vec.y/mMax.y, vec.z/mMax.z); }
+ public void setRand (PVector vec) { vec.set(random(mMax.x), random(mMax.y), random(mMax.z)); }
+ public void setVec (PVector vec, Point p) { vec.set(p.x, p.y, p.z); }
+ public void interpolate(float i, PVector a, PVector b) { a.set(interp(i,a.x,b.x), interp(i,a.y,b.y), interp(i,a.z,b.z)); }
+ public void StartRun(double deltaMs) { }
+ public float val (BasicParameter p) { return p.getValuef(); }
+ public int CalcPoint(PVector p) { return lx.hsb(0,0,0); }
+ public int blend3(int c1, int c2, int c3) { return blendColor(c1,blendColor(c2,c3,ADD),ADD); }
+
+ public void rotateZ (PVector p, PVector o, float nSin, float nCos) { p.set( nCos*(p.x-o.x) - nSin*(p.y-o.y) + o.x , nSin*(p.x-o.x) + nCos*(p.y-o.y) + o.y,p.z); }
+ public void rotateX (PVector p, PVector o, float nSin, float nCos) { p.set(p.x,nCos*(p.y-o.y) - nSin*(p.z-o.z) + o.y , nSin*(p.y-o.y) + nCos*(p.z-o.z) + o.z ); }
+ public void rotateY (PVector p, PVector o, float nSin, float nCos) { p.set( nSin*(p.z-o.z) + nCos*(p.x-o.x) + o.x,p.y, nCos*(p.z-o.z) - nSin*(p.x-o.x) + o.z ); }
+
+ public BasicParameter addParam(String label, double value) { BasicParameter p = new BasicParameter(label, value); addParameter(p); return p; }
+
+ PVector vT1 = new PVector(), vT2 = new PVector();
+ public float calcCone (PVector v1, PVector v2, PVector c) { vT1.set(v1); vT2.set(v2); vT1.sub(c); vT2.sub(c);
+ return degrees(PVector.angleBetween(vT1,vT2)); }
+
+ public Pick addPick(String name, int def, int _max, String[] desc) {
+ Pick P = new Pick(name, def, _max+1, nMaxRow, desc);
+ nMaxRow = P.EndRow + 1;
+ picks.add(P);
+ return P;
+ }
+
+ public boolean noteOff(Note note) {
+ int row = note.getPitch(), col = note.getChannel();
+ for (int i=0; i<bools.size(); i++) if (bools.get(i).set(row, col, false)) { presetManager.dirty(this); return true; }
+ updateLights(); return false;
+ }
+
+ public boolean noteOn(Note note) {
+ int row = note.getPitch(), col = note.getChannel();
+ for (int i=0; i<picks.size(); i++) if (picks.get(i).set(row, col)) { presetManager.dirty(this); return true; }
+ for (int i=0; i<bools.size(); i++) if (bools.get(i).set(row, col, true)) { presetManager.dirty(this); return true; }
+ println("row: " + row + " col: " + col); return false;
+ }
+
+ public void onInactive() { uiDebugText.setText(""); }
+ public void onReset() {
+ for (int i=0; i<bools .size(); i++) bools.get(i).reset();
+ for (int i=0; i<picks .size(); i++) picks.get(i).reset();
+ presetManager.dirty(this);
+ updateLights();
+ }
+
+ DPat(GLucose glucose) {
+ super(glucose);
+
+ pSpark = addParam("Sprk", 0);
+ pWave = addParam("Wave", 0);
+ pTransX = addParam("TrnX", .5f);
+ pTransY = addParam("TrnY", .5f);
+ pRotX = addParam("RotX", .5f);
+ pRotY = addParam("RotY", .5f);
+ pRotZ = addParam("RotZ", .5f);
+ pSpin = addParam("Spin", .5f);
+
+ nPoints = model.points.size();
+ pXsym = new DBool("X-SYM", false, 48, 0); bools.add(pXsym );
+ pYsym = new DBool("Y-SYM", false, 48, 1); bools.add(pYsym );
+ pRsym = new DBool("R-SYM", false, 48, 2); bools.add(pRsym );
+ pXdup = new DBool("X-DUP", false, 48, 3); bools.add(pXdup );
+ pJog = new DBool("JOG" , false, 48, 4); bools.add(pJog );
+ pGrey = new DBool("GREY" , false, 48, 5); bools.add(pGrey );
+
+ modmin = new PVector(model.xMin, model.yMin, model.zMin);
+ mMax = new PVector(model.xMax, model.yMax, model.zMax); mMax.sub(modmin);
+ mCtr = new PVector(); mCtr.set(mMax); mCtr.mult(.5f);
+ mHalf = new PVector(.5f,.5f,.5f);
+ xWaveNz = new float[ceil(mMax.y)+1];
+ yWaveNz = new float[ceil(mMax.x)+1];
+
+ //println (model.xMin + " " + model.yMin + " " + model.zMin);
+ //println (model.xMax + " " + model.yMax + " " + model.zMax);
+ //for (MidiOutputDevice o: RWMidi.getOutputDevices()) { if (o.toString().contains("APC")) { APCOut = o.createOutput(); break;}}
+ }
+
+ public float spin() {
+ float raw = val(pSpin);
+ if (raw <= 0.45f) {
+ return raw + 0.05f;
+ } else if (raw >= 0.55f) {
+ return raw - 0.05f;
+ }
+ return 0.5f;
+ }
+
+ public void setAPCOutput(MidiOutput output) {
+ APCOut = output;
+ }
+
+ public void updateLights() { if (APCOut == null) return;
+ for (int i = 0; i < NumApcRows; ++i)
+ for (int j = 0; j < 8; ++j) APCOut.sendNoteOn(j, 53+i, 0);
+ for (int i=0; i<picks .size(); i++) APCOut.sendNoteOn(picks.get(i).CurCol, picks.get(i).CurRow, 3);
+ for (int i=0; i<bools .size(); i++) if (bools.get(i).b) APCOut.sendNoteOn (bools.get(i).col, bools.get(i).row, 1);
+ else APCOut.sendNoteOff (bools.get(i).col, bools.get(i).row, 0);
+ }
+
+ public void run(double deltaMs)
+ {
+ if (deltaMs > 100) return;
+
+ if (this == midiEngine.getFocusedDeck().getActivePattern()) {
+ String Text1="", Text2="";
+ for (int i=0; i<bools.size(); i++) if (bools.get(i).b) Text1 += " " + bools.get(i).tag + " ";
+ for (int i=0; i<picks.size(); i++) Text1 += picks.get(i).tag + ": " + picks.get(i).CurDesc() + " ";
+ uiDebugText.setText(Text1, Text2);
+ }
+
+ NoiseMove += deltaMs; NoiseMove = NoiseMove % 1e7f;
+ StartRun (deltaMs);
+ PVector P = new PVector(), tP = new PVector(), pSave = new PVector();
+ PVector pTrans = new PVector(val(pTransX)*200-100, val(pTransY)*100-50,0);
+ nPoint = 0;
+
+ if (pJog.b) {
+ float tRamp = (lx.tempo.rampf() % .25f);
+ if (tRamp < LastJog) xyzJog.set(randctr(mMax.x*.2f), randctr(mMax.y*.2f), randctr(mMax.z*.2f));
+ LastJog = tRamp;
+ }
+
+ // precalculate this stuff
+ float wvAmp = val(pWave), sprk = val(pSpark);
+ if (wvAmp > 0) {
+ for (int i=0; i<ceil(mMax.x)+1; i++)
+ yWaveNz[i] = wvAmp * (noise(i/(mMax.x*.3f)-(2e3f+NoiseMove)/1500.f) - .5f) * (mMax.y/2.f);
+
+ for (int i=0; i<ceil(mMax.y)+1; i++)
+ xWaveNz[i] = wvAmp * (noise(i/(mMax.y*.3f)-(1e3f+NoiseMove)/1500.f) - .5f) * (mMax.x/2.f);
+ }
+
+ for (Point p : model.points) { nPoint++;
+ setVec(P,p);
+ P.sub(modmin);
+ P.sub(pTrans);
+ if (sprk > 0) {P.y += sprk*randctr(50); P.x += sprk*randctr(50); P.z += sprk*randctr(50); }
+ if (wvAmp > 0) P.y += interpWv(p.x-modmin.x, yWaveNz);
+ if (wvAmp > 0) P.x += interpWv(p.y-modmin.y, xWaveNz);
+ if (pJog.b) P.add(xyzJog);
+
+
+ int cNew, cOld = colors[p.index];
+ { tP.set(P); cNew = CalcPoint(tP); }
+ if (pXsym.b) { tP.set(mMax.x-P.x,P.y,P.z); cNew = blendColor(cNew, CalcPoint(tP), ADD); }
+ if (pYsym.b) { tP.set(P.x,mMax.y-P.y,P.z); cNew = blendColor(cNew, CalcPoint(tP), ADD); }
+ if (pRsym.b) { tP.set(mMax.x-P.x,mMax.y-P.y,mMax.z-P.z); cNew = blendColor(cNew, CalcPoint(tP), ADD); }
+ if (pXdup.b) { tP.set((P.x+mMax.x*.5f)%mMax.x,P.y,P.z); cNew = blendColor(cNew, CalcPoint(tP), ADD); }
+ if (pGrey.b) { cNew = lx.hsb(0, 0, lx.b(cNew)); }
+ colors[p.index] = cNew;
+ }
+ }
+}
+//----------------------------------------------------------------------------------------------------------------------------------
+class dTurn {
+ dVertex v;
+ int pos0, pos1;
+ dTurn(int _pos0, dVertex _v, int _pos1) { v = _v; pos0 = _pos0; pos1 = _pos1; }
+}
+
+class dVertex {
+ dVertex c0, c1, c2, c3, // connections on the cube
+ opp, same; // opp - same strip, opp direction
+ // same - same strut, diff strip, dir
+ dTurn t0, t1, t2, t3;
+ Strip s;
+ int dir, ci; // dir -- 1 or -1.
+ // ci -- color index
+
+ dVertex(Strip _s, Point _p) { s = _s; ci = _p.index; }
+ public Point getPoint(int i) { return s.points.get(dir>0 ? i : 15-i); }
+ public void setOpp(dVertex _opp) { opp = _opp; dir = (ci < opp.ci ? 1 : -1); }
+}
+//----------------------------------------------------------------------------------------------------------------------------------
+class dPixel { dVertex v; int pos; dPixel(dVertex _v, int _pos) { v=_v; pos=_pos; } }
+class dLattice {
+ public void addTurn (dVertex v0, int pos0, dVertex v1, int pos1) { dTurn t = new dTurn(pos0, v1, pos1);
+ if (v0.t0 == null) { v0.t0=t; return; }
+ if (v0.t1 == null) { v0.t1=t; return; }
+ if (v0.t2 == null) { v0.t2=t; return; }
+ if (v0.t3 == null) { v0.t3=t; return; }
+ }
+ public float dist2 (Strip s1, int pos1, Strip s2, int pos2) { return pointDist(s1.points.get(pos1), s2.points.get(pos2)); }
+ public float pd2 (Point p1, float x, float y, float z) { return dist(p1.x,p1.y,p1.z,x,y,z); }
+ public boolean sameSame (Strip s1, Strip s2) { return max(dist2(s1, 0, s2, 0), dist2(s1,15, s2,15)) < 5 ; } // same strut, same direction
+ public boolean sameOpp (Strip s1, Strip s2) { return max(dist2(s1, 0, s2,15), dist2(s1,15, s2,0 )) < 5 ; } // same strut, opp direction
+ public boolean sameBar (Strip s1, Strip s2) { return sameSame(s1,s2) || sameOpp(s1,s2); } // 2 strips on same strut
+
+
+ public void addJoint (dVertex v1, dVertex v2) {
+ // should probably replace parallel but further with the new one
+ if (v1.c0 != null && sameBar(v2.s, v1.c0.s)) return;
+ if (v1.c1 != null && sameBar(v2.s, v1.c1.s)) return;
+ if (v1.c2 != null && sameBar(v2.s, v1.c2.s)) return;
+ if (v1.c3 != null && sameBar(v2.s, v1.c3.s)) return;
+
+ if (v1.c0 == null) v1.c0 = v2;
+ else if (v1.c1 == null) v1.c1 = v2;
+ else if (v1.c2 == null) v1.c2 = v2;
+ else if (v1.c3 == null) v1.c3 = v2;
+ }
+
+ public dVertex v0(Strip s) { return (dVertex)s.obj1; }
+ public dVertex v1(Strip s) { return (dVertex)s.obj2; }
+
+ public dPixel getClosest(PVector p) {
+ dVertex v = null; int pos=0; float d = 500;
+
+ for (Strip s : glucose.model.strips) {
+ float nd = pd2(s.points.get(0),p.x,p.y,p.z); if (nd < d) { v=v0(s); d=nd; pos=0; }
+ if (nd > 30) continue;
+ for (int k=0; k<=15; k++) {
+ nd = pd2(s.points.get(k),p.x,p.y,p.z); if (nd < d) { v =v0(s); d=nd; pos=k; }
+ }
+ }
+ return random(2) < 1 ? new dPixel(v,pos) : new dPixel(v.opp,15-pos);
+ }
+
+ dLattice() {
+ lattice=this;
+
+ for (Strip s : glucose.model.strips) {
+ dVertex vrtx0 = new dVertex(s,s.points.get(0 )); s.obj1=vrtx0;
+ dVertex vrtx1 = new dVertex(s,s.points.get(15)); s.obj2=vrtx1;
+ vrtx0.setOpp(vrtx1); vrtx1.setOpp(vrtx0);
+ }
+
+ for (Strip s1 : glucose.model.strips) { for (Strip s2 : glucose.model.strips) {
+ if (s1.points.get(0).index < s2.points.get(0).index) continue;
+ int c=0;
+ if (sameSame(s1,s2)) { v0(s1).same = v0(s2); v1(s1).same = v1(s2);
+ v0(s2).same = v0(s1); v1(s2).same = v1(s1); continue; } // parallel
+ if (sameOpp (s1,s2)) { v0(s1).same = v1(s2); v1(s1).same = v0(s2);
+ v0(s2).same = v1(s1); v1(s2).same = v0(s1); continue; } // parallel
+ if (dist2(s1, 0, s2, 0) < 5) { c++; addJoint(v1(s1), v0(s2)); addJoint(v1(s2), v0(s1)); }
+ if (dist2(s1, 0, s2,15) < 5) { c++; addJoint(v1(s1), v1(s2)); addJoint(v0(s2), v0(s1)); }
+ if (dist2(s1,15, s2, 0) < 5) { c++; addJoint(v0(s1), v0(s2)); addJoint(v1(s2), v1(s1)); }
+ if (dist2(s1,15, s2,15) < 5) { c++; addJoint(v0(s1), v1(s2)); addJoint(v0(s2), v1(s1)); }
+ if (c>0) continue;
+
+ // Are they touching at all?
+ int pos1=0, pos2=0; float d = 100;
+
+ while (pos1 < 15 || pos2 < 15) {
+ float oldD = d;
+ if (pos1<15) { float d2 = dist2(s1, pos1+1, s2, pos2+0); if (d2 < d) { d=d2; pos1++; } }
+ if (pos2<15) { float d2 = dist2(s1, pos1+0, s2, pos2+1); if (d2 < d) { d=d2; pos2++; } }
+ if (d > 50 || oldD == d) break ;
+ }
+
+ if (d>5) continue;
+ addTurn(v0(s1), pos1, v0(s2), pos2); addTurn(v1(s1), 15-pos1, v0(s2), pos2);
+ addTurn(v0(s2), pos2, v0(s1), pos1); addTurn(v1(s2), 15-pos2, v0(s1), pos1);
+ }}
+ }
+}
+
+dLattice lattice;
+//----------------------------------------------------------------------------------------------------------------------------------
+
+class Graphic
+{
+ public boolean changed = false;
+ public int position = 0;
+ public ArrayList<Integer> graphicBuffer;
+ Graphic()
+ {
+ graphicBuffer = new ArrayList<Integer>();
+ }
+ public int width()
+ {
+ return graphicBuffer.size();
+ }
+
+
+};
+class Granim extends Graphic
+{
+ HashMap<String,Graphic> displayList;
+
+ Granim()
+ {
+ displayList = new HashMap<String,Graphic>();
+ }
+ public Graphic addGraphic(String name, Graphic g)
+ {
+ while(width()< g.position+1)
+ {
+ graphicBuffer.add(lx.hsb(0,0,0));
+ }
+ drawAll();
+ displayList.put(name , g);
+ changed =true;
+ return g;
+ }
+
+ public Graphic getGraphicByName(String name)
+ {
+ return displayList.get(name);
+ }
+
+ public void update()
+ {
+
+ for(Graphic g : displayList.values())
+ {
+ if(g instanceof Granim)
+ {
+ ((Granim) g).update();
+
+ }
+ changed = changed || g.changed;
+ if(changed)
+ {
+ while(width()< g.position + g.width())
+ {
+ graphicBuffer.add(lx.hsb(0,0,0));
+ }
+ if(g.changed)
+ {
+ drawOne(g);
+ g.changed =false;
+ }
+ }
+ }
+ changed = false;
+
+ }
+ public void drawOne(Graphic g)
+ {
+ graphicBuffer.addAll(g.position,g.graphicBuffer);
+ }
+ public void drawAll()
+ {
+ }
+};
+class GranimPattern extends SCPattern
+{
+ HashMap<String,Graphic> displayList;
+
+ GranimPattern(GLucose glucose)
+ {
+ super(glucose);
+ displayList = new HashMap<String,Graphic>();
+ }
+
+ public Graphic addGraphic(String name, Graphic g)
+ {
+ displayList.put(name,g);
+ return g;
+ }
+
+ public Graphic getGraphicByName(String name)
+ {
+ return displayList.get(name);
+ }
+
+ public void run(double deltaMs)
+ {
+ drawToPointList();
+ }
+ private Integer[] gbuffer;
+ public void drawToPointList()
+ {
+ for(Graphic g : displayList.values())
+ {
+ if(g instanceof Granim)
+ {
+ ((Granim) g).update();
+ }
+ List<Point> drawList = model.points.subList(Math.min(g.position,colors.length-1), Math.min(g.position + g.width(),colors.length-1));
+ //println("drawlistsize "+drawList.size());
+
+ gbuffer = g.graphicBuffer.toArray(new Integer[0]);
+
+ for (int i=0; i < drawList.size(); i++)
+ {
+ colors[drawList.get(i).index] = gbuffer[i];
+ }
+ g.changed = false;
+ }
+ }
+
+};
+
+class RedsGraphic extends Graphic
+{
+ RedsGraphic()
+ {
+ super();
+ drawit(10);
+ }
+ RedsGraphic(int len)
+ {
+ super();
+ drawit(len);
+
+ }
+ public void drawit(int len)
+ {
+ for(int i = 0; i < len ;i++)
+ {
+ graphicBuffer.add(lx.hsb(0,255,255));
+ }
+ }
+};
+
+class RedsGranim extends Granim
+{
+ RedsGranim()
+ {
+ super();
+ addGraphic("myreds", new RedsGraphic(10));
+ }
+ RedsGranim(int len)
+ {
+ super();
+ addGraphic("myreds", new RedsGraphic(len));
+ }
+ public float count = 0.0f;
+ public void update()
+ {
+ Graphic g=getGraphicByName("myreds");
+ g.position = Math.round(sin(count)*20)+100;
+ count+= 0.1f;
+ if(count>Math.PI*2)
+ {
+ count=0;
+ }
+ super.update();
+ }
+
+};
+
+class RandomsGranim extends Granim
+{
+ private int _len =0 ;
+ RandomsGranim()
+ {
+ super();
+ _len =100;
+ addGraphic("myrandoms", makeGraphic(_len));
+ }
+ RandomsGranim(int len)
+ {
+ super();
+ _len=len;
+ addGraphic("myrandoms", makeGraphic(len));
+ }
+ int colorLid=0;
+ public Graphic makeGraphic(int len)
+ {
+
+ int[] colors= new int[len];
+ for(int i =0;i<len;i++)
+ {
+ colors[i]=(int) Math.round(Math.random()*80)+colorLid;
+
+ }
+ colorLid+=4;
+ return new ColorDotsGraphic(colors);
+ }
+ private int count =1;
+ private int instanceCount =0;
+ public void update()
+ {
+
+ if(instanceCount<90 && count % 20==0)
+ {
+ instanceCount++;
+ Graphic h=addGraphic("myrandoms_"+instanceCount, makeGraphic(_len));
+ h.position = instanceCount*(_len+100);
+ //println("one more " + instanceCount+" at "+h.position);
+ count=0;
+ changed = true;
+ }
+ count++;
+ super.update();
+ }
+
+};
+
+
+class ColorDotsGraphic extends Graphic
+{
+ ColorDotsGraphic(int[] colorSequence)
+ {
+ super();
+ for (int colorVal : colorSequence)
+ {
+ graphicBuffer.add(lx.hsb(colorVal, 255, 255));
+ }
+ changed = true;
+ }
+};
+int BLACK = 0xff000000;
+
+class Gimbal extends SCPattern {
+
+ private final boolean DEBUG_MANUAL_ABG = false;
+ private final int MAXIMUM_BEATS_PER_REVOLUTION = 100;
+
+ private boolean first_run = true;
+ private final Projection projection;
+ private final BasicParameter beatsPerRevolutionParam = new BasicParameter("SLOW", 20.f/MAXIMUM_BEATS_PER_REVOLUTION);
+ private final BasicParameter hueDeltaParam = new BasicParameter("HUED", 60.f/360);
+ private final BasicParameter fadeFromCoreParam = new BasicParameter("FADE", 1);
+ private final BasicParameter girthParam = new BasicParameter("GRTH", .18f);
+ private final BasicParameter ringExtendParam = new BasicParameter("XTND", 1);
+ private final BasicParameter relativeSpeedParam = new BasicParameter("RLSP", .83f);
+ private final BasicParameter sizeParam = new BasicParameter("SIZE", .9f);
+
+ private final BasicParameter aP = new BasicParameter("a", 0);
+ private final BasicParameter bP = new BasicParameter("b", 0);
+ private final BasicParameter gP = new BasicParameter("g", 0);
+
+ Gimbal(GLucose glucose) {
+ super(glucose);
+ projection = new Projection(model);
+ addParameter(beatsPerRevolutionParam);
+ addParameter(hueDeltaParam);
+ addParameter(fadeFromCoreParam);
+ addParameter(girthParam);
+ addParameter(ringExtendParam);
+ addParameter(relativeSpeedParam);
+ addParameter(sizeParam);
+
+ if (DEBUG_MANUAL_ABG) {
+ addParameter(aP);
+ addParameter(bP);
+ addParameter(gP);
+ }
+ }
+
+ float a = 0, b = 0, g = 0;
+
+ public void run(double deltaMs) {
+
+ if (DEBUG_MANUAL_ABG) {
+ a = aP.getValuef() * (2 * PI);
+ b = bP.getValuef() * (2 * PI);
+ g = gP.getValuef() * (2 * PI);
+ } else {
+ float relativeSpeed = relativeSpeedParam.getValuef();
+ float time = millis() / 1000.f;
+
+ int beatsPerRevolution = (int) (beatsPerRevolutionParam.getValuef() * MAXIMUM_BEATS_PER_REVOLUTION) + 1;
+ float radiansPerMs = 2 * PI // radians / revolution
+ / beatsPerRevolution // beats / revolution
+ * lx.tempo.bpmf() // BPM beats / min
+ / 60 // sec / min
+ / 1000; // ms / sec
+
+ a += deltaMs * radiansPerMs * pow(relativeSpeed, 0);
+ b += deltaMs * radiansPerMs * pow(relativeSpeed, 1);
+ g += deltaMs * radiansPerMs * pow(relativeSpeed, 2);
+ a %= 2 * PI;
+ b %= 2 * PI;
+ g %= 2 * PI;
+ }
+
+ float hue = lx.getBaseHuef();
+ float hue_delta = hueDeltaParam.getValuef() * 360;
+
+ float radius1 = model.xMax / 2 * sizeParam.getValuef();
+ float radius2 = ((model.xMax + model.yMax) / 2) / 2 * sizeParam.getValuef();
+ float radius3 = model.yMax / 2 * sizeParam.getValuef();
+ float girth = model.xMax * girthParam.getValuef();
+ Ring ring1 = new Ring((hue + hue_delta * 0) % 360, radius1, girth);
+ Ring ring2 = new Ring((hue + hue_delta * 1) % 360, radius2, girth);
+ Ring ring3 = new Ring((hue + hue_delta * 2) % 360, radius3, girth);
+
+ projection.reset(model)
+ // Translate so the center of the car is the origin
+ .translateCenter(model, 0, 0, 0);
+
+ for (Coord 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);
+ int color1 = ring1.colorFor(c);
+
+ rotate3d(c, 0, b, 0);
+ int color2 = ring2.colorFor(c);
+
+ rotate3d(c, 0, 0, g);
+ int color3 = ring3.colorFor(c);
+
+ colors[c.index] = specialBlend(color1, color2, color3);
+ }
+
+ first_run = false;
+ }
+
+ class Ring {
+
+ float hue;
+ float radius, girth;
+
+ public Ring(float hue, float radius, float girth) {
+ this.hue = hue;
+ this.radius = radius;
+ this.girth = girth;
+ }
+
+ public int colorFor(Coord c) {
+ float theta = atan2(c.y, c.x);
+ float nearest_circle_x = cos(theta) * radius;
+ float nearest_circle_y = sin(theta) * radius;
+ float nearest_circle_z = 0;
+
+ float distance_to_circle
+ = sqrt(pow(nearest_circle_x - c.x, 2)
+ + pow(nearest_circle_y - c.y, 2)
+ + pow(nearest_circle_z - c.z * ringExtendParam.getValuef(), 2));
+
+ 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 Zebra extends SCPattern {
+
+ private final Projection projection;
+ SinLFO angleM = new SinLFO(0, PI * 2, 30000);
+
+/*
+ SinLFO x, y, z, dx, dy, dz;
+ float cRad;
+ _P size;
+ */
+
+ Zebra(GLucose glucose) {
+ super(glucose);
+ projection = new Projection(model);
+
+ addModulator(angleM).trigger();
+ }
+
+ public int colorFor(Coord c) {
+ float hue = lx.getBaseHuef();
+
+
+
+
+/* SLIDE ALONG
+ c.x = c.x + millis() / 100.f;
+ */
+
+
+
+ int stripe_count = 12;
+ float stripe_width = model.xMax / (float)stripe_count;
+ if (Math.floor((c.x) / stripe_width) % 2 == 0) {
+ return lx.hsb(hue, 100, 100);
+ } else {
+ return lx.hsb((hue + 90) % 360, 100, 100);
+ }
+
+
+ /* OCTANTS
+
+ if ((isPositiveBit(c.x) + isPositiveBit(c.y) + isPositiveBit(c.z)) % 2 == 0) {
+ return lx.hsb(lx.getBaseHuef(), 100, 100);
+ } else {
+ return lx.hsb(0, 0, 0);
+ }
+ */
+ }
+
+ public int isPositiveBit(float f) {
+ return f > 0 ? 1 : 0;
+ }
+
+ public void run(double deltaMs) {
+ float a = (millis() / 1000.f) % (2 * PI);
+ float b = (millis() / 1200.f) % (2 * PI);
+ float g = (millis() / 1600.f) % (2 * PI);
+
+ projection.reset(model)
+ // Translate so the center of the car is the origin
+ .translateCenter(model, 0, 0, 0);
+
+ for (Coord c : projection) {
+// rotate3d(c, a, b, g);
+ colors[c.index] = colorFor(c);
+ }
+
+ first_run = false;
+ }
+
+
+ // Utility!
+ boolean first_run = true;
+ private void log(String s) {
+ if (first_run) {
+ println(s);
+ }
+ }
+
+
+}
+
+public void rotate3d(Coord c, float a /* roll */, float b /* pitch */, float g /* yaw */) {
+ float cosa = cos(a);
+ float cosb = cos(b);
+ float cosg = cos(g);
+ float sina = sin(a);
+ float sinb = sin(b);
+ float sing = sin(g);
+
+ float a1 = cosa*cosb;
+ float a2 = cosa*sinb*sing - sina*cosg;
+ float a3 = cosa*sinb*cosg + sina*sing;
+ float b1 = sina*cosb;
+ float b2 = sina*sinb*sing + cosa*cosg;
+ float b3 = sina*sinb*cosg - cosa*sing;
+ 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);
+}
+
+public float dotProduct(float[] a, float[] b) {
+ float ret = 0;
+ for (int i = 0 ; i < a.length; ++i) {
+ ret += a[i] * b[i];
+ }
+ return ret;
+}
+
+public int specialBlend(int c1, int c2, int c3) {
+ float h1 = hue(c1);
+ float h2 = hue(c2);
+ float h3 = hue(c3);
+
+ // force h1 < h2 < h3
+ while (h2 < h1) {
+ h2 += 360;
+ }
+ while (h3 < h2) {
+ h3 += 360;
+ }
+
+ float s1 = saturation(c1);
+ float s2 = saturation(c2);
+ float s3 = saturation(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);
+
+ return lx.hsb(
+ (h1 * relative_b1 + h2 * relative_b1 + h3 * relative_b3) % 360,
+ s1 * relative_b1 + s2 * relative_b2 + s3 * relative_b3,
+ max(max(b1, b2), b3)
+ );
+}
+
+/**
+ * A Projection of sin wave in 3d space.
+ * It sort of looks like an animal swiming around in water.
+ * Angle sliders are sort of a work in progress that allow yo to change the crazy ways it moves around.
+ * Hue slider allows you to control how different the colors are along the wave.
+ *
+ * This code copied heavily from Tim and Slee.
+ */
+class Swim extends SCPattern {
+
+ // Projection stuff
+ private final Projection projection;
+ SawLFO rotation = new SawLFO(0, TWO_PI, 19000);
+ SinLFO yPos = new SinLFO(-25, 25, 12323);
+ final BasicParameter xAngle = new BasicParameter("XANG", 0.9f);
+ final BasicParameter yAngle = new BasicParameter("YANG", 0.3f);
+ final BasicParameter zAngle = new BasicParameter("ZANG", 0.3f);
+
+ final BasicParameter hueScale = new BasicParameter("HUE", 0.3f);
+
+ public Swim(GLucose glucose) {
+ super(glucose);
+ projection = new Projection(model);
+
+ addParameter(xAngle);
+ addParameter(yAngle);
+ addParameter(zAngle);
+ addParameter(hueScale);
+
+ addModulator(rotation).trigger();
+ addModulator(yPos).trigger();
+ }
+
+
+ int beat = 0;
+ float prevRamp = 0;
+ public void run(double deltaMs) {
+
+ // Sync to the beat
+ float ramp = (float)lx.tempo.ramp();
+ if (ramp < prevRamp) {
+ beat = (beat + 1) % 4;
+ }
+ prevRamp = ramp;
+ float phase = (beat+ramp) / 2.0f * 2 * PI;
+
+ float denominator = max(xAngle.getValuef() + yAngle.getValuef() + zAngle.getValuef(), 1);
+
+ projection.reset(model)
+ // Swim around the world
+ .rotate(rotation.getValuef(), xAngle.getValuef() / denominator, yAngle.getValuef() / denominator, zAngle.getValuef() / denominator)
+ .translateCenter(model, 0, 50 + yPos.getValuef(), 0);
+
+ float model_height = model.yMax - model.yMin;
+ float model_width = model.xMax - model.xMin;
+ for (Coord p : projection) {
+ float x_percentage = (p.x - model.xMin)/model_width;
+
+ // Multiply by 1.4 to shrink the size of the sin wave to be less than the height of the cubes.
+ float y_in_range = 1.4f * (2*p.y - model.yMax - model.yMin) / model_height;
+ float sin_x = sin(phase + 2 * PI * x_percentage);
+
+ // Color fade near the top of the sin wave
+ float v1 = sin_x > y_in_range ? (100 + 100*(y_in_range - sin_x)) : 0;
+
+ float hue_color = (lx.getBaseHuef() + hueScale.getValuef() * (abs(p.x-model.xMax/2.f)*.3f + abs(p.y-model.yMax/2)*.9f + abs(p.z - model.zMax/2.f))) % 360;
+ colors[p.index] = lx.hsb(hue_color, 70, v1);
+ }
+ }
+}
+
+/**
+ * The idea here is to do another sin wave pattern, but with less rotation and more of a breathing / heartbeat affect with spheres above / below the wave.
+ * This is not done.
+ */
+class Balance extends SCPattern {
+
+ final BasicParameter hueScale = new BasicParameter("Hue", 0.4f);
+
+ class Sphere {
+ float x, y, z;
+ }
+
+
+ // Projection stuff
+ private final Projection projection;
+
+ SinLFO sphere1Z = new SinLFO(0, 0, 15323);
+ SinLFO sphere2Z = new SinLFO(0, 0, 8323);
+ SinLFO rotationX = new SinLFO(-PI/32, PI/32, 9000);
+ SinLFO rotationY = new SinLFO(-PI/16, PI/16, 7000);
+ SinLFO rotationZ = new SinLFO(-PI/16, PI/16, 11000);
+ SawLFO phaseLFO = new SawLFO(0, 2 * PI, 5000 - 4500 * 0.5f);
+ final BasicParameter phaseParam = new BasicParameter("Spd", 0.5f);
+ final BasicParameter crazyParam = new BasicParameter("Crzy", 0.2f);
+
+
+ private final Sphere[] spheres;
+ private final float centerX, centerY, centerZ, modelHeight, modelWidth, modelDepth;
+ SinLFO heightMod = new SinLFO(0.8f, 1.9f, 17298);
+
+ public Balance(GLucose glucose) {
+ super(glucose);
+
+ projection = new Projection(model);
+
+ addParameter(hueScale);
+ addParameter(phaseParam);
+ addParameter(crazyParam);
+
+ spheres = new Sphere[2];
+ centerX = (model.xMax + model.xMin) / 2;
+ centerY = (model.yMax + model.yMin) / 2;
+ centerZ = (model.zMax + model.zMin) / 2;
+ modelHeight = model.yMax - model.yMin;
+ modelWidth = model.xMax - model.xMin;
+ modelDepth = model.zMax - model.zMin;
+
+ spheres[0] = new Sphere();
+ spheres[0].x = 1*modelWidth/2 + model.xMin;
+ spheres[0].y = centerY + 20;
+ spheres[0].z = centerZ;
+
+ spheres[1] = new Sphere();
+ spheres[1].x = model.xMin;
+ spheres[1].y = centerY - 20;
+ spheres[1].z = centerZ;
+
+ addModulator(rotationX).trigger();
+ addModulator(rotationY).trigger();
+ addModulator(rotationZ).trigger();
+
+
+ addModulator(sphere1Z).trigger();
+ addModulator(sphere2Z).trigger();
+ addModulator(phaseLFO).trigger();
+
+ addModulator(heightMod).trigger();
+ }
+
+ public void onParameterChanged(LXParameter parameter) {
+ if (parameter == phaseParam) {
+ phaseLFO.setDuration(5000 - 4500 * parameter.getValuef());
+ }
+ }
+
+ int beat = 0;
+ float prevRamp = 0;
+ public void run(double deltaMs) {
+
+ // Sync to the beat
+ float ramp = (float)lx.tempo.ramp();
+ if (ramp < prevRamp) {
+ beat = (beat + 1) % 4;
+ }
+ prevRamp = ramp;
+ float phase = phaseLFO.getValuef();
+
+ float crazy_factor = crazyParam.getValuef() / 0.2f;
+ projection.reset(model)
+ .rotate(rotationZ.getValuef() * crazy_factor, 0, 1, 0)
+ .rotate(rotationX.getValuef() * crazy_factor, 0, 0, 1)
+ .rotate(rotationY.getValuef() * crazy_factor, 0, 1, 0);
+
+ for (Coord p : projection) {
+ float x_percentage = (p.x - model.xMin)/modelWidth;
+
+ float y_in_range = heightMod.getValuef() * (2*p.y - model.yMax - model.yMin) / modelHeight;
+ float sin_x = sin(PI / 2 + phase + 2 * PI * x_percentage);
+
+ // Color fade near the top of the sin wave
+ float v1 = max(0, 100 * (1 - 4*abs(sin_x - y_in_range)));
+
+ float hue_color = (lx.getBaseHuef() + hueScale.getValuef() * (abs(p.x-model.xMax/2.f) + abs(p.y-model.yMax/2)*.2f + abs(p.z - model.zMax/2.f)*.5f)) % 360;
+ int c = lx.hsb(hue_color, 80, v1);
+
+ // Now draw the spheres
+ for (Sphere s : spheres) {
+ float phase_x = (s.x - phase / (2 * PI) * modelWidth) % modelWidth;
+ float x_dist = LXUtils.wrapdistf(p.x, phase_x, modelWidth);
+
+ float sphere_z = (s == spheres[0]) ? (s.z + sphere1Z.getValuef()) : (s.z - sphere2Z.getValuef());
+
+
+ float d = sqrt(pow(x_dist, 2) + pow(p.y - s.y, 2) + pow(p.z - sphere_z, 2));
+
+ float distance_from_beat = (beat % 2 == 1) ? 1 - ramp : ramp;
+
+ min(ramp, 1-ramp);
+
+ float r = 40 - pow(distance_from_beat, 0.75f) * 20;
+
+ float distance_value = max(0, 1 - max(0, d - r) / 10);
+ float beat_value = 1.0f;
+
+ float value = min(beat_value, distance_value);
+
+ float sphere_color = (lx.getBaseHuef() - (1 - hueScale.getValuef()) * d/r * 45) % 360;
+
+ c = blendColor(c, lx.hsb((sphere_color + 270) % 360, 60, min(1, value) * 100), ADD);
+ }
+ colors[p.index] = c;
+ }
+ }
+}
+class Cathedrals extends SCPattern {
+
+ private final BasicParameter xpos = new BasicParameter("XPOS", 0.5f);
+ private final BasicParameter wid = new BasicParameter("WID", 0.5f);
+ private final BasicParameter arms = new BasicParameter("ARMS", 0.5f);
+ private final BasicParameter sat = new BasicParameter("SAT", 0.5f);
+ private GraphicEQ eq;
+
+ Cathedrals(GLucose glucose) {
+ super(glucose);
+ addParameter(xpos);
+ addParameter(wid);
+ addParameter(arms);
+ addParameter(sat);
+ }
+
+ protected void onActive() {
+ if (eq == null) {
+ eq = new GraphicEQ(lx, 16);
+ eq.slope.setValue(0.7f);
+ eq.range.setValue(0.4f);
+ eq.attack.setValue(0.4f);
+ eq.release.setValue(0.4f);
+ addParameter(eq.level);
+ addParameter(eq.range);
+ addParameter(eq.attack);
+ addParameter(eq.release);
+ addParameter(eq.slope);
+ }
+ }
+
+
+ public void run(double deltaMs) {
+ eq.run(deltaMs);
+ float bassLevel = eq.getAverageLevel(0, 4);
+ float trebleLevel = eq.getAverageLevel(8, 6);
+
+ float falloff = 100 / (2 + 14*wid.getValuef());
+ float cx = model.xMin + (model.xMax-model.xMin) * xpos.getValuef();
+ float barm = 12 + 60*arms.getValuef()*max(0, 2*(bassLevel-0.1f));
+ float tarm = 12 + 60*arms.getValuef()*max(0, 2*(trebleLevel-0.1f));
+
+ float arm = 0;
+ float middle = 0;
+
+ float sf = 100.f / (70 - 69.9f*sat.getValuef());
+
+ for (Point p : model.points) {
+ float d = MAX_FLOAT;
+ if (p.y > model.cy) {
+ arm = tarm;
+ middle = model.yMax * 3/5.f;
+ } else {
+ arm = barm;
+ middle = model.yMax * 1/5.f;
+ }
+ if (abs(p.x - cx) < arm) {
+ d = min(abs(p.x - cx), abs(p.y - middle));
+ }
+ colors[p.index] = color(
+ (lx.getBaseHuef() + .2f*abs(p.y - model.cy)) % 360,
+ min(100, sf*dist(abs(p.x - cx), p.y, arm, middle)),
+ max(0, 120 - d*falloff));
+ }
+ }
+}
+
+class MidiMusic extends SCPattern {
+
+ private final Stack<LXLayer> newLayers = new Stack<LXLayer>();
+
+ private final Map<Integer, LightUp> lightMap = new HashMap<Integer, LightUp>();
+ private final List<LightUp> lights = new ArrayList<LightUp>();
+ private final BasicParameter lightSize = new BasicParameter("SIZE", 0.5f);
+
+ private final List<Sweep> sweeps = new ArrayList<Sweep>();
+
+ private final LinearEnvelope sparkle = new LinearEnvelope(0, 1, 500);
+ private boolean sparkleDirection = true;
+ private float sparkleBright = 100;
+
+ private final BasicParameter wave = new BasicParameter("WAVE", 0);
+
+ MidiMusic(GLucose glucose) {
+ super(glucose);
+ addParameter(lightSize);
+ addParameter(wave);
+ addModulator(sparkle).setValue(1);
+ }
+
+ public void onReset() {
+ for (LightUp light : lights) {
+ light.noteOff(null);
+ }
+ }
+
+ class Sweep extends LXLayer {
+
+ final LinearEnvelope position = new LinearEnvelope(0, 1, 1000);
+ float bright = 100;
+ float falloff = 10;
+
+ Sweep() {
+ addModulator(position);
+ }
+
+ public void run(double deltaMs, int[] colors) {
+ if (!position.isRunning()) {
+ return;
+ }
+ float posf = position.getValuef();
+ for (Point p : model.points) {
+ colors[p.index] = blendColor(colors[p.index], color(
+ (lx.getBaseHuef() + .2f*abs(p.x - model.cx) + .2f*abs(p.y - model.cy)) % 360,
+ 100,
+ max(0, bright - posf*100 - falloff*abs(p.y - posf*model.yMax))
+ ), ADD);
+ }
+ }
+ }
+
+ class LightUp extends LXLayer {
+
+ private final LinearEnvelope brt = new LinearEnvelope(0, 0, 0);
+ private final Accelerator yPos = new Accelerator(0, 0, 0);
+ private float xPos;
+
+ LightUp() {
+ addModulator(brt);
+ addModulator(yPos);
+ }
+
+ public boolean isAvailable() {
+ return brt.getValuef() <= 0;
+ }
+
+ public void noteOn(Note note) {
+ xPos = lerp(0, model.xMax, constrain(0.5f + (note.getPitch() - 60) / 28.f, 0, 1));
+ yPos.setValue(lerp(20, model.yMax*.72f, note.getVelocity() / 127.f)).stop();
+ brt.setRangeFromHereTo(lerp(40, 100, note.getVelocity() / 127.f), 20).start();
+ }
+
+ public void noteOff(Note note) {
+ yPos.setVelocity(0).setAcceleration(-380).start();
+ brt.setRangeFromHereTo(0, 1000).start();
+ }
+
+ public void run(double deltaMs, int[] colors) {
+ float bVal = brt.getValuef();
+ if (bVal <= 0) {
+ return;
+ }
+ float yVal = yPos.getValuef();
+ for (Point p : model.points) {
+ float falloff = 6 - 5*lightSize.getValuef();
+ float b = max(0, bVal - falloff*dist(p.x, p.y, xPos, yVal));
+ if (b > 0) {
+ colors[p.index] = blendColor(colors[p.index], lx.hsb(
+ (lx.getBaseHuef() + .2f*abs(p.x - model.cx) + .2f*abs(p.y - model.cy)) % 360,
+ 100,
+ b
+ ), ADD);
+ }
+ }
+ }
+ }
+
+ private LightUp getLight() {
+ for (LightUp light : lights) {
+ if (light.isAvailable()) {
+ return light;
+ }
+ }
+ LightUp newLight = new LightUp();
+ lights.add(newLight);
+ synchronized(newLayers) {
+ newLayers.push(newLight);
+ }
+ return newLight;
+ }
+
+ private Sweep getSweep() {
+ for (Sweep s : sweeps) {
+ if (!s.position.isRunning()) {
+ return s;
+ }
+ }
+ Sweep newSweep = new Sweep();
+ sweeps.add(newSweep);
+ synchronized(newLayers) {
+ newLayers.push(newSweep);
+ }
+ return newSweep;
+ }
+
+ public synchronized boolean noteOn(Note note) {
+ if (note.getChannel() == 0) {
+ LightUp light = getLight();
+ lightMap.put(note.getPitch(), light);
+ light.noteOn(note);
+ } else if (note.getChannel() == 1) {
+ } else if (note.getChannel() == 9) {
+ if (note.getVelocity() > 0) {
+ switch (note.getPitch()) {
+ case 36:
+ Sweep s = getSweep();
+ s.bright = 50 + note.getVelocity() / 127.f * 50;
+ s.falloff = 20 - note.getVelocity() / 127.f * 17;
+ s.position.trigger();
+ break;
+ case 37:
+ sparkleBright = note.getVelocity() / 127.f * 100;
+ sparkleDirection = true;
+ sparkle.trigger();
+ break;
+ case 38:
+ sparkleBright = note.getVelocity() / 127.f * 100;
+ sparkleDirection = false;
+ sparkle.trigger();
+ break;
+ case 39:
+ effects.boom.trigger();
+ break;
+ case 40:
+ effects.flash.trigger();
+ break;
+ }
+ }
+ }
+ return true;
+ }
+
+ public synchronized boolean noteOff(Note note) {
+ if (note.getChannel() == 0) {
+ LightUp light = lightMap.get(note.getPitch());
+ if (light != null) {
+ light.noteOff(note);
+ }
+ }
+ return true;
+ }
+
+ final float[] wval = new float[16];
+ float wavoff = 0;
+
+ public synchronized void run(double deltaMs) {
+ wavoff += deltaMs * .001f;
+ for (int i = 0; i < wval.length; ++i) {
+ wval[i] = model.cy + 0.2f * model.yMax/2.f * sin(wavoff + i / 1.9f);
+ }
+ float sparklePos = (sparkleDirection ? sparkle.getValuef() : (1 - sparkle.getValuef())) * (Cube.POINTS_PER_STRIP)/2.f;
+ float maxBright = sparkleBright * (1 - sparkle.getValuef());
+ for (Strip s : model.strips) {
+ int i = 0;
+ for (Point p : s.points) {
+ int wavi = (int) constrain(p.x / model.xMax * wval.length, 0, wval.length-1);
+ float wavb = max(0, wave.getValuef()*100.f - 8.f*abs(p.y - wval[wavi]));
+ colors[p.index] = color(
+ (lx.getBaseHuef() + .2f*abs(p.x - model.cx) + .2f*abs(p.y - model.cy)) % 360,
+ 100,
+ constrain(wavb + max(0, maxBright - 40.f*abs(sparklePos - abs(i - (Cube.POINTS_PER_STRIP-1)/2.f))), 0, 100)
+ );
+ ++i;
+ }
+ }
+
+ if (!newLayers.isEmpty()) {
+ synchronized(newLayers) {
+ while (!newLayers.isEmpty()) {
+ addLayer(newLayers.pop());
+ }
+ }
+ }
+ }
+}
+
+class Pulley extends SCPattern {
+
+ final int NUM_DIVISIONS = 16;
+ private final Accelerator[] gravity = new Accelerator[NUM_DIVISIONS];
+ private final Click[] delays = new Click[NUM_DIVISIONS];
+
+ private final Click reset = new Click(9000);
+ private boolean isRising = false;
+
+ private BasicParameter sz = new BasicParameter("SIZE", 0.5f);
+ private BasicParameter beatAmount = new BasicParameter("BEAT", 0);
+
+ Pulley(GLucose glucose) {
+ super(glucose);
+ for (int i = 0; i < NUM_DIVISIONS; ++i) {
+ addModulator(gravity[i] = new Accelerator(0, 0, 0));
+ addModulator(delays[i] = new Click(0));
+ }
+ addModulator(reset).start();
+ addParameter(sz);
+ addParameter(beatAmount);
+ trigger();
+
+ }
+
+ private void trigger() {
+ isRising = !isRising;
+ int i = 0;
+ for (Accelerator g : gravity) {
+ if (isRising) {
+ g.setSpeed(random(20, 33), 0).start();
+ } else {
+ g.setVelocity(0).setAcceleration(-420);
+ delays[i].setDuration(random(0, 500)).trigger();
+ }
+ ++i;
+ }
+ }
+
+ public void run(double deltaMs) {
+ if (reset.click()) {
+ trigger();
+ }
+
+ if (isRising) {
+ // Fucking A, had to comment this all out because of that bizarre
+ // Processing bug where some simple loop takes an absurd amount of
+ // time, must be some pre-processor bug
+// for (Accelerator g : gravity) {
+// if (g.getValuef() > model.yMax) {
+// g.stop();
+// } else if (g.getValuef() > model.yMax*.55) {
+// if (g.getVelocityf() > 10) {
+// g.setAcceleration(-16);
+// } else {
+// g.setAcceleration(0);
+// }
+// }
+// }
+ } else {
+ int j = 0;
+ for (Click d : delays) {
+ if (d.click()) {
+ gravity[j].start();
+ d.stop();
+ }
+ ++j;
+ }
+ for (Accelerator g : gravity) {
+ if (g.getValuef() < 0) {
+ g.setValue(-g.getValuef());
+ g.setVelocity(-g.getVelocityf() * random(0.74f, 0.84f));
+ }
+ }
+ }
+
+ // A little silliness to test the grid API
+ if (midiEngine != null && midiEngine.getFocusedPattern() == this) {
+ for (int i = 0; i < 5; ++i) {
+ for (int j = 0; j < 8; ++j) {
+ int gi = (int) constrain(j * NUM_DIVISIONS / 8, 0, NUM_DIVISIONS-1);
+ float b = 1 - 4.f*abs((6-i)/6.f - gravity[gi].getValuef() / model.yMax);
+ midiEngine.grid.setState(i, j, (b < 0) ? 0 : 3);
+ }
+ }
+ }
+
+ float fPos = 1 - lx.tempo.rampf();
+ if (fPos < .2f) {
+ fPos = .2f + 4 * (.2f - fPos);
+ }
+ float falloff = 100.f / (3 + sz.getValuef() * 36 + fPos * beatAmount.getValuef()*48);
+ for (Point p : model.points) {
+ int gi = (int) constrain((p.x - model.xMin) * NUM_DIVISIONS / (model.xMax - model.xMin), 0, NUM_DIVISIONS-1);
+ colors[p.index] = lx.hsb(
+ (lx.getBaseHuef() + abs(p.x - model.cx)*.8f + p.y*.4f) % 360,
+ constrain(130 - p.y*.8f, 0, 100),
+ max(0, 100 - abs(p.y - gravity[gi].getValuef())*falloff)
+ );
+ }
+ }
+}
+
+class ViolinWave extends SCPattern {
+
+ BasicParameter level = new BasicParameter("LVL", 0.45f);
+ BasicParameter range = new BasicParameter("RNG", 0.5f);
+ BasicParameter edge = new BasicParameter("EDG", 0.5f);
+ BasicParameter release = new BasicParameter("RLS", 0.5f);
+ BasicParameter speed = new BasicParameter("SPD", 0.5f);
+ BasicParameter amp = new BasicParameter("AMP", 0.25f);
+ BasicParameter period = new BasicParameter("WAVE", 0.5f);
+ BasicParameter pSize = new BasicParameter("PSIZE", 0.5f);
+ BasicParameter pSpeed = new BasicParameter("PSPD", 0.5f);
+ BasicParameter pDensity = new BasicParameter("PDENS", 0.25f);
+
+ LinearEnvelope dbValue = new LinearEnvelope(0, 0, 10);
+
+ ViolinWave(GLucose glucose) {
+ super(glucose);
+ addParameter(level);
+ addParameter(edge);
+ addParameter(range);
+ addParameter(release);
+ addParameter(speed);
+ addParameter(amp);
+ addParameter(period);
+ addParameter(pSize);
+ addParameter(pSpeed);
+ addParameter(pDensity);
+
+ addModulator(dbValue);
+ }
+
+ final List<Particle> particles = new ArrayList<Particle>();
+
+ class Particle {
+
+ LinearEnvelope x = new LinearEnvelope(0, 0, 0);
+ LinearEnvelope y = new LinearEnvelope(0, 0, 0);
+
+ Particle() {
+ addModulator(x);
+ addModulator(y);
+ }
+
+ public Particle trigger(boolean direction) {
+ float xInit = random(model.xMin, model.xMax);
+ float time = 3000 - 2500*pSpeed.getValuef();
+ x.setRange(xInit, xInit + random(-40, 40), time).trigger();
+ y.setRange(model.cy + 10, direction ? model.yMax + 50 : model.yMin - 50, time).trigger();
+ return this;
+ }
+
+ public boolean isActive() {
+ return x.isRunning() || y.isRunning();
+ }
+
+ public void run(double deltaMs) {
+ if (!isActive()) {
+ return;
+ }
+
+ float pFalloff = (30 - 27*pSize.getValuef());
+ for (Point p : model.points) {
+ float b = 100 - pFalloff * (abs(p.x - x.getValuef()) + abs(p.y - y.getValuef()));
+ if (b > 0) {
+ colors[p.index] = blendColor(colors[p.index], lx.hsb(
+ lx.getBaseHuef(), 20, b
+ ), ADD);
+ }
+ }
+ }
+ }
+
+ float[] centers = new float[30];
+ double accum = 0;
+ boolean rising = true;
+
+ public void fireParticle(boolean direction) {
+ boolean gotOne = false;
+ for (Particle p : particles) {
+ if (!p.isActive()) {
+ p.trigger(direction);
+ return;
+ }
+ }
+ particles.add(new Particle().trigger(direction));
+ }
+
+ public void run(double deltaMs) {
+ accum += deltaMs / (1000.f - 900.f*speed.getValuef());
+ for (int i = 0; i < centers.length; ++i) {
+ centers[i] = model.cy + 30*amp.getValuef()*sin((float) (accum + (i-centers.length/2.f)/(1.f + 9.f*period.getValuef())));
+ }
+
+ float zeroDBReference = pow(10, (50 - 190*level.getValuef())/20.f);
+ float dB = 20*GraphicEQ.log10(lx.audioInput().mix.level() / zeroDBReference);
+ if (dB > dbValue.getValuef()) {
+ rising = true;
+ dbValue.setRangeFromHereTo(dB, 10).trigger();
+ } else {
+ if (rising) {
+ for (int j = 0; j < pDensity.getValuef()*3; ++j) {
+ fireParticle(true);
+ fireParticle(false);
+ }
+ }
+ rising = false;
+ dbValue.setRangeFromHereTo(max(dB, -96), 50 + 1000*release.getValuef()).trigger();
+ }
+ float edg = 1 + edge.getValuef() * 40;
+ float rng = (78 - 64 * range.getValuef()) / (model.yMax - model.cy);
+ float val = max(2, dbValue.getValuef());
+
+ for (Point p : model.points) {
+ int ci = (int) lerp(0, centers.length-1, (p.x - model.xMin) / (model.xMax - model.xMin));
+ float rFactor = 1.0f - 0.9f * abs(p.x - model.cx) / (model.xMax - model.cx);
+ colors[p.index] = lx.hsb(
+ (lx.getBaseHuef() + abs(p.x - model.cx)) % 360,
+ min(100, 20 + 8*abs(p.y - centers[ci])),
+ constrain(edg*(val*rFactor - rng * abs(p.y-centers[ci])), 0, 100)
+ );
+ }
+
+ for (Particle p : particles) {
+ p.run(deltaMs);
+ }
+ }
+}
+
+class BouncyBalls extends SCPattern {
+
+ static final int NUM_BALLS = 6;
+
+ class BouncyBall {
+
+ Accelerator yPos;
+ TriangleLFO xPos = new TriangleLFO(0, model.xMax, random(8000, 19000));
+ float zPos;
+
+ BouncyBall(int i) {
+ addModulator(xPos.setBasis(random(0, TWO_PI)).start());
+ addModulator(yPos = new Accelerator(0, 0, 0));
+ zPos = lerp(model.zMin, model.zMax, (i+2.f) / (NUM_BALLS + 4.f));
+ }
+
+ public void bounce(float midiVel) {
+ float v = 100 + 8*midiVel;
+ yPos.setSpeed(v, getAccel(v, 60 / lx.tempo.bpmf())).start();
+ }
+
+ public float getAccel(float v, float oneBeat) {
+ return -2*v / oneBeat;
+ }
+
+ public void run(double deltaMs) {
+ float flrLevel = flr.getValuef() * model.xMax/2.f;
+ if (yPos.getValuef() < flrLevel) {
+ if (yPos.getVelocity() < -50) {
+ yPos.setValue(2*flrLevel-yPos.getValuef());
+ float v = -yPos.getVelocityf() * bounce.getValuef();
+ yPos.setSpeed(v, getAccel(v, 60 / lx.tempo.bpmf()));
+ } else {
+ yPos.setValue(flrLevel).stop();
+ }
+ }
+ float falloff = 130.f / (12 + blobSize.getValuef() * 36);
+ float xv = xPos.getValuef();
+ float yv = yPos.getValuef();
+
+ for (Point p : model.points) {
+ float d = sqrt((p.x-xv)*(p.x-xv) + (p.y-yv)*(p.y-yv) + .1f*(p.z-zPos)*(p.z-zPos));
+ float b = constrain(130 - falloff*d, 0, 100);
+ if (b > 0) {
+ colors[p.index] = blendColor(colors[p.index], lx.hsb(
+ (lx.getBaseHuef() + p.y*.5f + abs(model.cx - p.x) * .5f) % 360,
+ max(0, 100 - .45f*(p.y - flrLevel)),
+ b
+ ), ADD);
+ }
+ }
+ }
+ }
+
+ final BouncyBall[] balls = new BouncyBall[NUM_BALLS];
+
+ final BasicParameter bounce = new BasicParameter("BNC", .8f);
+ final BasicParameter flr = new BasicParameter("FLR", 0);
+ final BasicParameter blobSize = new BasicParameter("SIZE", 0.5f);
+
+ BouncyBalls(GLucose glucose) {
+ super(glucose);
+ for (int i = 0; i < balls.length; ++i) {
+ balls[i] = new BouncyBall(i);
+ }
+ addParameter(bounce);
+ addParameter(flr);
+ addParameter(blobSize);
+ }
+
+ public void run(double deltaMs) {
+ setColors(0xff000000);
+ for (BouncyBall b : balls) {
+ b.run(deltaMs);
+ }
+ }
+
+ public boolean noteOn(Note note) {
+ int pitch = (note.getPitch() + note.getChannel()) % NUM_BALLS;
+ balls[pitch].bounce(note.getVelocity());
+ return true;
+ }
+}
+
+class SpaceTime extends SCPattern {
+
+ SinLFO pos = new SinLFO(0, 1, 3000);
+ SinLFO rate = new SinLFO(1000, 9000, 13000);
+ SinLFO falloff = new SinLFO(10, 70, 5000);
+ float angle = 0;
+
+ BasicParameter rateParameter = new BasicParameter("RATE", 0.5f);
+ BasicParameter sizeParameter = new BasicParameter("SIZE", 0.5f);
+
+
+ public SpaceTime(GLucose glucose) {
+ super(glucose);
+
+ addModulator(pos).trigger();
+ addModulator(rate).trigger();
+ addModulator(falloff).trigger();
+ pos.modulateDurationBy(rate);
+ addParameter(rateParameter);
+ addParameter(sizeParameter);
+ }
+
+ public void onParameterChanged(LXParameter parameter) {
+ if (parameter == rateParameter) {
+ rate.stop().setValue(9000 - 8000*parameter.getValuef());
+ } else if (parameter == sizeParameter) {
+ falloff.stop().setValue(70 - 60*parameter.getValuef());
+ }
+ }
+
+ public void run(double deltaMs) {
+ angle += deltaMs * 0.0007f;
+ float sVal1 = model.strips.size() * (0.5f + 0.5f*sin(angle));
+ float sVal2 = model.strips.size() * (0.5f + 0.5f*cos(angle));
+
+ float pVal = pos.getValuef();
+ float fVal = falloff.getValuef();
+
+ int s = 0;
+ for (Strip strip : model.strips) {
+ int i = 0;
+ for (Point p : strip.points) {
+ colors[p.index] = lx.hsb(
+ (lx.getBaseHuef() + 360 - p.x*.2f + p.y * .3f) % 360,
+ constrain(.4f * min(abs(s - sVal1), abs(s - sVal2)), 20, 100),
+ max(0, 100 - fVal*abs(i - pVal*(strip.metrics.numPoints - 1)))
+ );
+ ++i;
+ }
+ ++s;
+ }
+ }
+}
+
+class Swarm extends SCPattern {
+
+ SawLFO offset = new SawLFO(0, 1, 1000);
+ SinLFO rate = new SinLFO(350, 1200, 63000);
+ SinLFO falloff = new SinLFO(15, 50, 17000);
+ SinLFO fX = new SinLFO(0, model.xMax, 19000);
+ SinLFO fY = new SinLFO(0, model.yMax, 11000);
+ SinLFO hOffX = new SinLFO(0, model.xMax, 13000);
+
+ public Swarm(GLucose glucose) {
+ super(glucose);
+
+ addModulator(offset).trigger();
+ addModulator(rate).trigger();
+ addModulator(falloff).trigger();
+ addModulator(fX).trigger();
+ addModulator(fY).trigger();
+ addModulator(hOffX).trigger();
+ offset.modulateDurationBy(rate);
+ }
+
+ public float modDist(float v1, float v2, float mod) {
+ v1 = v1 % mod;
+ v2 = v2 % mod;
+ if (v2 > v1) {
+ return min(v2-v1, v1+mod-v2);
+ }
+ else {
+ return min(v1-v2, v2+mod-v1);
+ }
+ }
+
+ public void run(double deltaMs) {
+ float s = 0;
+ for (Strip strip : model.strips ) {
+ int i = 0;
+ for (Point p : strip.points) {
+ float fV = max(-1, 1 - dist(p.x/2.f, p.y, fX.getValuef()/2.f, fY.getValuef()) / 64.f);
+ colors[p.index] = lx.hsb(
+ (lx.getBaseHuef() + 0.3f * abs(p.x - hOffX.getValuef())) % 360,
+ constrain(80 + 40 * fV, 0, 100),
+ constrain(100 - (30 - fV * falloff.getValuef()) * modDist(i + (s*63)%61, offset.getValuef() * strip.metrics.numPoints, strip.metrics.numPoints), 0, 100)
+ );
+ ++i;
+ }
+ ++s;
+ }
+ }
+}
+
+class SwipeTransition extends SCTransition {
+
+ final BasicParameter bleed = new BasicParameter("WIDTH", 0.5f);
+
+ SwipeTransition(GLucose glucose) {
+ super(glucose);
+ setDuration(5000);
+ addParameter(bleed);
+ }
+
+ public void computeBlend(int[] c1, int[] c2, double progress) {
+ float bleedf = 10 + bleed.getValuef() * 200.f;
+ float xPos = (float) (-bleedf + progress * (model.xMax + bleedf));
+ for (Point p : model.points) {
+ float d = (p.x - xPos) / bleedf;
+ if (d < 0) {
+ colors[p.index] = c2[p.index];
+ } else if (d > 1) {
+ colors[p.index] = c1[p.index];
+ } else {
+ colors[p.index] = lerpColor(c2[p.index], c1[p.index], d, RGB);
+ }
+ }
+ }
+}
+
+abstract class BlendTransition extends SCTransition {
+
+ final int blendType;
+
+ BlendTransition(GLucose glucose, int blendType) {
+ super(glucose);
+ this.blendType = blendType;
+ }
+
+ public void computeBlend(int[] c1, int[] c2, double progress) {
+ if (progress < 0.5f) {
+ for (int i = 0; i < c1.length; ++i) {
+ colors[i] = lerpColor(
+ c1[i],
+ blendColor(c1[i], c2[i], blendType),
+ (float) (2.f*progress),
+ RGB);
+ }
+ } else {
+ for (int i = 0; i < c1.length; ++i) {
+ colors[i] = lerpColor(
+ c2[i],
+ blendColor(c1[i], c2[i], blendType),
+ (float) (2.f*(1.f - progress)),
+ RGB);
+ }
+ }
+ }
+}
+
+class MultiplyTransition extends BlendTransition {
+ MultiplyTransition(GLucose glucose) {
+ super(glucose, MULTIPLY);
+ }
+}
+
+class ScreenTransition extends BlendTransition {
+ ScreenTransition(GLucose glucose) {
+ super(glucose, SCREEN);
+ }
+}
+
+class BurnTransition extends BlendTransition {
+ BurnTransition(GLucose glucose) {
+ super(glucose, BURN);
+ }
+}
+
+class DodgeTransition extends BlendTransition {
+ DodgeTransition(GLucose glucose) {
+ super(glucose, DODGE);
+ }
+}
+
+class OverlayTransition extends BlendTransition {
+ OverlayTransition(GLucose glucose) {
+ super(glucose, OVERLAY);
+ }
+}
+
+class AddTransition extends BlendTransition {
+ AddTransition(GLucose glucose) {
+ super(glucose, ADD);
+ }
+}
+
+class SubtractTransition extends BlendTransition {
+ SubtractTransition(GLucose glucose) {
+ super(glucose, SUBTRACT);
+ }
+}
+
+class SoftLightTransition extends BlendTransition {
+ SoftLightTransition(GLucose glucose) {
+ super(glucose, SOFT_LIGHT);
+ }
+}
+
+class BassPod extends SCPattern {
+
+ private GraphicEQ eq = null;
+
+ private final BasicParameter clr = new BasicParameter("CLR", 0.5f);
+
+ public BassPod(GLucose glucose) {
+ super(glucose);
+ addParameter(clr);
+ }
+
+ protected void onActive() {
+ if (eq == null) {
+ eq = new GraphicEQ(lx, 16);
+ eq.range.setValue(0.4f);
+ eq.level.setValue(0.4f);
+ eq.slope.setValue(0.6f);
+ addParameter(eq.level);
+ addParameter(eq.range);
+ addParameter(eq.attack);
+ addParameter(eq.release);
+ addParameter(eq.slope);
+ }
+ }
+
+ public void run(double deltaMs) {
+ eq.run(deltaMs);
+
+ float bassLevel = eq.getAverageLevel(0, 5);
+
+ float satBase = bassLevel*480*clr.getValuef();
+
+ for (Point p : model.points) {
+ int avgIndex = (int) constrain(1 + abs(p.x-model.cx)/(model.cx)*(eq.numBands-5), 0, eq.numBands-5);
+ float value = 0;
+ for (int i = avgIndex; i < avgIndex + 5; ++i) {
+ value += eq.getLevel(i);
+ }
+ value /= 5.f;
+
+ float b = constrain(8 * (value*model.yMax - abs(p.y-model.yMax/2.f)), 0, 100);
+ colors[p.index] = lx.hsb(
+ (lx.getBaseHuef() + abs(p.y - model.cy) + abs(p.x - model.cx)) % 360,
+ constrain(satBase - .6f*dist(p.x, p.y, model.cx, model.cy), 0, 100),
+ b
+ );
+ }
+ }
+}
+
+
+class CubeEQ extends SCPattern {
+
+ private GraphicEQ eq = null;
+
+ private final BasicParameter edge = new BasicParameter("EDGE", 0.5f);
+ private final BasicParameter clr = new BasicParameter("CLR", 0.5f);
+ private final BasicParameter blockiness = new BasicParameter("BLK", 0.5f);
+
+ public CubeEQ(GLucose glucose) {
+ super(glucose);
+ }
+
+ protected void onActive() {
+ if (eq == null) {
+ eq = new GraphicEQ(lx, 16);
+ addParameter(eq.level);
+ addParameter(eq.range);
+ addParameter(eq.attack);
+ addParameter(eq.release);
+ addParameter(eq.slope);
+ addParameter(edge);
+ addParameter(clr);
+ addParameter(blockiness);
+ }
+ }
+
+ public void run(double deltaMs) {
+ eq.run(deltaMs);
+
+ float edgeConst = 2 + 30*edge.getValuef();
+ float clrConst = 1.1f + clr.getValuef();
+
+ for (Point p : model.points) {
+ float avgIndex = constrain(2 + p.x / model.xMax * (eq.numBands-4), 0, eq.numBands-4);
+ int avgFloor = (int) avgIndex;
+
+ float leftVal = eq.getLevel(avgFloor);
+ float rightVal = eq.getLevel(avgFloor+1);
+ float smoothValue = lerp(leftVal, rightVal, avgIndex-avgFloor);
+
+ float chunkyValue = (
+ eq.getLevel(avgFloor/4*4) +
+ eq.getLevel(avgFloor/4*4 + 1) +
+ eq.getLevel(avgFloor/4*4 + 2) +
+ eq.getLevel(avgFloor/4*4 + 3)
+ ) / 4.f;
+
+ float value = lerp(smoothValue, chunkyValue, blockiness.getValuef());
+
+ float b = constrain(edgeConst * (value*model.yMax - p.y), 0, 100);
+ colors[p.index] = lx.hsb(
+ (480 + lx.getBaseHuef() - min(clrConst*p.y, 120)) % 360,
+ 100,
+ b
+ );
+ }
+ }
+}
+
+class BoomEffect extends SCEffect {
+
+ final BasicParameter falloff = new BasicParameter("WIDTH", 0.5f);
+ final BasicParameter speed = new BasicParameter("SPD", 0.5f);
+ final BasicParameter bright = new BasicParameter("BRT", 1.0f);
+ final BasicParameter sat = new BasicParameter("SAT", 0.2f);
+ List<Layer> layers = new ArrayList<Layer>();
+ final float maxr = sqrt(model.xMax*model.xMax + model.yMax*model.yMax + model.zMax*model.zMax) + 10;
+
+ class Layer {
+ LinearEnvelope boom = new LinearEnvelope(-40, 500, 1300);
+
+ Layer() {
+ addModulator(boom);
+ trigger();
+ }
+
+ public void trigger() {
+ float falloffv = falloffv();
+ boom.setRange(-100 / falloffv, maxr + 100/falloffv, 4000 - speed.getValuef() * 3300);
+ boom.trigger();
+ }
+
+ public void apply(int[] colors) {
+ float brightv = 100 * bright.getValuef();
+ float falloffv = falloffv();
+ float satv = sat.getValuef() * 100;
+ float huev = lx.getBaseHuef();
+ for (Point p : model.points) {
+ colors[p.index] = blendColor(
+ colors[p.index],
+ lx.hsb(huev, satv, constrain(brightv - falloffv*abs(boom.getValuef() - dist(p.x, 2*p.y, 3*p.z, model.xMax/2, model.yMax, model.zMax*1.5f)), 0, 100)),
+ ADD);
+ }
+ }
+ }
+
+ BoomEffect(GLucose glucose) {
+ super(glucose, true);
+ addParameter(falloff);
+ addParameter(speed);
+ addParameter(bright);
+ addParameter(sat);
+ }
+
+ public void onEnable() {
+ for (Layer l : layers) {
+ if (!l.boom.isRunning()) {
+ l.trigger();
+ return;
+ }
+ }
+ layers.add(new Layer());
+ }
+
+ private float falloffv() {
+ return 20 - 19 * falloff.getValuef();
+ }
+
+ public void onTrigger() {
+ onEnable();
+ }
+
+ public void apply(int[] colors) {
+ for (Layer l : layers) {
+ if (l.boom.isRunning()) {
+ l.apply(colors);
+ }
+ }
+ }
+}
+
+public class PianoKeyPattern extends SCPattern {
+
+ final LinearEnvelope[] cubeBrt;
+ final SinLFO base[];
+ final BasicParameter attack = new BasicParameter("ATK", 0.1f);
+ final BasicParameter release = new BasicParameter("REL", 0.5f);
+ final BasicParameter level = new BasicParameter("AMB", 0.6f);
+
+ PianoKeyPattern(GLucose glucose) {
+ super(glucose);
+
+ addParameter(attack);
+ addParameter(release);
+ addParameter(level);
+ cubeBrt = new LinearEnvelope[model.cubes.size() / 4];
+ for (int i = 0; i < cubeBrt.length; ++i) {
+ addModulator(cubeBrt[i] = new LinearEnvelope(0, 0, 100));
+ }
+ base = new SinLFO[model.cubes.size() / 12];
+ for (int i = 0; i < base.length; ++i) {
+ addModulator(base[i] = new SinLFO(0, 1, 7000 + 1000*i)).trigger();
+ }
+ }
+
+ private float getAttackTime() {
+ return 15 + attack.getValuef()*attack.getValuef() * 2000;
+ }
+
+ private float getReleaseTime() {
+ return 15 + release.getValuef() * 3000;
+ }
+
+ private LinearEnvelope getEnvelope(int index) {
+ return cubeBrt[index % cubeBrt.length];
+ }
+
+ private SinLFO getBase(int index) {
+ return base[index % base.length];
+ }
+
+ public boolean noteOn(Note note) {
+ LinearEnvelope env = getEnvelope(note.getPitch());
+ env.setEndVal(min(1, env.getValuef() + (note.getVelocity() / 127.f)), getAttackTime()).start();
+ return true;
+ }
+
+ public boolean noteOff(Note note) {
+ getEnvelope(note.getPitch()).setEndVal(0, getReleaseTime()).start();
+ return true;
+ }
+
+ public void run(double deltaMs) {
+ int i = 0;
+ float huef = lx.getBaseHuef();
+ float levelf = level.getValuef();
+ for (Cube c : model.cubes) {
+ float v = max(getBase(i).getValuef() * levelf/4.f, getEnvelope(i++).getValuef());
+ setColor(c, lx.hsb(
+ (huef + 20*v + abs(c.cx-model.xMax/2.f)*.3f + c.cy) % 360,
+ min(100, 120*v),
+ 100*v
+ ));
+ }
+ }
+}
+
+class CrossSections extends SCPattern {
+
+ final SinLFO x = new SinLFO(0, model.xMax, 5000);
+ final SinLFO y = new SinLFO(0, model.yMax, 6000);
+ final SinLFO z = new SinLFO(0, model.zMax, 7000);
+
+ final BasicParameter xw = new BasicParameter("XWID", 0.3f);
+ final BasicParameter yw = new BasicParameter("YWID", 0.3f);
+ final BasicParameter zw = new BasicParameter("ZWID", 0.3f);
+ final BasicParameter xr = new BasicParameter("XRAT", 0.7f);
+ final BasicParameter yr = new BasicParameter("YRAT", 0.6f);
+ final BasicParameter zr = new BasicParameter("ZRAT", 0.5f);
+ final BasicParameter xl = new BasicParameter("XLEV", 1);
+ final BasicParameter yl = new BasicParameter("YLEV", 1);
+ final BasicParameter zl = new BasicParameter("ZLEV", 0.5f);
+
+
+ CrossSections(GLucose glucose) {
+ super(glucose);
+ addModulator(x).trigger();
+ addModulator(y).trigger();
+ addModulator(z).trigger();
+ addParams();
+ }
+
+ protected void addParams() {
+ addParameter(xr);
+ addParameter(yr);
+ addParameter(zr);
+ addParameter(xw);
+ addParameter(xl);
+ addParameter(yl);
+ addParameter(zl);
+ addParameter(yw);
+ addParameter(zw);
+ }
+
+ public void onParameterChanged(LXParameter p) {
+ if (p == xr) {
+ x.setDuration(10000 - 8800*p.getValuef());
+ } else if (p == yr) {
+ y.setDuration(10000 - 9000*p.getValuef());
+ } else if (p == zr) {
+ z.setDuration(10000 - 9000*p.getValuef());
+ }
+ }
+
+ float xv, yv, zv;
+
+ protected void updateXYZVals() {
+ xv = x.getValuef();
+ yv = y.getValuef();
+ zv = z.getValuef();
+ }
+
+ public void run(double deltaMs) {
+ updateXYZVals();
+
+ float xlv = 100*xl.getValuef();
+ float ylv = 100*yl.getValuef();
+ float zlv = 100*zl.getValuef();
+
+ float xwv = 100.f / (10 + 40*xw.getValuef());
+ float ywv = 100.f / (10 + 40*yw.getValuef());
+ float zwv = 100.f / (10 + 40*zw.getValuef());
+
+ for (Point p : model.points) {
+ int c = 0;
+ c = blendColor(c, lx.hsb(
+ (lx.getBaseHuef() + p.x/10 + p.y/3) % 360,
+ constrain(140 - 1.1f*abs(p.x - model.xMax/2.f), 0, 100),
+ max(0, xlv - xwv*abs(p.x - xv))
+ ), ADD);
+ c = blendColor(c, lx.hsb(
+ (lx.getBaseHuef() + 80 + p.y/10) % 360,
+ constrain(140 - 2.2f*abs(p.y - model.yMax/2.f), 0, 100),
+ max(0, ylv - ywv*abs(p.y - yv))
+ ), ADD);
+ c = blendColor(c, lx.hsb(
+ (lx.getBaseHuef() + 160 + p.z / 10 + p.y/2) % 360,
+ constrain(140 - 2.2f*abs(p.z - model.zMax/2.f), 0, 100),
+ max(0, zlv - zwv*abs(p.z - zv))
+ ), ADD);
+ colors[p.index] = c;
+ }
+ }
+}
+
+class Blinders extends SCPattern {
+
+ final SinLFO[] m;
+ final TriangleLFO r;
+ final SinLFO s;
+ final TriangleLFO hs;
+
+ public Blinders(GLucose glucose) {
+ super(glucose);
+ m = new SinLFO[12];
+ for (int i = 0; i < m.length; ++i) {
+ addModulator(m[i] = new SinLFO(0.5f, 120, (120000.f / (3+i)))).trigger();
+ }
+ addModulator(r = new TriangleLFO(9000, 15000, 29000)).trigger();
+ addModulator(s = new SinLFO(-20, 275, 11000)).trigger();
+ addModulator(hs = new TriangleLFO(0.1f, 0.5f, 15000)).trigger();
+ s.modulateDurationBy(r);
+ }
+
+ public void run(double deltaMs) {
+ float hv = lx.getBaseHuef();
+ int si = 0;
+ for (Strip strip : model.strips) {
+ int i = 0;
+ float mv = m[si % m.length].getValuef();
+ for (Point p : strip.points) {
+ colors[p.index] = lx.hsb(
+ (hv + p.z + p.y*hs.getValuef()) % 360,
+ min(100, abs(p.x - s.getValuef())/2.f),
+ max(0, 100 - mv/2.f - mv * abs(i - (strip.metrics.length-1)/2.f))
+ );
+ ++i;
+ }
+ ++si;
+ }
+ }
+}
+
+class Psychedelia extends SCPattern {
+
+ final int NUM = 3;
+ SinLFO m = new SinLFO(-0.5f, NUM-0.5f, 9000);
+ SinLFO s = new SinLFO(-20, 147, 11000);
+ TriangleLFO h = new TriangleLFO(0, 240, 19000);
+ SinLFO c = new SinLFO(-.2f, .8f, 31000);
+
+ Psychedelia(GLucose glucose) {
+ super(glucose);
+ addModulator(m).trigger();
+ addModulator(s).trigger();
+ addModulator(h).trigger();
+ addModulator(c).trigger();
+ }
+
+ public void run(double deltaMs) {
+ float huev = h.getValuef();
+ float cv = c.getValuef();
+ float sv = s.getValuef();
+ float mv = m.getValuef();
+ int i = 0;
+ for (Strip strip : model.strips) {
+ for (Point p : strip.points) {
+ colors[p.index] = lx.hsb(
+ (huev + i*constrain(cv, 0, 2) + p.z/2.f + p.x/4.f) % 360,
+ min(100, abs(p.y-sv)),
+ max(0, 100 - 50*abs((i%NUM) - mv))
+ );
+ }
+ ++i;
+ }
+ }
+}
+
+class AskewPlanes extends SCPattern {
+
+ class Plane {
+ private final SinLFO a;
+ private final SinLFO b;
+ private final SinLFO c;
+ float av = 1;
+ float bv = 1;
+ float cv = 1;
+ float denom = 0.1f;
+
+ Plane(int i) {
+ addModulator(a = new SinLFO(-1, 1, 4000 + 1029*i)).trigger();
+ addModulator(b = new SinLFO(-1, 1, 11000 - 1104*i)).trigger();
+ addModulator(c = new SinLFO(-50, 50, 4000 + 1000*i * ((i % 2 == 0) ? 1 : -1))).trigger();
+ }
+
+ public void run(double deltaMs) {
+ av = a.getValuef();
+ bv = b.getValuef();
+ cv = c.getValuef();
+ denom = sqrt(av*av + bv*bv);
+ }
+ }
+
+ final Plane[] planes;
+ final int NUM_PLANES = 3;
+
+ AskewPlanes(GLucose glucose) {
+ super(glucose);
+ planes = new Plane[NUM_PLANES];
+ for (int i = 0; i < planes.length; ++i) {
+ planes[i] = new Plane(i);
+ }
+ }
+
+ public void run(double deltaMs) {
+ float huev = lx.getBaseHuef();
+
+ // This is super fucking bizarre. But if this is a for loop, the framerate
+ // tanks to like 30FPS, instead of 60. Call them manually and it works fine.
+ // Doesn't make ANY sense... there must be some weird side effect going on
+ // with the Processing internals perhaps?
+// for (Plane plane : planes) {
+// plane.run(deltaMs);
+// }
+ planes[0].run(deltaMs);
+ planes[1].run(deltaMs);
+ planes[2].run(deltaMs);
+
+ for (Point p : model.points) {
+ float d = MAX_FLOAT;
+ for (Plane plane : planes) {
+ if (plane.denom != 0) {
+ d = min(d, abs(plane.av*(p.x-model.cx) + plane.bv*(p.y-model.cy) + plane.cv) / plane.denom);
+ }
+ }
+ colors[p.index] = lx.hsb(
+ (huev + abs(p.x-model.cx)*.3f + p.y*.8f) % 360,
+ max(0, 100 - .8f*abs(p.x - model.cx)),
+ constrain(140 - 10.f*d, 0, 100)
+ );
+ }
+ }
+}
+
+class ShiftingPlane extends SCPattern {
+
+ final SinLFO a = new SinLFO(-.2f, .2f, 5300);
+ final SinLFO b = new SinLFO(1, -1, 13300);
+ final SinLFO c = new SinLFO(-1.4f, 1.4f, 5700);
+ final SinLFO d = new SinLFO(-10, 10, 9500);
+
+ ShiftingPlane(GLucose glucose) {
+ super(glucose);
+ addModulator(a).trigger();
+ addModulator(b).trigger();
+ addModulator(c).trigger();
+ addModulator(d).trigger();
+ }
+
+ public void run(double deltaMs) {
+ float hv = lx.getBaseHuef();
+ float av = a.getValuef();
+ float bv = b.getValuef();
+ float cv = c.getValuef();
+ float dv = d.getValuef();
+ float denom = sqrt(av*av + bv*bv + cv*cv);
+ for (Point p : model.points) {
+ float d = abs(av*(p.x-model.cx) + bv*(p.y-model.cy) + cv*(p.z-model.cz) + dv) / denom;
+ colors[p.index] = lx.hsb(
+ (hv + abs(p.x-model.cx)*.6f + abs(p.y-model.cy)*.9f + abs(p.z - model.cz)) % 360,
+ constrain(110 - d*6, 0, 100),
+ constrain(130 - 7*d, 0, 100)
+ );
+ }
+ }
+}
+
+class Traktor extends SCPattern {
+
+ final int FRAME_WIDTH = 60;
+
+ final BasicParameter speed = new BasicParameter("SPD", 0.5f);
+
+ private float[] bass = new float[FRAME_WIDTH];
+ private float[] treble = new float[FRAME_WIDTH];
+
+ private int index = 0;
+ private GraphicEQ eq = null;
+
+ public Traktor(GLucose glucose) {
+ super(glucose);
+ for (int i = 0; i < FRAME_WIDTH; ++i) {
+ bass[i] = 0;
+ treble[i] = 0;
+ }
+ addParameter(speed);
+ }
+
+ public void onActive() {
+ if (eq == null) {
+ eq = new GraphicEQ(lx, 16);
+ eq.slope.setValue(0.6f);
+ eq.level.setValue(0.65f);
+ eq.range.setValue(0.35f);
+ eq.release.setValue(0.4f);
+ addParameter(eq.level);
+ addParameter(eq.range);
+ addParameter(eq.attack);
+ addParameter(eq.release);
+ addParameter(eq.slope);
+ }
+ }
+
+ int counter = 0;
+
+ public void run(double deltaMs) {
+ eq.run(deltaMs);
+
+ int stepThresh = (int) (40 - 39*speed.getValuef());
+ counter += deltaMs;
+ if (counter < stepThresh) {
+ return;
+ }
+ counter = counter % stepThresh;
+
+ index = (index + 1) % FRAME_WIDTH;
+
+ float rawBass = eq.getAverageLevel(0, 4);
+ float rawTreble = eq.getAverageLevel(eq.numBands-7, 7);
+
+ bass[index] = rawBass * rawBass * rawBass * rawBass;
+ treble[index] = rawTreble * rawTreble;
+
+ for (Point p : model.points) {
+ int i = (int) constrain((model.xMax - p.x) / model.xMax * FRAME_WIDTH, 0, FRAME_WIDTH-1);
+ int pos = (index + FRAME_WIDTH - i) % FRAME_WIDTH;
+
+ colors[p.index] = lx.hsb(
+ (360 + lx.getBaseHuef() + .8f*abs(p.x-model.cx)) % 360,
+ 100,
+ constrain(9 * (bass[pos]*model.cy - abs(p.y - model.cy + 5)), 0, 100)
+ );
+ colors[p.index] = blendColor(colors[p.index], lx.hsb(
+ (400 + lx.getBaseHuef() + .5f*abs(p.x-model.cx)) % 360,
+ 60,
+ constrain(5 * (treble[pos]*.6f*model.cy - abs(p.y - model.cy)), 0, 100)
+
+ ), ADD);
+ }
+ }
+}
+
+class ColorFuckerEffect extends SCEffect {
+
+ final BasicParameter level = new BasicParameter("BRT", 1);
+ final BasicParameter desat = new BasicParameter("DSAT", 0);
+ final BasicParameter hueShift = new BasicParameter("HSHFT", 0);
+ final BasicParameter sharp = new BasicParameter("SHARP", 0);
+ final BasicParameter soft = new BasicParameter("SOFT", 0);
+ final BasicParameter mono = new BasicParameter("MONO", 0);
+ final BasicParameter invert = new BasicParameter("INVERT", 0);
+
+
+ float[] hsb = new float[3];
+
+ ColorFuckerEffect(GLucose glucose) {
+ super(glucose);
+ addParameter(level);
+ addParameter(desat);
+ addParameter(sharp);
+ addParameter(hueShift);
+ addParameter(soft);
+ addParameter(mono);
+ addParameter(invert);
+ }
+
+ public void apply(int[] colors) {
+ if (!enabled) {
+ return;
+ }
+ float bMod = level.getValuef();
+ float sMod = 1 - desat.getValuef();
+ float hMod = hueShift.getValuef();
+ float fSharp = 1/(1.0001f-sharp.getValuef());
+ float fSoft = soft.getValuef();
+ boolean mon = mono.getValuef() > 0.5f;
+ boolean ivt = invert.getValuef() > 0.5f;
+ if (bMod < 1 || sMod < 1 || hMod > 0 || fSharp > 0 || ivt || mon || fSoft > 0) {
+ for (int i = 0; i < colors.length; ++i) {
+ lx.RGBtoHSB(colors[i], hsb);
+ if (mon) {
+ hsb[0] = lx.getBaseHuef() / 360.f;
+ }
+ if (ivt) {
+ hsb[2] = 1 - hsb[2];
+ }
+ if (fSharp > 0) {
+ hsb[2] = hsb[2] < .5f ? pow(hsb[2],fSharp) : 1-pow(1-hsb[2],fSharp);
+ }
+ if (fSoft > 0) {
+ if (hsb[2] > 0.5f) {
+ hsb[2] = lerp(hsb[2], 0.5f + 2 * (hsb[2]-0.5f)*(hsb[2]-0.5f), fSoft);
+ } else {
+ hsb[2] = lerp(hsb[2], 0.5f * sqrt(2*hsb[2]), fSoft);
+ }
+ }
+ colors[i] = lx.hsb(
+ (360.f * hsb[0] + hMod*360.f) % 360,
+ 100.f * hsb[1] * sMod,
+ 100.f * hsb[2] * bMod
+ );
+ }
+ }
+ }
+}
+
+class QuantizeEffect extends SCEffect {
+
+ int[] quantizedFrame;
+ float lastQuant;
+ final BasicParameter amount = new BasicParameter("AMT", 0);
+
+ QuantizeEffect(GLucose glucose) {
+ super(glucose);
+ quantizedFrame = new int[glucose.lx.total];
+ lastQuant = 0;
+ }
+
+ public void apply(int[] colors) {
+ float fQuant = amount.getValuef();
+ if (fQuant > 0) {
+ float tRamp = (lx.tempo.rampf() % (1.f/pow(2,floor((1-fQuant) * 4))));
+ float f = lastQuant;
+ lastQuant = tRamp;
+ if (tRamp > f) {
+ for (int i = 0; i < colors.length; ++i) {
+ colors[i] = quantizedFrame[i];
+ }
+ return;
+ }
+ }
+ for (int i = 0; i < colors.length; ++i) {
+ quantizedFrame[i] = colors[i];
+ }
+ }
+}
+
+class BlurEffect extends SCEffect {
+
+ final LXParameter amount = new BasicParameter("AMT", 0);
+ final int[] frame;
+ final LinearEnvelope env = new LinearEnvelope(0, 1, 100);
+
+ BlurEffect(GLucose glucose) {
+ super(glucose);
+ addParameter(amount);
+ addModulator(env);
+ frame = new int[lx.total];
+ for (int i = 0; i < frame.length; ++i) {
+ frame[i] = 0xff000000;
+ }
+ }
+
+ public void onEnable() {
+ env.setRangeFromHereTo(1, 400).start();
+ for (int i = 0; i < frame.length; ++i) {
+ frame[i] = 0xff000000;
+ }
+ }
+
+ public void onDisable() {
+ env.setRangeFromHereTo(0, 1000).start();
+ }
+
+ public void apply(int[] colors) {
+ float amt = env.getValuef() * amount.getValuef();
+ if (amt > 0) {
+ amt = (1 - amt);
+ amt = 1 - (amt*amt*amt);
+ for (int i = 0; i < colors.length; ++i) {
+ // frame[i] = colors[i] = blendColor(colors[i], lerpColor(#000000, frame[i], amt, RGB), SCREEN);
+ frame[i] = colors[i] = lerpColor(colors[i], blendColor(colors[i], frame[i], SCREEN), amt, RGB);
+ }
+ }
+
+ }
+}
+abstract class SamPattern extends SCPattern {
+ public SamPattern(GLucose glucose) {
+ super(glucose);
+ setEligible(false);
+ }
+}
+
+class JazzRainbow extends SamPattern {
+ public JazzRainbow(GLucose glucose) {
+ super(glucose);
+ }
+
+
+ public void run(double deltaMs) {
+ // Access the core master hue via this method call
+ float hv = lx.getBaseHuef();
+ for (int i = 0; i < colors.length*5; i=i+27) {
+ float a = hv%250;
+ if (i%2 == 0) {
+ for (int b = 0; b < 70; b++) {
+ colors[(i+b)%colors.length] = lx.hsb(a+i%250, 100, b*a%100);
+ }
+ }
+ }
+ }
+}
+
+
+
+class HelixPattern extends SCPattern {
+
+ // Stores a line in point + vector form
+ private class Line {
+ private final PVector origin;
+ private final PVector vector;
+
+ Line(PVector pt, PVector v) {
+ origin = pt;
+ vector = v.get();
+ vector.normalize();
+ }
+
+ public PVector getPoint() {
+ return origin;
+ }
+
+ public PVector getVector() {
+ return vector;
+ }
+
+ public PVector getPointAt(final float t) {
+ return PVector.add(origin, PVector.mult(vector, t));
+ }
+
+ public boolean isColinear(final PVector pt) {
+ PVector projected = projectPoint(pt);
+ return projected.x==pt.x && projected.y==pt.y && projected.z==pt.z;
+ }
+
+ public float getTValue(final PVector pt) {
+ PVector subtraction = PVector.sub(pt, origin);
+ return subtraction.dot(vector);
+ }
+
+ public PVector projectPoint(final PVector pt) {
+ return getPointAt(getTValue(pt));
+ }
+
+ public PVector rotatePoint(final PVector p, final float t) {
+ final PVector o = origin;
+ final PVector v = vector;
+
+ final float cost = cos(t);
+ final float sint = sin(t);
+
+ float x = (o.x*(v.y*v.y + v.z*v.z) - v.x*(o.y*v.y + o.z*v.z - v.x*p.x - v.y*p.y - v.z*p.z))*(1 - cost) + p.x*cost + (-o.z*v.y + o.y*v.z - v.z*p.y + v.y*p.z)*sint;
+ float y = (o.y*(v.x*v.x + v.z*v.z) - v.y*(o.x*v.x + o.z*v.z - v.x*p.x - v.y*p.y - v.z*p.z))*(1 - cost) + p.y*cost + (o.z*v.x - o.x*v.z + v.z*p.x - v.x*p.z)*sint;
+ float z = (o.z*(v.x*v.x + v.y*v.y) - v.z*(o.x*v.x + o.y*v.y - v.x*p.x - v.y*p.y - v.z*p.z))*(1 - cost) + p.z*cost + (-o.y*v.x + o.x*v.y - v.y*p.x + v.x*p.y)*sint;
+ return new PVector(x, y, z);
+ }
+ }
+
+ private class Helix {
+ private final Line axis;
+ private final float period; // period of coil
+ private final float rotationPeriod; // animation period
+ private final float radius; // radius of coil
+ private final float girth; // girth of coil
+ private final PVector referencePoint;
+ private float phase;
+ private PVector phaseNormal;
+
+ Helix(Line axis, float period, float radius, float girth, float phase, float rotationPeriod) {
+ this.axis = axis;
+ this.period = period;
+ this.radius = radius;
+ this.girth = girth;
+ this.phase = phase;
+ this.rotationPeriod = rotationPeriod;
+
+ // Generate a normal that will rotate to
+ // produce the helical shape.
+ PVector pt = new PVector(0, 1, 0);
+ if (this.axis.isColinear(pt)) {
+ pt = new PVector(0, 0, 1);
+ if (this.axis.isColinear(pt)) {
+ pt = new PVector(0, 1, 1);
+ }
+ }
+
+ this.referencePoint = pt;
+
+ // The normal is calculated by the cross product of the axis
+ // and a random point that is not colinear with it.
+ phaseNormal = axis.getVector().cross(referencePoint);
+ phaseNormal.normalize();
+ phaseNormal.mult(radius);
+ }
+
+ public Line getAxis() {
+ return axis;
+ }
+
+ public PVector getPhaseNormal() {
+ return phaseNormal;
+ }
+
+ public float getPhase() {
+ return phase;
+ }
+
+ public void step(double deltaMs) {
+ // Rotate
+ if (rotationPeriod != 0) {
+ this.phase = (phase + ((float)deltaMs / (float)rotationPeriod) * TWO_PI);
+ }
+ }
+
+ public PVector pointOnToroidalAxis(float t) {
+ PVector p = axis.getPointAt(t);
+ PVector middle = PVector.add(p, phaseNormal);
+ return axis.rotatePoint(middle, (t / period) * TWO_PI + phase);
+ }
+
+ private float myDist(PVector p1, PVector p2) {
+ final float x = p2.x-p1.x;
+ final float y = p2.y-p1.y;
+ final float z = p2.z-p1.z;
+ return sqrt(x*x + y*y + z*z);
+ }
+
+ public int colorOfPoint(final PVector p) {
+ final float t = axis.getTValue(p);
+ final PVector axisPoint = axis.getPointAt(t);
+
+ // For performance reasons, cut out points that are outside of
+ // the tube where the toroidal coil lives.
+ if (abs(myDist(p, axisPoint) - radius) > girth*.5f) {
+ return lx.hsb(0,0,0);
+ }
+
+ // Find the appropriate point for the current rotation
+ // of the helix.
+ PVector toroidPoint = axisPoint;
+ toroidPoint.add(phaseNormal);
+ toroidPoint = axis.rotatePoint(toroidPoint, (t / period) * TWO_PI + phase);
+
+ // The rotated point represents the middle of the girth of
+ // the helix. Figure out if the current point is inside that
+ // region.
+ float d = myDist(p, toroidPoint);
+
+ // Soften edges by fading brightness.
+ float b = constrain(100*(1 - ((d-.5f*girth)/(girth*.5f))), 0, 100);
+ return lx.hsb((lx.getBaseHuef() + (360*(phase / TWO_PI)))%360, 80, b);
+ }
+ }
+
+ private class BasePairInfo {
+ Line line;
+ float colorPhase1;
+ float colorPhase2;
+
+ BasePairInfo(Line line, float colorPhase1, float colorPhase2) {
+ this.line = line;
+ this.colorPhase1 = colorPhase1;
+ this.colorPhase2 = colorPhase2;
+ }
+ }
+
+ private final Helix h1;
+ private final Helix h2;
+ private final BasePairInfo[] basePairs;
+
+ private final BasicParameter helix1On = new BasicParameter("H1ON", 1);
+ private final BasicParameter helix2On = new BasicParameter("H2ON", 1);
+ private final BasicParameter basePairsOn = new BasicParameter("BPON", 1);
+
+ private static final float helixCoilPeriod = 100;
+ private static final float helixCoilRadius = 50;
+ private static final float helixCoilGirth = 30;
+ private static final float helixCoilRotationPeriod = 5000;
+
+ private static final float spokePeriod = 40;
+ private static final float spokeGirth = 20;
+ private static final float spokePhase = 10;
+ private static final float spokeRadius = helixCoilRadius - helixCoilGirth*.5f;
+
+ private static final float tMin = -200;
+ private static final float tMax = 200;
+
+ public HelixPattern(GLucose glucose) {
+ super(glucose);
+
+ addParameter(helix1On);
+ addParameter(helix2On);
+ addParameter(basePairsOn);
+
+ PVector origin = new PVector(100, 50, 55);
+ PVector axis = new PVector(1,0,0);
+
+ h1 = new Helix(
+ new Line(origin, axis),
+ helixCoilPeriod,
+ helixCoilRadius,
+ helixCoilGirth,
+ 0,
+ helixCoilRotationPeriod);
+ h2 = new Helix(
+ new Line(origin, axis),
+ helixCoilPeriod,
+ helixCoilRadius,
+ helixCoilGirth,
+ PI,
+ helixCoilRotationPeriod);
+
+ basePairs = new BasePairInfo[(int)floor((tMax - tMin)/spokePeriod)];
+ }
+
+ private void calculateSpokes() {
+ float colorPhase = PI/6;
+ for (float t = tMin + spokePhase; t < tMax; t += spokePeriod) {
+ int spokeIndex = (int)floor((t - tMin)/spokePeriod);
+ PVector h1point = h1.pointOnToroidalAxis(t);
+ PVector spokeCenter = h1.getAxis().getPointAt(t);
+ PVector spokeVector = PVector.sub(h1point, spokeCenter);
+ Line spokeLine = new Line(spokeCenter, spokeVector);
+ basePairs[spokeIndex] = new BasePairInfo(spokeLine, colorPhase * spokeIndex, colorPhase * (spokeIndex + 1));
+ }
+ }
+
+ private int calculateSpokeColor(final PVector pt) {
+ // Find the closest spoke's t-value and calculate its
+ // axis. Until everything animates in the model reference
+ // frame, this has to be calculated at every step because
+ // the helices rotate.
+ Line axis = h1.getAxis();
+ float t = axis.getTValue(pt) + spokePhase;
+ int spokeIndex = (int)floor((t - tMin + spokePeriod/2) / spokePeriod);
+ if (spokeIndex < 0 || spokeIndex >= basePairs.length) {
+ return lx.hsb(0,0,0);
+ }
+ BasePairInfo basePair = basePairs[spokeIndex];
+ Line spokeLine = basePair.line;
+ PVector pointOnSpoke = spokeLine.projectPoint(pt);
+ float d = PVector.dist(pt, pointOnSpoke);
+ float b = (PVector.dist(pointOnSpoke, spokeLine.getPoint()) < spokeRadius) ? constrain(100*(1 - ((d-.5f*spokeGirth)/(spokeGirth*.5f))), 0, 100) : 0.f;
+ float phase = spokeLine.getTValue(pointOnSpoke) < 0 ? basePair.colorPhase1 : basePair.colorPhase2;
+ return lx.hsb((lx.getBaseHuef() + (360*(phase / TWO_PI)))%360, 80.f, b);
+ }
+
+ public void run(double deltaMs) {
+ boolean h1on = helix1On.getValue() > 0.5f;
+ boolean h2on = helix2On.getValue() > 0.5f;
+ boolean spokesOn = (float)basePairsOn.getValue() > 0.5f;
+
+ h1.step(deltaMs);
+ h2.step(deltaMs);
+ calculateSpokes();
+
+ for (Point p : model.points) {
+ PVector pt = new PVector(p.x,p.y,p.z);
+ int h1c = h1.colorOfPoint(pt);
+ int h2c = h2.colorOfPoint(pt);
+ int spokeColor = calculateSpokeColor(pt);
+
+ if (!h1on) {
+ h1c = lx.hsb(0,0,0);
+ }
+
+ if (!h2on) {
+ h2c = lx.hsb(0,0,0);
+ }
+
+ if (!spokesOn) {
+ spokeColor = lx.hsb(0,0,0);
+ }
+
+ // The helices are positioned to not overlap. If that changes,
+ // a better blending formula is probably needed.
+ colors[p.index] = blendColor(blendColor(h1c, h2c, ADD), spokeColor, ADD);
+ }
+ }
+}
+
+class BlankPattern extends SCPattern {
+ BlankPattern(GLucose glucose) {
+ super(glucose);
+ }
+
+ public void run(double deltaMs) {
+ setColors(0xff000000);
+ }
+}
+
+abstract class TestPattern extends SCPattern {
+ public TestPattern(GLucose glucose) {
+ super(glucose);
+ setEligible(false);
+ }
+}
+
+class TestSpeakerMapping extends TestPattern {
+ TestSpeakerMapping(GLucose glucose) {
+ super(glucose);
+ }
+
+ public void run(double deltaMs) {
+ int h = 0;
+ for (Speaker speaker : model.speakers) {
+ for (Strip strip : speaker.strips) {
+ float b = 100;
+ for (Point p : strip.points) {
+ colors[p.index] = lx.hsb(h % 360, 100, b);
+ b = max(0, b - 10);
+ }
+ h += 70;
+ }
+ }
+ }
+
+}
+
+class TestBassMapping extends TestPattern {
+ TestBassMapping(GLucose glucose) {
+ super(glucose);
+ }
+
+ public void run(double deltaMs) {
+ int[] strips = { 2, 1, 0, 3, 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6 };
+ int h = 0;
+ for (int si : strips) {
+ float b = 100;
+ for (Point p : model.bassBox.strips.get(si).points) {
+ colors[p.index] = lx.hsb(h % 360, 100, b);
+ b = max(0, b - 10);
+ }
+ h += 70;
+ }
+ }
+}
+
+class TestFloorMapping extends TestPattern {
+ TestFloorMapping(GLucose glucose) {
+ super(glucose);
+ }
+
+ public void run(double deltaMs) {
+ int[] strutIndices = {6, 5, 4, 3, 2, 1, 0, 7};
+ int h = 0;
+ for (int si : strutIndices) {
+ float b = 100;
+ for (Point p : model.bassBox.struts.get(si).points) {
+ colors[p.index] = lx.hsb(h % 360, 100, b);
+ b = max(0, b - 10);
+ }
+ h += 50;
+ }
+ int[] floorIndices = {0, 1, 2, 3};
+ h = 0;
+ for (int fi : floorIndices) {
+ float b = 100;
+ for (Point p : model.boothFloor.strips.get(fi).points) {
+ colors[p.index] = lx.hsb(h, 100, b);
+ b = max(0, b - 3);
+ }
+ h += 90;
+ }
+ }
+}
+
+class TestPerformancePattern extends TestPattern {
+
+ final BasicParameter ops = new BasicParameter("OPS", 0);
+ final BasicParameter iter = new BasicParameter("ITER", 0);
+
+ TestPerformancePattern(GLucose glucose) {
+ super(glucose);
+ addParameter(ops);
+ addParameter(iter);
+ }
+
+ public void run(double deltaMs) {
+ float x = 1;
+ for (int j = 0; j < ops.getValuef() * 400000; ++j) {
+ x *= random(0, 1);
+ }
+
+ if (iter.getValuef() < 0.25f) {
+ for (Point p : model.points) {
+ colors[p.index] = lx.hsb(
+ (p.x*.1f + p.y*.1f) % 360,
+ 100,
+ 100
+ );
+ }
+ } else if (iter.getValuef() < 0.5f) {
+ for (int i = 0; i < colors.length; ++i) {
+ colors[i] = lx.hsb(
+ (90 + model.px[i]*.1f + model.py[i]*.1f) % 360,
+ 100,
+ 100
+ );
+ }
+ } else if (iter.getValuef() < 0.75f) {
+ for (int i = 0; i < colors.length; ++i) {
+ colors[i] = lx.hsb(
+ (180 + model.p[3*i]*.1f + model.p[3*i+1]*.1f) % 360,
+ 100,
+ 100
+ );
+ }
+ } else {
+ for (int i = 0; i < colors.length; ++i) {
+ colors[i] = lx.hsb(
+ (270 + model.x(i)*.1f + model.y(i)*.1f) % 360,
+ 100,
+ 100
+ );
+ }
+ }
+ }
+}
+
+class TestStripPattern extends TestPattern {
+
+ SinLFO d = new SinLFO(4, 40, 4000);
+
+ public TestStripPattern(GLucose glucose) {
+ super(glucose);
+ addModulator(d).trigger();
+ }
+
+ public void run(double deltaMs) {
+ for (Strip s : model.strips) {
+ for (Point p : s.points) {
+ colors[p.index] = lx.hsb(
+ lx.getBaseHuef(),
+ 100,
+ max(0, 100 - d.getValuef()*dist(p.x, p.y, s.cx, s.cy))
+ );
+ }
+ }
+ }
+}
+
+/**
+ * Simplest demonstration of using the rotating master hue.
+ * All pixels are full-on the same color.
+ */
+class TestHuePattern extends TestPattern {
+ public TestHuePattern(GLucose glucose) {
+ super(glucose);
+ }
+
+ public void run(double deltaMs) {
+ // Access the core master hue via this method call
+ float hv = lx.getBaseHuef();
+ for (int i = 0; i < colors.length; ++i) {
+ colors[i] = lx.hsb(hv, 100, 100);
+ }
+ }
+}
+
+/**
+ * Test of a wave moving across the X axis.
+ */
+class TestXPattern extends TestPattern {
+ private final SinLFO xPos = new SinLFO(0, model.xMax, 4000);
+ public TestXPattern(GLucose glucose) {
+ super(glucose);
+ addModulator(xPos).trigger();
+ }
+ public void run(double deltaMs) {
+ float hv = lx.getBaseHuef();
+ for (Point p : model.points) {
+ // This is a common technique for modulating brightness.
+ // You can use abs() to determine the distance between two
+ // values. The further away this point is from an exact
+ // point, the more we decrease its brightness
+ float bv = max(0, 100 - abs(p.x - xPos.getValuef()));
+ colors[p.index] = lx.hsb(hv, 100, bv);
+ }
+ }
+}
+
+/**
+ * Test of a wave on the Y axis.
+ */
+class TestYPattern extends TestPattern {
+ private final SinLFO yPos = new SinLFO(0, model.yMax, 4000);
+ public TestYPattern(GLucose glucose) {
+ super(glucose);
+ addModulator(yPos).trigger();
+ }
+ public void run(double deltaMs) {
+ float hv = lx.getBaseHuef();
+ for (Point p : model.points) {
+ float bv = max(0, 100 - abs(p.y - yPos.getValuef()));
+ colors[p.index] = lx.hsb(hv, 100, bv);
+ }
+ }
+}
+
+/**
+ * Test of a wave on the Z axis.
+ */
+class TestZPattern extends TestPattern {
+ private final SinLFO zPos = new SinLFO(0, model.zMax, 4000);
+ public TestZPattern(GLucose glucose) {
+ super(glucose);
+ addModulator(zPos).trigger();
+ }
+ public void run(double deltaMs) {
+ float hv = lx.getBaseHuef();
+ for (Point p : model.points) {
+ float bv = max(0, 100 - abs(p.z - zPos.getValuef()));
+ colors[p.index] = lx.hsb(hv, 100, bv);
+ }
+ }
+}
+
+/**
+ * This shows how to iterate over towers, enumerated in the model.
+ */
+class TestTowerPattern extends TestPattern {
+ private final SawLFO towerIndex = new SawLFO(0, model.towers.size(), 1000*model.towers.size());
+
+ public TestTowerPattern(GLucose glucose) {
+ super(glucose);
+ addModulator(towerIndex).trigger();
+ }
+
+ public void run(double deltaMs) {
+ int ti = 0;
+ for (Tower t : model.towers) {
+ for (Point p : t.points) {
+ colors[p.index] = lx.hsb(
+ lx.getBaseHuef(),
+ 100,
+ max(0, 100 - 80*LXUtils.wrapdistf(ti, towerIndex.getValuef(), model.towers.size()))
+ );
+ }
+ ++ti;
+ }
+ }
+
+}
+
+/**
+ * This is a demonstration of how to use the projection library. A projection
+ * creates a mutation of the coordinates of all the points in the model, creating
+ * virtual x,y,z coordinates. In effect, this is like virtually rotating the entire
+ * art car. However, since in reality the car does not move, the result is that
+ * it appears that the object we are drawing on the car is actually moving.
+ *
+ * Keep in mind that what we are creating a projection of is the view coordinates.
+ * Depending on your intuition, some operations may feel backwards. For instance,
+ * if you translate the view to the right, it will make it seem that the object
+ * you are drawing has moved to the left. If you scale the view up 2x, objects
+ * drawn with the same absolute values will seem to be half the size.
+ *
+ * If this feels counterintuitive at first, don't worry. Just remember that you
+ * are moving the pixels, not the structure. We're dealing with a finite set
+ * of sparse, non-uniformly spaced pixels. Mutating the structure would move
+ * things to a space where there are no pixels in 99% of the cases.
+ */
+class TestProjectionPattern extends TestPattern {
+
+ private final Projection projection;
+ private final SawLFO angle = new SawLFO(0, TWO_PI, 9000);
+ private final SinLFO yPos = new SinLFO(-20, 40, 5000);
+
+ public TestProjectionPattern(GLucose glucose) {
+ super(glucose);
+ projection = new Projection(model);
+ addModulator(angle).trigger();
+ addModulator(yPos).trigger();
+ }
+
+ public void run(double deltaMs) {
+ // For the same reasons described above, it may logically feel to you that
+ // some of these operations are in reverse order. Again, just keep in mind that
+ // the car itself is what's moving, not the object
+ projection.reset(model)
+
+ // Translate so the center of the car is the origin, offset by yPos
+ .translateCenter(model, 0, yPos.getValuef(), 0)
+
+ // Rotate around the origin (now the center of the car) about an X-vector
+ .rotate(angle.getValuef(), 1, 0, 0)
+
+ // Scale up the Y axis (objects will look smaller in that access)
+ .scale(1, 1.5f, 1);
+
+ float hv = lx.getBaseHuef();
+ for (Coord c : projection) {
+ float d = sqrt(c.x*c.x + c.y*c.y + c.z*c.z); // distance from origin
+ // d = abs(d-60) + max(0, abs(c.z) - 20); // life saver / ring thing
+ d = max(0, abs(c.y) - 10 + .1f*abs(c.z) + .02f*abs(c.x)); // plane / spear thing
+ colors[c.index] = lx.hsb(
+ (hv + .6f*abs(c.x) + abs(c.z)) % 360,
+ 100,
+ constrain(140 - 40*d, 0, 100)
+ );
+ }
+ }
+}
+
+class TestCubePattern extends TestPattern {
+
+ private SawLFO index = new SawLFO(0, Cube.POINTS_PER_CUBE, Cube.POINTS_PER_CUBE*60);
+
+ TestCubePattern(GLucose glucose) {
+ super(glucose);
+ addModulator(index).start();
+ }
+
+ public void run(double deltaMs) {
+ for (Cube c : model.cubes) {
+ int i = 0;
+ for (Point p : c.points) {
+ colors[p.index] = lx.hsb(
+ lx.getBaseHuef(),
+ 100,
+ max(0, 100 - 80.f*abs(i - index.getValuef()))
+ );
+ ++i;
+ }
+ }
+ }
+}
+
+class MappingTool extends TestPattern {
+
+ private int cubeIndex = 0;
+ private int stripIndex = 0;
+ private int channelIndex = 0;
+
+ public final int MAPPING_MODE_ALL = 0;
+ public final int MAPPING_MODE_CHANNEL = 1;
+ public final int MAPPING_MODE_SINGLE_CUBE = 2;
+ public int mappingMode = MAPPING_MODE_ALL;
+
+ public final int CUBE_MODE_ALL = 0;
+ public final int CUBE_MODE_SINGLE_STRIP = 1;
+ public final int CUBE_MODE_STRIP_PATTERN = 2;
+ public int cubeMode = CUBE_MODE_ALL;
+
+ public boolean channelModeRed = true;
+ public boolean channelModeGreen = false;
+ public boolean channelModeBlue = false;
+
+ private final int numChannels;
+
+ private final PandaMapping[] pandaMappings;
+ private PandaMapping activePanda;
+ private ChannelMapping activeChannel;
+
+ MappingTool(GLucose glucose, PandaMapping[] pandaMappings) {
+ super(glucose);
+ this.pandaMappings = pandaMappings;
+ numChannels = pandaMappings.length * PandaMapping.CHANNELS_PER_BOARD;
+ setChannel();
+ }
+
+ public int numChannels() {
+ return numChannels;
+ }
+
+ private void setChannel() {
+ activePanda = pandaMappings[channelIndex / PandaMapping.CHANNELS_PER_BOARD];
+ activeChannel = activePanda.channelList[channelIndex % PandaMapping.CHANNELS_PER_BOARD];
+ }
+
+ private int indexOfCubeInChannel(Cube c) {
+ if (activeChannel.mode == ChannelMapping.MODE_CUBES) {
+ int i = 1;
+ for (int index : activeChannel.objectIndices) {
+ if ((index >= 0) && (c == model.getCubeByRawIndex(index))) {
+ return i;
+ }
+ ++i;
+ }
+ }
+ return 0;
+ }
+
+ private void printInfo() {
+ println("Cube:" + cubeIndex + " Strip:" + (stripIndex+1));
+ }
+
+ public void cube(int delta) {
+ int len = model.cubes.size();
+ cubeIndex = (len + cubeIndex + delta) % len;
+ printInfo();
+ }
+
+ public void strip(int delta) {
+ int len = Cube.STRIPS_PER_CUBE;
+ stripIndex = (len + stripIndex + delta) % len;
+ printInfo();
+ }
+
+ public void run(double deltaMs) {
+ int off = 0xff000000;
+ int c = off;
+ int r = 0xffFF0000;
+ int g = 0xff00FF00;
+ int b = 0xff0000FF;
+ if (channelModeRed) c |= r;
+ if (channelModeGreen) c |= g;
+ if (channelModeBlue) c |= b;
+
+ int ci = 0;
+ for (Cube cube : model.cubes) {
+ boolean cubeOn = false;
+ int indexOfCubeInChannel = indexOfCubeInChannel(cube);
+ switch (mappingMode) {
+ case MAPPING_MODE_ALL: cubeOn = true; break;
+ case MAPPING_MODE_SINGLE_CUBE: cubeOn = (cubeIndex == ci); break;
+ case MAPPING_MODE_CHANNEL: cubeOn = (indexOfCubeInChannel > 0); break;
+ }
+ if (cubeOn) {
+ if (mappingMode == MAPPING_MODE_CHANNEL) {
+ int cc = off;
+ switch (indexOfCubeInChannel) {
+ case 1: cc = r; break;
+ case 2: cc = r|g; break;
+ case 3: cc = g; break;
+ case 4: cc = b; break;
+ case 5: cc = r|b; break;
+ }
+ setColor(cube, cc);
+ } else if (cubeMode == CUBE_MODE_STRIP_PATTERN) {
+ int si = 0;
+ int sc = off;
+ for (Strip strip : cube.strips) {
+ int faceI = si / Face.STRIPS_PER_FACE;
+ switch (faceI) {
+ case 0: sc = r; break;
+ case 1: sc = g; break;
+ case 2: sc = b; break;
+ case 3: sc = r|g|b; break;
+ }
+ if (si % Face.STRIPS_PER_FACE == 2) {
+ sc = r|g;
+ }
+ setColor(strip, sc);
+ ++si;
+ }
+ } else if (cubeMode == CUBE_MODE_SINGLE_STRIP) {
+ setColor(cube, off);
+ setColor(cube.strips.get(stripIndex), c);
+ } else {
+ setColor(cube, c);
+ }
+ } else {
+ setColor(cube, off);
+ }
+ ++ci;
+ }
+ }
+
+ public void setCube(int index) {
+ cubeIndex = index % model.cubes.size();
+ }
+
+ public void incCube() {
+ cubeIndex = (cubeIndex + 1) % model.cubes.size();
+ }
+
+ public void decCube() {
+ --cubeIndex;
+ if (cubeIndex < 0) {
+ cubeIndex += model.cubes.size();
+ }
+ }
+
+ public void setChannel(int index) {
+ channelIndex = index % numChannels;
+ setChannel();
+ }
+
+ public void incChannel() {
+ channelIndex = (channelIndex + 1) % numChannels;
+ setChannel();
+ }
+
+ public void decChannel() {
+ channelIndex = (channelIndex + numChannels - 1) % numChannels;
+ setChannel();
+ }
+
+ public void setStrip(int index) {
+ stripIndex = index % Cube.STRIPS_PER_CUBE;
+ }
+
+ public void incStrip() {
+ stripIndex = (stripIndex + 1) % Cube.STRIPS_PER_CUBE;
+ }
+
+ public void decStrip() {
+ stripIndex = (stripIndex + Cube.STRIPS_PER_CUBE - 1) % Cube.STRIPS_PER_CUBE;
+ }
+
+ public void keyPressed(UIMapping uiMapping) {
+ switch (keyCode) {
+ case UP: if (mappingMode == MAPPING_MODE_CHANNEL) incChannel(); else incCube(); break;
+ case DOWN: if (mappingMode == MAPPING_MODE_CHANNEL) decChannel(); else decCube(); break;
+ case LEFT: decStrip(); break;
+ case RIGHT: incStrip(); break;
+ }
+ switch (key) {
+ case 'r': channelModeRed = !channelModeRed; break;
+ case 'g': channelModeGreen = !channelModeGreen; break;
+ case 'b': channelModeBlue = !channelModeBlue; break;
+ }
+ uiMapping.setChannelID(channelIndex+1);
+ uiMapping.setCubeID(cubeIndex+1);
+ uiMapping.setStripID(stripIndex+1);
+ uiMapping.redraw();
+ }
+
+}
+/**
+ * Not very flushed out, but kind of fun nonetheless.
+ */
+class TimSpheres extends SCPattern {
+ private BasicParameter hueParameter = new BasicParameter("RAD", 1.0f);
+ private final SawLFO lfo = new SawLFO(0, 1, 10000);
+ private final SinLFO sinLfo = new SinLFO(0, 1, 4000);
+ private final float centerX, centerY, centerZ;
+
+ class Sphere {
+ float x, y, z;
+ float radius;
+ float hue;
+ }
+
+ private final Sphere[] spheres;
+
+ public TimSpheres(GLucose glucose) {
+ super(glucose);
+ addParameter(hueParameter);
+ addModulator(lfo).trigger();
+ addModulator(sinLfo).trigger();
+ centerX = (model.xMax + model.xMin) / 2;
+ centerY = (model.yMax + model.yMin) / 2;
+ centerZ = (model.zMax + model.zMin) / 2;
+
+ spheres = new Sphere[2];
+
+ spheres[0] = new Sphere();
+ spheres[0].x = model.xMin;
+ spheres[0].y = centerY;
+ spheres[0].z = centerZ;
+ spheres[0].hue = 0;
+ spheres[0].radius = 50;
+
+ spheres[1] = new Sphere();
+ spheres[1].x = model.xMax;
+ spheres[1].y = centerY;
+ spheres[1].z = centerZ;
+ spheres[1].hue = 0.33f;
+ spheres[1].radius = 50;
+ }
+
+ public void run(double deltaMs) {
+ // Access the core master hue via this method call
+ float hv = hueParameter.getValuef();
+ float lfoValue = lfo.getValuef();
+ float sinLfoValue = sinLfo.getValuef();
+
+ spheres[0].x = model.xMin + sinLfoValue * model.xMax;
+ spheres[1].x = model.xMax - sinLfoValue * model.xMax;
+
+ spheres[0].radius = 100 * hueParameter.getValuef();
+ spheres[1].radius = 100 * hueParameter.getValuef();
+
+ for (Point p : model.points) {
+ float value = 0;
+
+ int 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, lx.hsb(((s.hue + lfoValue) % 1) * 360, 100, min(1, value) * 100), ADD);
+ }
+
+ colors[p.index] = c;
+ }
+ }
+}
+
+class Vector2 {
+ float x, y;
+
+ Vector2() {
+ this(0, 0);
+ }
+
+ Vector2(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public float distanceTo(float x, float y) {
+ return sqrt(pow(x - this.x, 2) + pow(y - this.y, 2));
+ }
+
+ public float distanceTo(Vector2 v) {
+ return distanceTo(v.x, v.y);
+ }
+
+ public Vector2 plus(float x, float y) {
+ return new Vector2(this.x + x, this.y + y);
+ }
+
+ public Vector2 plus(Vector2 v) {
+ return plus(v.x, v.y);
+ }
+
+ public Vector2 minus(Vector2 v) {
+ return plus(-1 * v.x, -1 * v.y);
+ }
+}
+
+class Vector3 {
+ float x, y, z;
+
+ Vector3() {
+ this(0, 0, 0);
+ }
+
+ Vector3(float x, float y, float z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public float distanceTo(float x, float y, float z) {
+ return sqrt(pow(x - this.x, 2) + pow(y - this.y, 2) + pow(z - this.z, 2));
+ }
+
+ public float distanceTo(Vector3 v) {
+ return distanceTo(v.x, v.y, v.z);
+ }
+
+ public float distanceTo(Point p) {
+ return distanceTo(p.x, p.y, p.z);
+ }
+
+ public void add(Vector3 other, float multiplier) {
+ this.add(other.x * multiplier, other.y * multiplier, other.z * multiplier);
+ }
+
+ public void add(float x, float y, float z) {
+ this.x += x;
+ this.y += y;
+ this.z += z;
+ }
+
+ public void divide(float factor) {
+ this.x /= factor;
+ this.y /= factor;
+ this.z /= factor;
+ }
+}
+
+class Rotation {
+ private float a, b, c, d, e, f, g, h, i;
+
+ Rotation(float yaw, float pitch, float roll) {
+ float cosYaw = cos(yaw);
+ float sinYaw = sin(yaw);
+ float cosPitch = cos(pitch);
+ float sinPitch = sin(pitch);
+ float cosRoll = cos(roll);
+ float sinRoll = sin(roll);
+
+ a = cosYaw * cosPitch;
+ b = cosYaw * sinPitch * sinRoll - sinYaw * cosRoll;
+ c = cosYaw * sinPitch * cosRoll + sinYaw * sinRoll;
+ d = sinYaw * cosPitch;
+ e = sinYaw * sinPitch * sinRoll + cosYaw * cosRoll;
+ f = sinYaw * sinPitch * cosRoll - cosYaw * sinRoll;
+ g = -1 * sinPitch;
+ h = cosPitch * sinRoll;
+ i = cosPitch * cosRoll;
+ }
+
+ public Vector3 rotated(Vector3 v) {
+ return new Vector3(
+ rotatedX(v),
+ rotatedY(v),
+ rotatedZ(v));
+
+ }
+
+ public float rotatedX(Vector3 v) {
+ return a * v.x + b * v.y + c * v.z;
+ }
+
+ public float rotatedY(Vector3 v) {
+ return d * v.x + e * v.y + f * v.z;
+ }
+
+ public float rotatedZ(Vector3 v) {
+ return g * v.x + h * v.y + i * v.z;
+ }
+}
+
+/**
+ * Very literal rain effect. Not that great as-is but some tweaking could make it nice.
+ * A couple ideas:
+ * - changing hue and direction of "rain" could make a nice fire effect
+ * - knobs to change frequency and size of rain drops
+ * - sync somehow to tempo but maybe less frequently than every beat?
+ */
+class TimRaindrops extends SCPattern {
+ public Vector3 randomVector3() {
+ return new Vector3(
+ random(model.xMax - model.xMin) + model.xMin,
+ random(model.yMax - model.yMin) + model.yMin,
+ random(model.zMax - model.zMin) + model.zMin);
+ }
+
+ class Raindrop {
+ Vector3 p;
+ Vector3 v;
+ float radius;
+ float hue;
+
+ Raindrop() {
+ this.radius = 30;
+ this.p = new Vector3(
+ random(model.xMax - model.xMin) + model.xMin,
+ model.yMax + this.radius,
+ random(model.zMax - model.zMin) + model.zMin);
+ float velMagnitude = 120;
+ this.v = new Vector3(
+ 0,
+ -3 * model.yMax,
+ 0);
+ this.hue = random(40) + 200;
+ }
+
+ // returns TRUE when this should die
+ public boolean age(double ms) {
+ p.add(v, (float) (ms / 1000.0f));
+ return this.p.y < (0 - this.radius);
+ }
+ }
+
+ private float leftoverMs = 0;
+ private float msPerRaindrop = 40;
+ private List<Raindrop> raindrops;
+
+ public TimRaindrops(GLucose glucose) {
+ super(glucose);
+ raindrops = new LinkedList<Raindrop>();
+ }
+
+ public void run(double deltaMs) {
+ leftoverMs += deltaMs;
+ while (leftoverMs > msPerRaindrop) {
+ leftoverMs -= msPerRaindrop;
+ raindrops.add(new Raindrop());
+ }
+
+ for (Point p : model.points) {
+ int c =
+ blendColor(
+ 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.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, lx.hsb(raindrop.hue, 80, (float)Math.pow(1 - d, 0.01f) * 100), ADD);
+ }
+ }
+ }
+ colors[p.index] = c;
+ }
+
+ Iterator<Raindrop> i = raindrops.iterator();
+ while (i.hasNext()) {
+ Raindrop raindrop = i.next();
+ boolean dead = raindrop.age(deltaMs);
+ if (dead) {
+ i.remove();
+ }
+ }
+ }
+}
+
+
+class TimCubes extends SCPattern {
+ private BasicParameter rateParameter = new BasicParameter("RATE", 0.125f);
+ private BasicParameter attackParameter = new BasicParameter("ATTK", 0.5f);
+ private BasicParameter decayParameter = new BasicParameter("DECAY", 0.5f);
+ private BasicParameter hueParameter = new BasicParameter("HUE", 0.5f);
+ private BasicParameter hueVarianceParameter = new BasicParameter("H.V.", 0.25f);
+ private BasicParameter saturationParameter = new BasicParameter("SAT", 0.5f);
+
+ class CubeFlash {
+ Cube c;
+ float value;
+ float hue;
+ boolean hasPeaked;
+
+ CubeFlash() {
+ c = model.cubes.get(floor(random(model.cubes.size())));
+ hue = random(1);
+ boolean infiniteAttack = (attackParameter.getValuef() > 0.999f);
+ hasPeaked = infiniteAttack;
+ value = (infiniteAttack ? 1 : 0);
+ }
+
+ // returns TRUE if this should die
+ public boolean age(double ms) {
+ if (!hasPeaked) {
+ value = value + (float) (ms / 1000.0f * ((attackParameter.getValuef() + 0.01f) * 5));
+ if (value >= 1.0f) {
+ value = 1.0f;
+ hasPeaked = true;
+ }
+ return false;
+ } else {
+ value = value - (float) (ms / 1000.0f * ((decayParameter.getValuef() + 0.01f) * 10));
+ return value <= 0;
+ }
+ }
+ }
+
+ private float leftoverMs = 0;
+ private List<CubeFlash> flashes;
+
+ public TimCubes(GLucose glucose) {
+ super(glucose);
+ addParameter(rateParameter);
+ addParameter(attackParameter);
+ addParameter(decayParameter);
+ addParameter(hueParameter);
+ addParameter(hueVarianceParameter);
+ addParameter(saturationParameter);
+ flashes = new LinkedList<CubeFlash>();
+ }
+
+ public void run(double deltaMs) {
+ leftoverMs += deltaMs;
+ float msPerFlash = 1000 / ((rateParameter.getValuef() + .01f) * 100);
+ while (leftoverMs > msPerFlash) {
+ leftoverMs -= msPerFlash;
+ flashes.add(new CubeFlash());
+ }
+
+ for (Point p : model.points) {
+ colors[p.index] = 0;
+ }
+
+ for (CubeFlash flash : flashes) {
+ float hue = (hueParameter.getValuef() + (hueVarianceParameter.getValuef() * flash.hue)) % 1.0f;
+ int c = lx.hsb(hue * 360, saturationParameter.getValuef() * 100, (flash.value) * 100);
+ for (Point p : flash.c.points) {
+ colors[p.index] = c;
+ }
+ }
+
+ Iterator<CubeFlash> i = flashes.iterator();
+ while (i.hasNext()) {
+ CubeFlash flash = i.next();
+ boolean dead = flash.age(deltaMs);
+ if (dead) {
+ i.remove();
+ }
+ }
+ }
+}
+
+/**
+ * This one is the best but you need to play with all the knobs. It's synced to
+ * the tempo, with the WSpd knob letting you pick 4 discrete multipliers for
+ * the tempo.
+ *
+ * Basically it's just 3 planes all rotating to the beat, but also rotated relative
+ * to one another. The intersection of the planes and the cubes over time makes
+ * for a nice abstract effect.
+ */
+class TimPlanes extends SCPattern {
+ private BasicParameter wobbleParameter = new BasicParameter("Wob", 0.166f);
+ private BasicParameter wobbleSpreadParameter = new BasicParameter("WSpr", 0.25f);
+ private BasicParameter wobbleSpeedParameter = new BasicParameter("WSpd", 0.375f);
+ private BasicParameter wobbleOffsetParameter = new BasicParameter("WOff", 0);
+ private BasicParameter derezParameter = new BasicParameter("Drez", 0.5f);
+ private BasicParameter thicknessParameter = new BasicParameter("Thick", 0.4f);
+ private BasicParameter ySpreadParameter = new BasicParameter("ySpr", 0.2f);
+ private BasicParameter hueParameter = new BasicParameter("Hue", 0.75f);
+ private BasicParameter hueSpreadParameter = new BasicParameter("HSpr", 0.68f);
+
+ final float centerX, centerY, centerZ;
+ float phase;
+
+ class Plane {
+ Vector3 center;
+ Rotation rotation;
+ float hue;
+
+ Plane(Vector3 center, Rotation rotation, float hue) {
+ this.center = center;
+ this.rotation = rotation;
+ this.hue = hue;
+ }
+ }
+
+ TimPlanes(GLucose glucose) {
+ super(glucose);
+ centerX = (model.xMin + model.xMax) / 2;
+ centerY = (model.yMin + model.yMax) / 2;
+ centerZ = (model.zMin + model.zMax) / 2;
+ phase = 0;
+ addParameter(wobbleParameter);
+ addParameter(wobbleSpreadParameter);
+ addParameter(wobbleSpeedParameter);
+// addParameter(wobbleOffsetParameter);
+ addParameter(derezParameter);
+ addParameter(thicknessParameter);
+ addParameter(ySpreadParameter);
+ addParameter(hueParameter);
+ addParameter(hueSpreadParameter);
+ }
+
+ int beat = 0;
+ float prevRamp = 0;
+ float[] wobbleSpeeds = { 1.0f/8, 1.0f/4, 1.0f/2, 1.0f };
+
+ public void run(double deltaMs) {
+ float ramp = (float)lx.tempo.ramp();
+ if (ramp < prevRamp) {
+ beat = (beat + 1) % 32;
+ }
+ prevRamp = ramp;
+
+ float wobbleSpeed = wobbleSpeeds[floor(wobbleSpeedParameter.getValuef() * wobbleSpeeds.length * 0.9999f)];
+
+ phase = (((beat + ramp) * wobbleSpeed + wobbleOffsetParameter.getValuef()) % 1) * 2 * PI;
+
+ float ySpread = ySpreadParameter.getValuef() * 50;
+ float wobble = wobbleParameter.getValuef() * PI;
+ float wobbleSpread = wobbleSpreadParameter.getValuef() * PI;
+ float hue = hueParameter.getValuef() * 360;
+ float hueSpread = (hueSpreadParameter.getValuef() - 0.5f) * 360;
+
+ float saturation = 10 + 60.0f * pow(ramp, 0.25f);
+
+ float derez = derezParameter.getValuef();
+
+ Plane[] planes = {
+ new Plane(
+ new Vector3(centerX, centerY + ySpread, centerZ),
+ new Rotation(wobble - wobbleSpread, phase, 0),
+ (hue + 360 - hueSpread) % 360),
+ new Plane(
+ new Vector3(centerX, centerY, centerZ),
+ new Rotation(wobble, phase, 0),
+ hue),
+ new Plane(
+ new Vector3(centerX, centerY - ySpread, centerZ),
+ new Rotation(wobble + wobbleSpread, phase, 0),
+ (hue + 360 + hueSpread) % 360)
+ };
+
+ float thickness = (thicknessParameter.getValuef() * 25 + 1);
+
+ Vector3 normalizedPoint = new Vector3();
+
+ for (Point p : model.points) {
+ if (random(1.0f) < derez) {
+ continue;
+ }
+
+ int c = 0;
+
+ for (Plane plane : planes) {
+ 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 int planeColor;
+ if (d <= thickness) {
+ planeColor = lx.hsb(plane.hue, saturation, 100);
+ } else if (d <= thickness * 2) {
+ float value = 1 - ((d - thickness) / thickness);
+ planeColor = lx.hsb(plane.hue, saturation, value * 100);
+ } else {
+ planeColor = 0;
+ }
+
+ if (planeColor != 0) {
+ if (c == 0) {
+ c = planeColor;
+ } else {
+ c = blendColor(c, planeColor, ADD);
+ }
+ }
+ }
+
+ colors[p.index] = c;
+ }
+ }
+}
+
+/**
+ * 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.75f);
+ private BasicParameter vertSpreadParameter = new BasicParameter("VSpr", 0.5f);
+ private BasicParameter vertOffsetParameter = new BasicParameter("VOff", 1.0f);
+ private BasicParameter zSlopeParameter = new BasicParameter("ZSlp", 0.6f);
+ private BasicParameter sharpnessParameter = new BasicParameter("Shrp", 0.25f);
+ private BasicParameter derezParameter = new BasicParameter("Drez", 0.25f);
+ private BasicParameter clickinessParameter = new BasicParameter("Clic", 0.5f);
+ private BasicParameter hueParameter = new BasicParameter("Hue", 0.667f);
+ private BasicParameter hueSpreadParameter = new BasicParameter("HSpd", 0.667f);
+
+ float phase = 0;
+ private final int NUM_BLADES = 12;
+
+ class Pinwheel {
+ Vector2 center;
+ int numBlades;
+ float realPhase;
+ float phase;
+ float speed;
+
+ Pinwheel(float xCenter, float yCenter, int numBlades, float speed) {
+ this.center = new Vector2(xCenter, yCenter);
+ this.numBlades = numBlades;
+ this.speed = speed;
+ }
+
+ public void age(float numBeats) {
+ int numSteps = numBlades;
+
+ realPhase = (realPhase + numBeats / numSteps) % 2.0f;
+
+ float phaseStep = floor(realPhase * numSteps);
+ float phaseRamp = (realPhase * numSteps) % 1.0f;
+ phase = (phaseStep + pow(phaseRamp, (clickinessParameter.getValuef() * 10) + 1)) / (numSteps * 2);
+// phase = (phase + deltaMs / 1000.0 * speed) % 1.0;
+ }
+
+ public boolean isOnBlade(float x, float y) {
+ x = x - center.x;
+ y = y - center.y;
+
+ 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;
+ }
+ }
+
+ 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.1f));
+ pinwheels.add(new Pinwheel(0, 0, NUM_BLADES, -0.1f));
+
+ 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 spread = horizSpreadParameter.getValuef() - 0.5f;
+ 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();
+
+ float spread = vertSpreadParameter.getValuef() - 0.5f;
+ pinwheels.get(0).center.y = yCenter - yDist * spread;
+ pinwheels.get(1).center.y = yCenter + yDist * spread;
+ }
+
+ 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.5f) * 360;
+
+ float fadeAmount = (float) (deltaMs / 1000.0f) * pow(sharpnessParameter.getValuef() * 10, 1);
+
+ for (Pinwheel pw : pinwheels) {
+ pw.age(numBeats);
+ }
+
+ float derez = derezParameter.getValuef();
+
+ float zSlope = (zSlopeParameter.getValuef() - 0.5f) * 2;
+
+ int i = -1;
+ for (Point p : model.points) {
+ ++i;
+
+ int value = 0;
+ for (Pinwheel pw : pinwheels) {
+ value += (pw.isOnBlade(p.x, p.y - p.z * zSlope) ? 1 : 0);
+ }
+ if (value == 1) {
+ values[i] = 1;
+// colors[p.index] = lx.hsb(120, 0, 100);
+ } else {
+ 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.0f) >= derez) {
+ float v = values[i];
+ colors[p.index] = lx.hsb((360 + hue + pow(v, 2) * hueSpread) % 360, 30 + pow(1 - v, 0.25f) * 60, v * 100);
+ }
+ }
+ }
+}
+
+/**
+ * This tries to figure out neighboring pixels from one cube to another to
+ * let you have a bunch of moving points tracing all over the structure.
+ * Adds a couple seconds of startup time to do the calculation, and in the
+ * end just comes out looking a lot like a screensaver. Probably not worth
+ * it but there may be useful code here.
+ */
+class TimTrace extends SCPattern {
+ private Map<Point, List<Point>> pointToNeighbors;
+ private Map<Point, Strip> pointToStrip;
+ // private final Map<Strip, List<Strip>> stripToNearbyStrips;
+
+ int extraMs;
+
+ class MovingPoint {
+ Point currentPoint;
+ float hue;
+ private Strip currentStrip;
+ private int currentStripIndex;
+ private int direction; // +1 or -1
+
+ MovingPoint(Point p) {
+ this.setPointOnNewStrip(p);
+ hue = random(360);
+ }
+
+ private void setPointOnNewStrip(Point p) {
+ this.currentPoint = p;
+ this.currentStrip = pointToStrip.get(p);
+ for (int i = 0; i < this.currentStrip.points.size(); ++i) {
+ if (this.currentStrip.points.get(i) == p) {
+ this.currentStripIndex = i;
+ break;
+ }
+ }
+ if (this.currentStripIndex == 0) {
+ // we are at the beginning of the strip; go forwards
+ this.direction = 1;
+ } else if (this.currentStripIndex == this.currentStrip.points.size()) {
+ // we are at the end of the strip; go backwards
+ this.direction = -1;
+ } else {
+ // we are in the middle of a strip; randomly go one way or another
+ this.direction = ((random(1.0f) < 0.5f) ? -1 : 1);
+ }
+ }
+
+ public void step() {
+ List<Point> neighborsOnOtherStrips = pointToNeighbors.get(this.currentPoint);
+
+ Point nextPointOnCurrentStrip = null;
+ this.currentStripIndex += this.direction;
+ if (this.currentStripIndex >= 0 && this.currentStripIndex < this.currentStrip.points.size()) {
+ nextPointOnCurrentStrip = this.currentStrip.points.get(this.currentStripIndex);
+ }
+
+ // pick which option to take; if we can keep going on the current strip then
+ // add that as another option
+ int option = floor(random(neighborsOnOtherStrips.size() + (nextPointOnCurrentStrip == null ? 0 : 100)));
+
+ if (option < neighborsOnOtherStrips.size()) {
+ this.setPointOnNewStrip(neighborsOnOtherStrips.get(option));
+ } else {
+ this.currentPoint = nextPointOnCurrentStrip;
+ }
+ }
+ }
+
+ List<MovingPoint> movingPoints;
+
+ TimTrace(GLucose glucose) {
+ super(glucose);
+
+ extraMs = 0;
+
+ pointToNeighbors = this.buildPointToNeighborsMap();
+ pointToStrip = this.buildPointToStripMap();
+
+ int numMovingPoints = 1000;
+ movingPoints = new ArrayList();
+ for (int i = 0; i < numMovingPoints; ++i) {
+ movingPoints.add(new MovingPoint(model.points.get(floor(random(model.points.size())))));
+ }
+
+ }
+
+ private Map<Strip, List<Strip>> buildStripToNearbyStripsMap() {
+ Map<Strip, Vector3> stripToCenter = new HashMap();
+ for (Strip s : model.strips) {
+ Vector3 v = new Vector3();
+ for (Point p : s.points) {
+ v.add(p.x, p.y, p.z);
+ }
+ v.divide(s.points.size());
+ stripToCenter.put(s, v);
+ }
+
+ Map<Strip, List<Strip>> stripToNeighbors = new HashMap();
+ for (Strip s : model.strips) {
+ List<Strip> neighbors = new ArrayList();
+ Vector3 sCenter = stripToCenter.get(s);
+ for (Strip potentialNeighbor : model.strips) {
+ if (s != potentialNeighbor) {
+ float distance = sCenter.distanceTo(stripToCenter.get(potentialNeighbor));
+ if (distance < 25) {
+ neighbors.add(potentialNeighbor);
+ }
+ }
+ }
+ stripToNeighbors.put(s, neighbors);
+ }
+
+ return stripToNeighbors;
+ }
+
+ private Map<Point, List<Point>> buildPointToNeighborsMap() {
+ Map<Point, List<Point>> 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.x, p.y, p.z);
+
+ List<Point> neighbors = new ArrayList();
+
+ for (Strip nearbyStrip : nearbyStrips) {
+ Point closestPoint = null;
+ float closestPointDistance = 100000;
+
+ for (Point nsp : nearbyStrip.points) {
+ float distance = v.distanceTo(nsp.x, nsp.y, nsp.z);
+ if (closestPoint == null || distance < closestPointDistance) {
+ closestPoint = nsp;
+ closestPointDistance = distance;
+ }
+ }
+
+ if (closestPointDistance < 15) {
+ neighbors.add(closestPoint);
+ }
+ }
+
+ m.put(p, neighbors);
+ }
+ }
+
+ return m;
+ }
+
+ private Map<Point, Strip> buildPointToStripMap() {
+ Map<Point, Strip> m = new HashMap();
+ for (Strip s : model.strips) {
+ for (Point p : s.points) {
+ m.put(p, s);
+ }
+ }
+ return m;
+ }
+
+ public void run(double deltaMs) {
+ for (Point p : model.points) {
+ int c = colors[p.index];
+ 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], lx.hsb(mp.hue, 10, 100), ADD);
+ }
+ }
+}
+class GlitchPlasma extends SCPattern {
+ private int pos = 0;
+ private float satu = 100;
+ private float speed = 1;
+ private float glitch = 0;
+ BasicParameter saturationParameter = new BasicParameter("SATU", 1.0f);
+ BasicParameter speedParameter = new BasicParameter("SPEED", 0.1f);
+ BasicParameter glitchParameter = new BasicParameter("GLITCH", 0.0f);
+
+ public GlitchPlasma(GLucose glucose) {
+ super(glucose);
+ addParameter(saturationParameter);
+ addParameter(speedParameter);
+ addParameter(glitchParameter);
+ }
+ public void onParameterChanged(LXParameter parameter) {
+ if (parameter == saturationParameter) {
+ satu = 100*parameter.getValuef();
+ } else if (parameter == speedParameter) {
+ speed = 8*parameter.getValuef();
+ } else if (parameter == glitchParameter) {
+ glitch = parameter.getValuef();
+ }
+ }
+
+ public void run(double deltaMs) {
+ for (Point p : model.points) {
+ float hv = sin(dist(p.x + pos, p.y, 128.0f, 128.0f) / 8.0f)
+ + sin(dist(p.x, p.y, 64.0f, 64.0f) / 8.0f)
+ + sin(dist(p.x, p.y + pos / 7, 192.0f, 64.0f) / 7.0f)
+ + sin(dist(p.x, p.z + pos, 192.0f, 100.0f) / 8.0f);
+ float bv = 100;
+ colors[p.index] = lx.hsb((hv+2)*50, satu, bv);
+ }
+ if (random(1.0f)<glitch/20) {
+ pos=pos-PApplet.parseInt(random(10,30));
+ }
+ pos+=speed;
+ if (pos >= MAX_INT-1) pos=0;
+ }
+}
+
+// This is very much a work in progress. Trying to get a flame effect.
+class FireEffect extends SCPattern {
+ private float[][] intensity;
+ private float hotspot;
+ private float decay = 0.3f;
+ private int xm;
+ private int ym;
+ BasicParameter decayParameter = new BasicParameter("DECAY", 0.3f);
+
+ public FireEffect(GLucose glucose) {
+ super(glucose);
+ xm = PApplet.parseInt(model.xMax);
+ ym = PApplet.parseInt(model.yMax);
+
+ intensity = new float[xm][ym];
+ addParameter(decayParameter);
+ }
+ public void onParameterChanged(LXParameter parameter) {
+ if (parameter == decayParameter) {
+ decay = parameter.getValuef();
+ }
+ }
+ private int flameColor(float level) {
+ if (level<=0) return lx.hsb(0,0,0);
+ float br=min(100,sqrt(level)*15);
+ return lx.hsb(level/1.7f,100,br);
+ }
+ public void run(double deltaMs) {
+ for (int x=10;x<xm-10;x++) {
+ if (x%50>45 || x%50<5) {
+ intensity[x][ym-1] = random(30,100);
+ } else {
+ intensity[x][ym-1] = random(0,50);
+ }
+ }
+ for (int x=1;x<xm-1;x++) {
+ for (int y=0;y<ym-1;y++) {
+ intensity[x][y] = (intensity[x-1][y+1]+intensity[x][y+1]+intensity[x+1][y+1])/3-decay;
+ }
+ }
+
+ for (Point p : model.points) {
+ int x = max(0,(PApplet.parseInt(p.x)+PApplet.parseInt(p.z))%xm);
+ int y = constrain(ym-PApplet.parseInt(p.y),0,ym-1);
+ colors[p.index] = flameColor(intensity[x][y]);
+ }
+ }
+}
+
+class StripBounce extends SCPattern {
+ private final int numOsc = 30;
+ SinLFO[] fX = new SinLFO[numOsc]; //new SinLFO(0, model.xMax, 5000);
+ SinLFO[] fY = new SinLFO[numOsc]; //new SinLFO(0, model.yMax, 4000);
+ SinLFO[] fZ = new SinLFO[numOsc]; //new SinLFO(0, model.yMax, 3000);
+ SinLFO[] sat = new SinLFO[numOsc];
+ float[] colorOffset = new float[numOsc];
+
+ public StripBounce(GLucose glucose) {
+ super(glucose);
+ for (int i=0;i<numOsc;i++) {
+ fX[i] = new SinLFO(0, model.xMax, random(2000,20000));
+ fY[i] = new SinLFO(0, model.yMax, random(2000,20000));
+ fZ[i] = new SinLFO(0, model.zMax, random(2000,20000));
+ sat[i] = new SinLFO(60, 100, random(2000,50000));
+ addModulator(fX[i]).trigger();
+ addModulator(fY[i]).trigger();
+ addModulator(fZ[i]).trigger();
+ colorOffset[i]=random(0,256);
+ }
+ }
+
+ public void run(double deltaMs) {
+ float[] bright = new float[model.points.size()];
+ for (Strip strip : model.strips) {
+ for (int i=0;i<numOsc;i++) {
+ float avgdist=0.0f;
+ avgdist = dist(strip.points.get(8).x,strip.points.get(8).y,strip.points.get(8).z,fX[i].getValuef(),fY[i].getValuef(),fZ[i].getValuef());
+ boolean on = avgdist<30;
+ float hv = (lx.getBaseHuef()+colorOffset[i])%360;
+ float br = max(0,100-avgdist*4);
+ for (Point p : strip.points) {
+ if (on && br>bright[p.index]) {
+ colors[p.index] = lx.hsb(hv,sat[i].getValuef(),br);
+ bright[p.index] = br;
+ }
+ }
+ }
+ }
+ }
+}
+
+class SoundRain extends SCPattern {
+
+ private FFT fft = null;
+ private LinearEnvelope[] bandVals = null;
+ private float[] lightVals = null;
+ private int avgSize;
+ private float gain = 25;
+ SawLFO pos = new SawLFO(0, 9, 8000);
+ SinLFO col1 = new SinLFO(0, model.xMax, 5000);
+ BasicParameter gainParameter = new BasicParameter("GAIN", 0.5f);
+
+ public SoundRain(GLucose glucose) {
+ super(glucose);
+ addModulator(pos).trigger();
+ addModulator(col1).trigger();
+ addParameter(gainParameter);
+ }
+
+ public void onParameterChanged(LXParameter parameter) {
+ if (parameter == gainParameter) {
+ gain = 50*parameter.getValuef();
+ }
+ }
+ protected void onActive() {
+ if (this.fft == null) {
+ this.fft = new FFT(lx.audioInput().bufferSize(), lx.audioInput().sampleRate());
+ this.fft.window(FFT.HAMMING);
+ this.fft.logAverages(40, 1);
+ this.avgSize = this.fft.avgSize();
+ this.bandVals = new LinearEnvelope[this.avgSize];
+ for (int i = 0; i < this.bandVals.length; ++i) {
+ this.addModulator(this.bandVals[i] = (new LinearEnvelope(0, 0, 700+i*4))).trigger();
+ }
+ lightVals = new float[avgSize];
+ }
+ }
+
+ public void run(double deltaMs) {
+ this.fft.forward(this.lx.audioInput().mix);
+ for (int i = 0; i < avgSize; ++i) {
+ float value = this.fft.getAvg(i);
+ this.bandVals[i].setEndVal(value,40).trigger();
+ float lv = min(value*gain,100);
+ if (lv>lightVals[i]) {
+ lightVals[i]=min(lightVals[i]+15,lv,100);
+ } else {
+ lightVals[i]=max(lv,lightVals[i]-5,0);
+ }
+ }
+ for (Cube c : model.cubes) {
+ for (int j=0; j<c.strips.size(); j++) {
+ Strip s = c.strips.get(j);
+ if (j%4!=0 && j%4!=2) {
+ for (Point p : s.points) {
+ int seq = PApplet.parseInt(p.y*avgSize/model.yMax+pos.getValuef()+sin(p.x+p.z)*2)%avgSize;
+ seq=min(abs(seq-(avgSize/2)),avgSize-1);
+ colors[p.index] = lx.hsb(200,max(0,100-abs(p.x-col1.getValuef())/2),lightVals[seq]);
+ }
+ }
+ }
+ }
+ }
+}
+
+class FaceSync extends SCPattern {
+ SinLFO xosc = new SinLFO(-10, 10, 3000);
+ SinLFO zosc = new SinLFO(-10, 10, 3000);
+ SinLFO col1 = new SinLFO(0, model.xMax, 5000);
+ SinLFO col2 = new SinLFO(0, model.xMax, 4000);
+
+ public FaceSync(GLucose glucose) {
+ super(glucose);
+ addModulator(xosc).trigger();
+ addModulator(zosc).trigger();
+ zosc.setValue(0);
+ addModulator(col1).trigger();
+ addModulator(col2).trigger();
+ col2.setValue(model.xMax);
+ }
+
+ public void run(double deltaMs) {
+ int i=0;
+ for (Strip s : model.strips) {
+ i++;
+ for (Point p : s.points) {
+ float dx, dz;
+ if (i%32 < 16) {
+ dx = p.x - (s.cx+xosc.getValuef());
+ dz = p.z - (s.cz+zosc.getValuef());
+ } else {
+ dx = p.x - (s.cx+zosc.getValuef());
+ dz = p.z - (s.cz+xosc.getValuef());
+ }
+ //println(dx);
+ float a1=max(0,100-abs(p.x-col1.getValuef()));
+ float a2=max(0,100-abs(p.x-col2.getValuef()));
+ float sat = max(a1,a2);
+ float h = (359*a1+200*a2) / (a1+a2);
+ colors[p.index] = lx.hsb(h,sat,100-abs(dx*5)-abs(dz*5));
+ }
+ }
+ }
+}
+
+class SoundSpikes extends SCPattern {
+ private FFT fft = null;
+ private LinearEnvelope[] bandVals = null;
+ private float[] lightVals = null;
+ private int avgSize;
+ private float gain = 25;
+ BasicParameter gainParameter = new BasicParameter("GAIN", 0.5f);
+ SawLFO pos = new SawLFO(0, model.xMax, 8000);
+
+ public SoundSpikes(GLucose glucose) {
+ super(glucose);
+ addParameter(gainParameter);
+ addModulator(pos).trigger();
+ }
+
+ public void onParameterChanged(LXParameter parameter) {
+ if (parameter == gainParameter) {
+ gain = 50*parameter.getValuef();
+ }
+ }
+ protected void onActive() {
+ if (this.fft == null) {
+ this.fft = new FFT(lx.audioInput().bufferSize(), lx.audioInput().sampleRate());
+ this.fft.window(FFT.HAMMING);
+ this.fft.logAverages(40, 1);
+ this.avgSize = this.fft.avgSize();
+ this.bandVals = new LinearEnvelope[this.avgSize];
+ for (int i = 0; i < this.bandVals.length; ++i) {
+ this.addModulator(this.bandVals[i] = (new LinearEnvelope(0, 0, 700+i*4))).trigger();
+ }
+ lightVals = new float[avgSize];
+ }
+ }
+
+ public void run(double deltaMs) {
+ this.fft.forward(this.lx.audioInput().mix);
+ for (int i = 0; i < avgSize; ++i) {
+ float value = this.fft.getAvg(i);
+ this.bandVals[i].setEndVal(value,40).trigger();
+ float lv = min(value*gain,model.yMax+10);
+ if (lv>lightVals[i]) {
+ lightVals[i]=min(lightVals[i]+30,lv,model.yMax+10);
+ } else {
+ lightVals[i]=max(lv,lightVals[i]-10,0);
+ }
+ }
+ int i = 0;
+ for (Cube c : model.cubes) {
+ for (int j=0; j<c.strips.size(); j++) {
+ Strip s = c.strips.get(j);
+ if (j%4!=0 && j%4!=2) {
+ for (Point p : s.points) {
+ float dis = (abs(p.x-model.xMax/2)+pos.getValuef())%model.xMax/2;
+ int seq = PApplet.parseInt((dis*avgSize*2)/model.xMax);
+ if (seq>avgSize) seq=avgSize-seq;
+ seq=constrain(seq,0,avgSize-1);
+ float br=max(0, lightVals[seq]-p.y);
+ colors[p.index] = lx.hsb((dis*avgSize*65)/model.xMax,90,br);
+ }
+ }
+ }
+ }
+ }
+}
+
+ static public void main(String[] passedArgs) {
+ String[] appletArgs = new String[] { "SugarCubes" };
+ if (passedArgs != null) {
+ PApplet.main(concat(appletArgs, passedArgs));
+ } else {
+ PApplet.main(appletArgs);
+ }
+ }
+}