Work-in-progress "Rings" effect, ported from Fadecandy
[SugarCubes.git] / build-tmp / source / SugarCubes.java
1 import processing.core.*;
2 import processing.data.*;
3 import processing.event.*;
4 import processing.opengl.*;
5
6 import netP5.*;
7 import oscP5.*;
8 import processing.serial.*;
9 import java.util.LinkedHashMap;
10 import toxi.geom.Vec3D;
11 import toxi.geom.Matrix4x4;
12
13 import heronarts.lx.font.*;
14 import heronarts.lx.transition.*;
15 import glucose.transform.*;
16 import netP5.*;
17 import heronarts.lx.pattern.*;
18 import glucose.pattern.*;
19 import heronarts.lx.model.*;
20 import toxi.geom.mesh2d.*;
21 import heronarts.lx.client.*;
22 import glucose.*;
23 import toxi.util.datatypes.*;
24 import toxi.math.waves.*;
25 import heronarts.lx.kinet.*;
26 import oscP5.*;
27 import toxi.geom.*;
28 import toxi.util.events.*;
29 import heronarts.lx.modulator.*;
30 import rwmidi.*;
31 import glucose.transition.*;
32 import glucose.effect.*;
33 import glucose.model.*;
34 import toxi.math.conversion.*;
35 import heronarts.lx.effect.*;
36 import heronarts.lx.control.*;
37 import glucose.control.*;
38 import toxi.math.noise.*;
39 import toxi.util.*;
40 import heronarts.lx.*;
41 import toxi.math.*;
42 import heronarts.lx.audio.*;
43
44 import java.util.HashMap;
45 import java.util.ArrayList;
46 import java.io.File;
47 import java.io.BufferedReader;
48 import java.io.PrintWriter;
49 import java.io.InputStream;
50 import java.io.OutputStream;
51 import java.io.IOException;
52
53 public class SugarCubes extends PApplet {
54
55 /**
56 * +-+-+-+-+-+ +-+-+-+-+-+
57 * / /| |\ \
58 * / / + + \ \
59 * +-+-+-+-+-+ | +-+-+-+-+ | +-+-+-+-+-+
60 * | | + / \ + | |
61 * + THE + / / \ \ + CUBES +
62 * | |/ +-+-+-+-+-+-+-+ \| |
63 * +-+-+-+-+-+ | | +-+-+-+-+-+
64 * + +
65 * | SUGAR |
66 * + +
67 * | |
68 * +-+-+-+-+-+-+-+
69 *
70 * Welcome to the Sugar Cubes! This Processing sketch is a fun place to build
71 * animations, effects, and interactions for the platform. Most of the icky
72 * code guts are embedded in the GLucose library extension. If you're an
73 * artist, you shouldn't need to worry about any of that.
74 *
75 * Below, you will find definitions of the Patterns, Effects, and Interactions.
76 * If you're an artist, create a new tab in the Processing environment with
77 * your name. Implement your classes there, and add them to the list below.
78 */
79
80 public LXPattern[] patterns(GLucose glucose) {
81 return new LXPattern[] {
82
83
84 // Slee
85 new Cathedrals(glucose),
86 new MidiMusic(glucose),
87 new Pulley(glucose),
88 new Swarm(glucose),
89 new ViolinWave(glucose),
90 new BouncyBalls(glucose),
91 new SpaceTime(glucose),
92 new ShiftingPlane(glucose),
93 new AskewPlanes(glucose),
94 new Blinders(glucose),
95 new CrossSections(glucose),
96 new Psychedelia(glucose),
97
98 new Traktor(glucose).setEligible(false),
99 new BassPod(glucose).setEligible(false),
100 new CubeEQ(glucose).setEligible(false),
101 new PianoKeyPattern(glucose).setEligible(false),
102
103 // DanH
104 new Noise(glucose),
105 new Play (glucose),
106 new Pong (glucose),
107 new Worms(glucose),
108
109 // Alex G
110 new SineSphere(glucose),
111 // new CubeCurl(glucose),
112
113 // Shaheen
114 new HelixPattern(glucose).setEligible(false),
115
116 // Toby
117 new GlitchPlasma(glucose),
118 new FireEffect(glucose).setEligible(false),
119 new StripBounce(glucose),
120 new SoundRain(glucose).setEligible(false),
121 new SoundSpikes(glucose).setEligible(false),
122 new FaceSync(glucose),
123
124 // Jack
125 new Swim(glucose),
126 new Balance(glucose),
127
128 // Tim
129 new TimPlanes(glucose),
130 new TimPinwheels(glucose),
131 new TimRaindrops(glucose),
132 new TimCubes(glucose),
133 // new TimTrace(glucose),
134 new TimSpheres(glucose),
135
136 // Ben
137 // new Sandbox(glucose),
138 new TowerParams(glucose),
139 new DriveableCrossSections(glucose),
140 new GranimTestPattern2(glucose),
141
142 //JR
143 new Gimbal(glucose),
144
145 // Sam
146 new JazzRainbow(glucose),
147
148 // Arjun
149 new TelevisionStatic(glucose),
150 new AbstractPainting(glucose),
151 new Spirality(glucose),
152
153 // Basic test patterns for reference, not art
154 new TestCubePattern(glucose),
155 new TestTowerPattern(glucose),
156 new TestProjectionPattern(glucose),
157 new TestStripPattern(glucose),
158 new TestBassMapping(glucose),
159 new TestFloorMapping(glucose),
160 new TestSpeakerMapping(glucose),
161 new TestPerformancePattern(glucose),
162 // new TestHuePattern(glucose),
163 // new TestXPattern(glucose),
164 // new TestYPattern(glucose),
165 // new TestZPattern(glucose),
166
167 };
168 }
169
170 public LXTransition[] transitions(GLucose glucose) {
171 return new LXTransition[] {
172 new DissolveTransition(lx),
173 new AddTransition(glucose),
174 new MultiplyTransition(glucose),
175 new OverlayTransition(glucose),
176 new DodgeTransition(glucose),
177 new SwipeTransition(glucose),
178 new FadeTransition(lx),
179 // new SubtractTransition(glucose), // similar to multiply - dh
180 // new BurnTransition(glucose), // similar to multiply - dh
181 // new ScreenTransition(glucose), // same as add -dh
182 // new SoftLightTransition(glucose), // same as overlay -dh
183 };
184 }
185
186 // Handles to globally triggerable effects
187 class Effects {
188 FlashEffect flash = new FlashEffect(lx);
189 BoomEffect boom = new BoomEffect(glucose);
190 BlurEffect blur = new BlurEffect(glucose);
191 QuantizeEffect quantize = new QuantizeEffect(glucose);
192 ColorFuckerEffect colorFucker = new ColorFuckerEffect(glucose);
193
194 Effects() {
195 blur.enable();
196 quantize.enable();
197 colorFucker.enable();
198 }
199 }
200
201 class SineSphere extends SCPattern {
202 private SinLFO yrot = new SinLFO(0, TWO_PI, 2000);
203 public final Projection sinespin;
204 float modelrad = sqrt((model.xMax)*(model.xMax) + (model.yMax)*(model.yMax) + (model.zMax)*(model.zMax));
205 Pick Sshape;
206
207 class Sphery {
208 float f1xcenter, f1ycenter, f1zcenter, f2xcenter , f2ycenter, f2zcenter; //second three are for an ellipse with two foci
209 private SinLFO vibration;
210 private SinLFO surface;
211 private SinLFO vx;
212 private SinLFO xbounce;
213 public SinLFO ybounce;
214 private SinLFO zbounce;
215 float vibration_min, vibration_max, vperiod;
216 public BasicParameter widthparameter;
217 public BasicParameter huespread;
218 public BasicParameter bouncerate;
219 public BasicParameter bounceamp;
220
221
222
223 public Sphery(float f1xcenter, float f1ycenter, float f1zcenter, float vibration_min, float vibration_max, float vperiod)
224 {
225 this.f1xcenter = f1xcenter;
226 this.f1ycenter = f1ycenter;
227 this.f1zcenter = f1zcenter;
228 this.vibration_min = vibration_min;
229 this.vibration_max = vibration_max;
230 this.vperiod = vperiod;
231 addParameter(bounceamp = new BasicParameter("Amp", .5f));
232 addParameter(bouncerate = new BasicParameter("Rate", .5f)); //ybounce.modulateDurationBy(bouncerate);
233 addParameter(widthparameter = new BasicParameter("Width", .1f));
234 addParameter(huespread = new BasicParameter("Hue", .2f));
235
236 addModulator( vx = new SinLFO(-4000, 10000, 100000)).trigger() ;
237 //addModulator(xbounce = new SinLFO(model.xMax/3, 2*model.yMax/3, 2000)).trigger();
238 addModulator(ybounce= new SinLFO(model.yMax/3, 2*model.yMax/3, 240000.f/lx.tempo.bpm())).trigger(); //ybounce.modulateDurationBy
239
240 //addModulator(bounceamp); //ybounce.setMagnitude(bouncerate);
241 addModulator( vibration = new SinLFO(vibration_min , vibration_max, 240000.f/lx.tempo.bpm())).trigger(); //vibration.modulateDurationBy(vx);
242
243 }
244 public Sphery(float f1xcenter, float f1ycenter, float f1zcenter, float f2xcenter, float f2ycenter, float f2zcenter,
245 float vibration_min, float vibration_max, float vperiod)
246 {
247 this.f1xcenter = f1xcenter;
248 this.f1ycenter = f1ycenter;
249 this.f1zcenter = f1zcenter;
250 this.f2xcenter = f2xcenter;
251 this.f2ycenter = f2ycenter;
252 this.f2zcenter = f2zcenter;
253 this.vibration_min = vibration_min;
254 this.vibration_max = vibration_max;
255 this.vperiod = vperiod;
256 //addModulator(xbounce = new SinLFO(model.xMax/3, 2*model.yMax/3, 2000)).trigger();
257 addModulator(ybounce).trigger();
258 addModulator( vibration = new SinLFO(vibration_min , vibration_max, lx.tempo.rampf())).trigger(); //vibration.modulateDurationBy(vx);
259 addParameter(widthparameter = new BasicParameter("Width", .1f));
260 addParameter(huespread = new BasicParameter("Hue", .2f));
261
262 }
263
264
265
266
267
268 public float distfromcirclecenter(float px, float py, float pz, float f1x, float f1y, float f1z)
269 {
270 return dist(px, py, pz, f1x, f1y, f1z);
271 }
272 //void updatespherey(deltaMs, )
273 public int spheryvalue (float px, float py, float pz , float f1xc, float f1yc, float f1zc)
274 {
275 //switch(sShpape.cur() ) {}
276 return lx.hsb(constrain(huespread.getValuef()*5*px, 0, 360) , dist(px, py, pz, f1xc, f1yc, f1zc) ,
277 max(0, 100 - 100*widthparameter.getValuef()*abs(dist(px, py, pz, f1xcenter, ybounce.getValuef(), f1zcenter)
278 - vibration.getValuef() ) ) );
279 }
280 public int ellipsevalue(float px, float py, float pz , float f1xc, float f1yc, float f1zc, float f2xc, float f2yc, float f2zc)
281 {
282 //switch(sShpape.cur() ) {}
283 return lx.hsb(huespread.getValuef()*5*px, dist(model.xMax-px, model.yMax-py, model.zMax-pz, f1xc, f1yc, f1zc) ,
284 max(0, 100 - 100*widthparameter.getValuef() *
285 abs( (dist(px, py, pz, f1xc, ybounce.getValuef(), f1zc) +
286 (dist(px, py , pz, f2xc, ybounce.getValuef(), f2zc) ) )/2
287 - 1.2f*vibration.getValuef() ) ) ) ;
288 }
289
290 public void run(double deltaMs) {
291 float vv = vibration.getValuef();
292 float ybv = ybounce.getValuef();
293
294 }
295
296 }
297
298
299 final Sphery[] spherys;
300 SineSphere(GLucose glucose)
301 {
302 super(glucose);
303 sinespin = new Projection(model);
304 addModulator(yrot).trigger();
305 //Sshape = addPick("Shape", , 1);
306 spherys = new Sphery[] {
307 new Sphery(model.xMax/4, model.yMax/2, model.zMax/2, modelrad/16, modelrad/8, 3000),
308 new Sphery(.75f*model.xMax, model.yMax/2, model.zMax/2, modelrad/20, modelrad/10, 2000),
309 new Sphery(model.xMax/2, model.yMax/2, model.zMax/2, modelrad/4, modelrad/8, 2300),
310 };
311
312 }
313
314 // public void onParameterChanged(LXParameter parameter)
315 // {
316
317
318 // for (Sphery s : spherys) {
319 // if (s == null) continue;
320 // double bampv = s.bounceamp.getValue();
321 // double brv = s.bouncerate.getValue();
322 // double tempobounce = lx.tempo.bpm();
323 // if (parameter == s.bounceamp)
324 // {
325 // s.ybounce.setRange(bampv*model.yMax/3 , bampv*2*model.yMax/3, brv);
326 // }
327 // else if ( parameter == s.bouncerate )
328 // {
329 // s.ybounce.setDuration(120000./tempobounce);
330 // }
331 // }
332 // }
333
334 public void run( double deltaMs) {
335 float t = lx.tempo.rampf();
336 float bpm = lx.tempo.bpmf();
337 //spherys[1].run(deltaMs);
338 //spherys[2].run(deltaMs);
339 //spherys[3].run(deltaMs);]
340 sinespin.reset(model)
341
342 // Translate so the center of the car is the origin, offset by yPos
343 .translateCenter(model, 0, 0, 0)
344
345 // Rotate around the origin (now the center of the car) about an X-vector
346 .rotate(yrot.getValuef(), 0, 1, 0);
347
348
349
350 for (Point p: model.points){
351 int c = 0;
352 c = blendColor(c, spherys[1].spheryvalue(p.x, p.y, p.z, .75f*model.xMax, model.yMax/2, model.zMax/2), ADD);
353 c = blendColor(c, spherys[0].spheryvalue(p.x, p.y, p.z, model.xMax/4, model.yMax/4, model.zMax/2), ADD);
354 c = blendColor(c, spherys[2].spheryvalue(p.x, p.y, p.z, model.xMax/2, model.yMax/2, model.zMax/2),ADD);
355
356 colors[p.index] = lx.hsb(lx.h(c), lx.s(c), lx.b(c));
357
358 }
359
360
361
362 }
363 int spheremode = 0;
364
365 // void keyPressed() {
366 // spheremode++;
367 // }
368
369 // color CalcPoint(PVector Px)
370 // {
371 // // if (spheremode == 0 )
372 //{
373
374 //}
375 // else if (spheremode == 1)
376 // {
377
378 // color c = 0;
379 // 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);
380 // return c;
381 // }
382 // return lx.hsb(0,0,0);
383 // // else if(spheremode ==2)
384 // { color c = 0;
385 // return lx.hsb(CalcCone( (xyz by = new xyz(0,spherys[2].ybounce.getValuef(),0) ), Px, mid) );
386
387 // }
388
389
390 // }
391
392 }
393
394 class CubeCurl extends SCPattern{
395 float CH, CW, diag;
396 ArrayList<PVector> cubeorigin = new ArrayList<PVector>();
397 ArrayList<PVector> centerlist = new ArrayList<PVector>();
398 private SinLFO curl = new SinLFO(0, Cube.EDGE_HEIGHT, 5000 );
399
400 private SinLFO bg = new SinLFO(180, 220, 3000);
401
402 CubeCurl(GLucose glucose){
403 super(glucose);
404 addModulator(curl).trigger();
405 addModulator(bg).trigger();
406 this.CH = Cube.EDGE_HEIGHT;
407 this.CW = Cube.EDGE_WIDTH;
408 this.diag = sqrt(CW*CW + CW*CW);
409
410
411 ArrayList<PVector> centerlistrelative = new ArrayList<PVector>();
412 for (int i = 0; i < model.cubes.size(); i++){
413 Cube a = model.cubes.get(i);
414 cubeorigin.add(new PVector(a.x, a.y, a.z));
415 centerlist.add(centerofcube(i));
416
417 }
418
419 }
420 //there is definitely a better way of doing this!
421 public PVector centerofcube(int i) {
422 Cube c = model.cubes.get(i);
423
424 println(" cube #: " + i + " c.x " + c.x + " c.y " + c.y + " c.z " + c.z );
425 PVector cubeangle = new PVector(c.rx, c.ry, c.rz);
426 //println("raw x" + cubeangle.x + "raw y" + cubeangle.y + "raw z" + cubeangle.z);
427 PVector cubecenter = new PVector(c.x + CW/2, c.y + CH/2, c.z + CW/2);
428 println("cubecenter unrotated: " + cubecenter.x + " " +cubecenter.y + " " +cubecenter.z );
429 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);
430 // nCos*(y-o.y) - nSin*(z-o.z) + o.y
431 cubecenter = PVector.add(cubecenter, centerrot);
432 println( " cubecenter.x " + cubecenter.x + " cubecenter.y " + cubecenter.y + " cubecenter.z " + cubecenter.z + " ");
433
434
435 return cubecenter;
436 }
437
438
439 public void run(double deltaMs){
440 for (int i =0; i < model.cubes.size(); i++) {
441 Cube c = model.cubes.get(i);
442 float cfloor = c.y;
443
444 // if (i%3 == 0){
445
446 // for (Point p : c.points ){
447 // // colors[p.index]=color(0,0,0);
448 // //float dif = (p.y - c.y);
449 // //colors[p.index] = color( bg.getValuef() , 80 , dif < curl.getValuef() ? 80 : 0, ADD);
450 // }
451 // }
452
453 // else if (i%3 == 1) {
454
455 // for (Point p: c.points){
456 // colors[p.index]=color(0,0,0);
457 // float dif = (p.y - c.y);
458 // // colors[p.index] =
459 // // color(bg.getValuef(),
460 // // map(curl.getValuef(), 0, Cube.EDGE_HEIGHT, 20, 100),
461 // // 100 - 10*abs(dif - curl.getValuef()), ADD );
462 // }
463 // }
464 // else if (i%3 == 2){
465 // centerlist[i].sub(cubeorigin(i);
466 for (Point p: c.points) {
467 PVector pv = new PVector(p.x, p.y, p.z);
468 colors[p.index] =color( constrain(4* pv.dist(centerlist.get(i)), 0, 360) , 50, 100 );
469 // colors[p.index] =color(constrain(centerlist[i].x, 0, 360), constrain(centerlist[i].y, 0, 100), );
470
471
472 }
473
474
475 //}
476
477 }
478 }
479 }
480
481 class HueTestHSB extends SCPattern{
482 BasicParameter HueT = new BasicParameter("Hue", .5f);
483 BasicParameter SatT = new BasicParameter("Sat", .5f);
484 BasicParameter BriT = new BasicParameter("Bright", .5f);
485
486 HueTestHSB(GLucose glucose) {
487 super(glucose);
488 addParameter(HueT);
489 addParameter(SatT);
490 addParameter(BriT);
491 }
492 public void run(double deltaMs){
493
494 for (Point p : model.points) {
495 int c = 0;
496 c = blendColor(c, lx.hsb(360*HueT.getValuef(), 100*SatT.getValuef(), 100*BriT.getValuef()), ADD);
497 colors[p.index]= c;
498 }
499 int now= millis();
500 if (now % 1000 <= 20)
501 {
502 println("Hue: " + 360*HueT.getValuef() + "Sat: " + 100*SatT.getValuef() + "Bright: " + 100*BriT.getValuef());
503 }
504 }
505
506 }
507
508 class TelevisionStatic extends SCPattern {
509 BasicParameter brightParameter = new BasicParameter("BRIGHT", 1.0f);
510 BasicParameter saturationParameter = new BasicParameter("SAT", 1.0f);
511 BasicParameter hueParameter = new BasicParameter("HUE", 1.0f);
512 SinLFO direction = new SinLFO(0, 10, 3000);
513
514 public TelevisionStatic(GLucose glucose) {
515 super(glucose);
516 addModulator(direction).trigger();
517 addParameter(brightParameter);
518 addParameter(saturationParameter);
519 addParameter(hueParameter);
520 }
521
522 public void run(double deltaMs) {
523 boolean d = direction.getValuef() > 5.0f;
524 for (Point p : model.points) {
525 colors[p.index] = lx.hsb((lx.getBaseHuef() + random(hueParameter.getValuef() * 360))%360, random(saturationParameter.getValuef() * 100), random(brightParameter.getValuef() * 100));
526 }
527 }
528 }
529
530 class AbstractPainting extends SCPattern {
531
532 PImage img;
533
534 SinLFO colorMod = new SinLFO(0, 360, 5000);
535 SinLFO brightMod = new SinLFO(0, model.zMax, 2000);
536
537 public AbstractPainting(GLucose glucose) {
538 super(glucose);
539 addModulator(colorMod).trigger();
540 addModulator(brightMod).trigger();
541
542 img = loadImage("abstract.jpg");
543 img.loadPixels();
544 }
545
546 public void run(double deltaMs) {
547 for (Point p : model.points) {
548 int c = img.get((int)((p.x / model.xMax) * img.width), img.height - (int)((p.y / model.yMax) * img.height));
549 colors[p.index] = lx.hsb(hue(c) + colorMod.getValuef()%360, saturation(c), brightness(c) - ((p.z - brightMod.getValuef())/p.z));
550 }
551 }
552 }
553
554 class Spirality extends SCPattern {
555 final BasicParameter r = new BasicParameter("RADIUS", 0.5f);
556
557 float angle = 0;
558 float rad = 0;
559 int direction = 1;
560
561 Spirality(GLucose glucose) {
562 super(glucose);
563 addParameter(r);
564 for (Point p : model.points) {
565 colors[p.index] = lx.hsb(0, 0, 0);
566 }
567 }
568
569 public void run(double deltaMs) {
570 angle += deltaMs * 0.007f;
571 rad += deltaMs * .025f * direction;
572 float x = model.xMax / 2 + cos(angle) * rad;
573 float y = model.yMax / 2 + sin(angle) * rad;
574 for (Point p : model.points) {
575 float b = dist(x,y,p.x,p.y);
576 if (b < 90) {
577 colors[p.index] = blendColor(
578 colors[p.index],
579 lx.hsb(lx.getBaseHuef() + 25, 10, map(b, 0, 10, 100, 0)),
580 ADD);
581 } else {
582 colors[p.index] = blendColor(
583 colors[p.index],
584 lx.hsb(25, 10, map(b, 0, 10, 0, 15)),
585 SUBTRACT);
586 }
587 }
588 if (rad > model.xMax / 2 || rad <= .001f) {
589 direction *= -1;
590 }
591 }
592 }
593
594
595
596
597 /**
598 * This is a reusable equalizer class that lets you get averaged
599 * bands with dB scaling and smoothing.
600 */
601 public static class GraphicEQ {
602
603 private final LX lx;
604
605 public final BasicParameter level = new BasicParameter("LVL", 0.5f);
606 public final BasicParameter range = new BasicParameter("RNGE", 0.5f);
607 public final BasicParameter slope = new BasicParameter("SLOP", 0.5f);
608 public final BasicParameter attack = new BasicParameter("ATK", 0.5f);
609 public final BasicParameter release = new BasicParameter("REL", 0.5f);
610
611 private final FFT fft;
612 private final int numBands;
613
614 private final LinearEnvelope[] bandVals;
615
616 public final static int DEFAULT_NUM_BANDS = 16;
617
618 public GraphicEQ(LX lx) {
619 this(lx, DEFAULT_NUM_BANDS);
620 }
621
622 /**
623 * Note that the number of bands is a suggestion. Due to the FFT implementation
624 * the actual number may be slightly different.
625 */
626 public GraphicEQ(LX lx, int num) {
627 this.lx = lx;
628 fft = new FFT(lx.audioInput().bufferSize(), lx.audioInput().sampleRate());
629 fft.window(FFT.HAMMING);
630 fft.logAverages(50, num/8);
631 numBands = this.fft.avgSize();
632 bandVals = new LinearEnvelope[numBands];
633 for (int i = 0; i < bandVals.length; ++i) {
634 (bandVals[i] = new LinearEnvelope(0, 0, 500)).trigger();
635 }
636 }
637
638 static final float logTen = log(10);
639 public static float log10(float val) {
640 return log(val) / logTen;
641 }
642
643 public float getLevel(int band) {
644 return bandVals[band].getValuef();
645 }
646
647 public float getAverageLevel(int minBand, int numBands) {
648 float avg = 0;
649 for (int i = minBand; i < minBand + numBands; ++i) {
650 avg += bandVals[i].getValuef();
651 }
652 avg /= numBands;
653 return avg;
654 }
655
656 public void run(double deltaMs) {
657 fft.forward(lx.audioInput().mix);
658 float zeroDBReference = pow(10, 100*(1-level.getValuef())/20.f);
659 float decibelRange = 12 + range.getValuef() * 60;
660 float decibelSlope = slope.getValuef() * 60.f / numBands;
661 for (int i = 0; i < numBands; ++i) {
662 float raw = fft.getAvg(i);
663 float decibels = 20*log10(raw / zeroDBReference);
664 float positiveDecibels = decibels + decibelRange;
665 positiveDecibels += i*decibelSlope;
666 float value = constrain(positiveDecibels / decibelRange, 0, 1);
667
668 if (value > bandVals[i].getValuef()) {
669 bandVals[i].setRangeFromHereTo(value, attack.getValuef() * 20).trigger();
670 }
671 }
672 for (LinearEnvelope band : bandVals) {
673 band.run(deltaMs);
674 if (!band.isRunning() && band.getValuef() > 0) {
675 band.setRangeFromHereTo(0, release.getValuef() * 1600).trigger();
676 }
677 }
678 }
679 }
680
681
682 class TowerParams extends SCPattern
683 {
684 BasicParameter hueoff = new BasicParameter("Hueoff", 0.0f);
685 BasicParameter hueSpan = new BasicParameter("HueRange", 0.0f);
686 BasicParameter t1 = new BasicParameter("T1", 0.0f);
687 BasicParameter t2 = new BasicParameter("T2", 0.0f);
688 BasicParameter t3 = new BasicParameter("T3", 0.0f);
689 BasicParameter t4 = new BasicParameter("T4", 0.0f);
690 BasicParameter t5 = new BasicParameter("T5", 0.0f);
691 BasicParameter t6 = new BasicParameter("T6", 0.0f);
692 BasicParameter t7 = new BasicParameter("T7", 0.0f);
693 BasicParameter t8 = new BasicParameter("T8", 0.0f);
694 BasicParameter t9 = new BasicParameter("T9", 0.0f);
695 BasicParameter t10 = new BasicParameter("T10", 0.0f);
696 BasicParameter t11 = new BasicParameter("T11", 0.0f);
697 BasicParameter t12 = new BasicParameter("T12", 0.0f);
698 BasicParameter t13 = new BasicParameter("T13", 0.0f);
699 BasicParameter t14 = new BasicParameter("T14", 0.0f);
700 BasicParameter t15 = new BasicParameter("T15", 0.0f);
701 BasicParameter t16 = new BasicParameter("T16", 0.0f);
702
703 ArrayList<BasicParameter> towerParams;
704 int towerSize;
705 int colorSpan;
706 TowerParams(GLucose glucose) {
707 super(glucose);
708
709 towerParams = new ArrayList<BasicParameter>();
710 addParameter(hueoff);
711 addParameter(hueSpan);
712 towerParams.add(t1);
713 towerParams.add(t2);
714 towerParams.add(t3);
715 towerParams.add(t4);
716 towerParams.add(t5);
717 towerParams.add(t6);
718 towerParams.add(t7);
719 towerParams.add(t8);
720 towerParams.add(t9);
721 towerParams.add(t10);
722 towerParams.add(t11);
723 towerParams.add(t12);
724 towerParams.add(t13);
725 towerParams.add(t14);
726 towerParams.add(t15);
727 towerParams.add(t16);
728 for(BasicParameter p : towerParams)
729 {
730 addParameter(p);
731 }
732 towerSize = model.towers.size();
733 colorSpan = 255 / towerSize;
734 }
735
736 public void run(double deltaMs)
737 {
738 clearALL();
739 Tower t;
740 for(int i=0; i<towerSize ;i++)
741 {
742 t= model.towers.get(i);
743 for(Point p : t.points)
744 {
745 if(p.y<towerParams.get(i).getValuef()*200)
746 {
747 colors[p.index]=lx.hsb(255 * hueoff.getValuef()+colorSpan * hueSpan.getValuef() * i, 255, 255);
748 }
749 }
750 }
751
752 }
753
754 public void clearALL()
755 {
756 for(Point p : model.points)
757 {
758 colors[p.index] = 0;
759 }
760 }
761
762 }
763 class Sandbox extends SCPattern
764 {
765 int c=0;
766 int prevC=0;
767 int huerange=255;
768 int pointrange= model.points.size();
769 int striprange= model.strips.size();
770 int facerange= model.faces.size();
771 int cuberange = model.cubes.size();
772 int towerrange = model.towers.size();
773 int counter=0;
774
775 Sandbox(GLucose glucose) {
776 super(glucose);
777 println("points "+pointrange);
778 println("strips "+striprange);
779 println("faces "+facerange);
780 println("cubes "+cuberange);
781 println("towers "+towerrange);
782 }
783
784 public void run(double deltaMs) {
785
786
787 if(counter % 10 ==0)
788 {
789 doDraw(c,0);
790 c = (c + 1) % towerrange;
791 long col = lx.hsb(Math.round(Math.random()*255),255,255) ;
792 doDraw(c,col);
793 }
794 counter++;
795
796 }
797
798 public void doDraw(int c,long col)
799 {
800 Tower t= model.towers.get((int) c);
801 for(Point p : t.points)
802 {
803 colors[p.index] = (int) col;
804 }
805 }
806 };
807
808 class GranimTestPattern extends GranimPattern
809 {
810 GranimTestPattern(GLucose glucose)
811 {
812 super(glucose);
813 addGraphic("myReds",new RedsGraphic(100));
814 int[] dots = {0,128,0,128,0,128,0,128,0,128,0,128};
815 addGraphic("myOtherColors",new ColorDotsGraphic(dots));
816
817 getGraphicByName("myOtherColors").position=100;
818 }
819 int counter=0;
820 public void run(double deltaMs)
821 {
822 clearALL();
823 super.run(deltaMs);
824
825 if(counter % 3 ==0)
826 {
827 Graphic reds = getGraphicByName("myReds");
828 Graphic others = getGraphicByName("myOtherColors");
829 reds.position = reds.position + 1 % 19000;
830 others.position = others.position + 10 % 19000;
831 }
832 }
833 public void clearALL()
834 {
835 for(int i = 0; i < colors.length; i++)
836 {
837 colors[i] = 0;
838 }
839 }
840
841
842 }
843
844 class GranimTestPattern2 extends GranimPattern
845 {
846 GranimTestPattern2(GLucose glucose)
847 {
848 super(glucose);
849 /*for(int i = 0;i < 100; i++)
850 {
851 Graphic g = addGraphic("myReds_"+i,new RedsGraphic(Math.round(Math.random() * 100)));
852
853 }*/
854 Graphic g = addGraphic("myRandoms",new RandomsGranim(50));
855 g.position = 200;
856
857 }
858 int counter=0;
859 float count=0;
860 public void run(double deltaMs)
861 {
862 clearALL();
863 super.run(deltaMs);
864 Graphic randomsGraphic = getGraphicByName("myRandoms");
865 randomsGraphic.position = Math.round(sin(count)*1000)+5000;
866 count+= 0.005f;
867 }
868 public void clearALL()
869 {
870 for(Point p : model.points)
871 {
872 colors[p.index] = 0;
873 }
874 }
875
876
877 };
878
879 class DriveableCrossSections extends CrossSections
880 {
881 BasicParameter xd;
882 BasicParameter yd;
883 BasicParameter zd;
884 BasicParameter mode;
885
886 DriveableCrossSections(GLucose glucose) {
887 super(glucose);
888 }
889
890 public void addParams()
891 {
892 mode = new BasicParameter("Mode", 0.0f);
893 xd = new BasicParameter("XD", 0.0f);
894 yd = new BasicParameter("YD", 0.0f);
895 zd = new BasicParameter("ZD", 0.0f);
896 addParameter(mode);
897 addParameter(xd);
898 addParameter(yd);
899 addParameter(zd);
900
901 super.addParams();
902 }
903
904 public void onParameterChanged(LXParameter p) {
905 if(p == mode)
906 {
907 if(interactive())
908 {
909 copyValuesToKnobs();
910 }else{
911 copyKnobsToValues();
912 }
913 }
914 }
915
916 public void copyValuesToKnobs()
917 {
918 xd.setValue(x.getValue()/200);
919 yd.setValue(y.getValue()/115);
920 zd.setValue(z.getValue()/100);
921 }
922
923 public void copyKnobsToValues()
924 {
925 x.setValue(xd.getValue()*200);
926 y.setValue(yd.getValue()*115);
927 z.setValue(zd.getValue()*100);
928 }
929
930 public boolean interactive()
931 {
932 return Math.round(mode.getValuef())>0.5f;
933 }
934
935 public void updateXYZVals()
936 {
937 if(interactive())
938 {
939 xv = xd.getValuef()*200;
940 yv = yd.getValuef()*115;
941 zv = zd.getValuef()*100;
942 }else{
943 super.updateXYZVals();
944 copyValuesToKnobs();
945 }
946 }
947
948 }
949 //----------------------------------------------------------------------------------------------------------------------------------
950 public class Pong extends DPat {
951 SinLFO x,y,z,dx,dy,dz;
952 float cRad; BasicParameter pSize;
953 Pick pChoose;
954 PVector v = new PVector(), vMir = new PVector();
955
956 Pong(GLucose glucose) {
957 super(glucose);
958 cRad = mMax.x/10;
959 addModulator(dx = new SinLFO(6000, 500, 30000 )).trigger();
960 addModulator(dy = new SinLFO(3000, 500, 22472 )).trigger();
961 addModulator(dz = new SinLFO(1000, 500, 18420 )).trigger();
962 addModulator(x = new SinLFO(cRad, mMax.x - cRad, 0)).trigger(); x.modulateDurationBy(dx);
963 addModulator(y = new SinLFO(cRad, mMax.y - cRad, 0)).trigger(); y.modulateDurationBy(dy);
964 addModulator(z = new SinLFO(cRad, mMax.z - cRad, 0)).trigger(); z.modulateDurationBy(dz);
965 pSize = addParam ("Size" , 0.4f );
966 pChoose = addPick ("Animiation" , 2, 2, new String[] {"Pong", "Ball", "Cone"} );
967 }
968
969 public void StartRun(double deltaMs) { cRad = mMax.x*val(pSize)/6; }
970 public int CalcPoint(PVector p) {
971 v.set(x.getValuef(), y.getValuef(), z.getValuef());
972 v.z=0;p.z=0;// ignore z dimension
973 switch(pChoose.Cur()) {
974 case 0: vMir.set(mMax); vMir.sub(p);
975 return lx.hsb(lxh(),100,c1c(1 - min(v.dist(p), v.dist(vMir))*.5f/cRad)); // balls
976 case 1: return lx.hsb(lxh(),100,c1c(1 - v.dist(p)*.5f/cRad)); // ball
977 case 2: vMir.set(mMax.x/2,0,mMax.z/2);
978 return lx.hsb(lxh(),100,c1c(1 - calcCone(p,v,vMir) * max(.02f,.45f-val(pSize)))); // spot
979 }
980 return lx.hsb(0,0,0);
981 }
982 }
983 //----------------------------------------------------------------------------------------------------------------------------------
984 public class NDat {
985 float xz, yz, zz, hue, speed, angle, den;
986 float xoff,yoff,zoff;
987 float sinAngle, cosAngle;
988 boolean isActive;
989 NDat () { isActive=false; }
990 public boolean Active() { return isActive; }
991 public void set (float _hue, float _xz, float _yz, float _zz, float _den, float _speed, float _angle) {
992 isActive = true;
993 hue=_hue; xz=_xz; yz=_yz; zz =_zz; den=_den; speed=_speed; angle=_angle;
994 xoff = random(100e3f); yoff = random(100e3f); zoff = random(100e3f);
995 }
996 }
997
998 public class Noise extends DPat
999 {
1000 int CurAnim, iSymm;
1001 int XSym=1,YSym=2,RadSym=3;
1002 float zTime , zTheta=0, zSin, zCos, rtime, ttime;
1003 BasicParameter pSpeed , pDensity, pSharp;
1004 Pick pChoose, pSymm;
1005 int _ND = 4;
1006 NDat N[] = new NDat[_ND];
1007
1008 Noise(GLucose glucose) {
1009 super(glucose);
1010 pSpeed = addParam("Fast" , .55f);
1011 pDensity = addParam("Dens" , .5f);
1012 pSharp = addParam("Shrp" , 0);
1013 pSymm = addPick("Symmetry" , 0, 3, new String[] {"None", "X", "Y", "Radial"} );
1014 pChoose = addPick("Animation", 6, 7, new String[] {"Drip", "Cloud", "Rain", "Fire", "Machine", "Spark","VWave", "Wave"} );
1015 for (int i=0; i<_ND; i++) N[i] = new NDat();
1016 }
1017
1018 public void onActive() { zTime = random(500); zTheta=0; rtime = 0; ttime = 0; }
1019
1020 public void StartRun(double deltaMs) {
1021 zTime += deltaMs*(val(pSpeed)-.5f)*.002f ;
1022 zTheta += deltaMs*(spin()-.5f)*.01f ;
1023 rtime += deltaMs;
1024 iSymm = pSymm.Cur();
1025 zSin = sin(zTheta);
1026 zCos = cos(zTheta);
1027
1028 if (pChoose.Cur() != CurAnim) {
1029 CurAnim = pChoose.Cur(); ttime = rtime;
1030 pSpin .reset(); zTheta = 0;
1031 pDensity .reset(); pSpeed .reset();
1032 for (int i=0; i<_ND; i++) { N[i].isActive = false; }
1033
1034 switch(CurAnim) {
1035 // hue xz yz zz den mph angle
1036 case 0: N[0].set(0 ,75 ,75 ,150,45 ,3 ,0 ); pSharp.setValue(1 ); break; // drip
1037 case 1: N[0].set(0 ,100,100,200,45 ,3 ,180); pSharp.setValue(0 ); break; // clouds
1038 case 2: N[0].set(0 ,2 ,400,2 ,20 ,3 ,0 ); pSharp.setValue(.5f); break; // rain
1039 case 3: N[0].set(40 ,100,100,200,10 ,1 ,180);
1040 N[1].set(0 ,100,100,200,10 ,5 ,180); pSharp.setValue(0 ); break; // fire 1
1041 case 4: N[0].set(0 ,40 ,40 ,40 ,15 ,2.5f,180);
1042 N[1].set(20 ,40 ,40 ,40 ,15 ,4 ,0 );
1043 N[2].set(40 ,40 ,40 ,40 ,15 ,2 ,90 );
1044 N[3].set(60 ,40 ,40 ,40 ,15 ,3 ,-90); pSharp.setValue(.5f); break; // machine
1045 case 5: N[0].set(0 ,400,100,2 ,15 ,3 ,90 );
1046 N[1].set(20 ,400,100,2 ,15 ,2.5f,0 );
1047 N[2].set(40 ,100,100,2 ,15 ,2 ,180);
1048 N[3].set(60 ,100,100,2 ,15 ,1.5f,270); pSharp.setValue(.5f); break; // spark
1049 }
1050 }
1051
1052 for (int i=0; i<_ND; i++) if (N[i].Active()) {
1053 N[i].sinAngle = sin(radians(N[i].angle));
1054 N[i].cosAngle = cos(radians(N[i].angle));
1055 }
1056 }
1057
1058 public int CalcPoint(PVector p) {
1059 int c = 0;
1060 rotateZ(p, mCtr, zSin, zCos);
1061
1062 if (CurAnim == 6 || CurAnim == 7) {
1063 setNorm(p);
1064 return lx.hsb(lxh(),100, 100 * (
1065 constrain(1-50*(1-val(pDensity))*abs(p.y-sin(zTime*10 + p.x*(300))*.5f - .5f),0,1) +
1066 (CurAnim == 7 ? constrain(1-50*(1-val(pDensity))*abs(p.x-sin(zTime*10 + p.y*(300))*.5f - .5f),0,1) : 0))
1067 );
1068 }
1069
1070 if (iSymm == XSym && p.x > mMax.x/2) p.x = mMax.x-p.x;
1071 if (iSymm == YSym && p.y > mMax.y/2) p.y = mMax.y-p.y;
1072
1073 for (int i=0;i<_ND; i++) if (N[i].Active()) {
1074 NDat n = N[i];
1075 float zx = zTime * n.speed * n.sinAngle,
1076 zy = zTime * n.speed * n.cosAngle;
1077
1078 float b = (iSymm==RadSym ? noise(zTime*n.speed+n.xoff-p.dist(mCtr)/n.xz)
1079 : noise(p.x/n.xz+zx+n.xoff,p.y/n.yz+zy+n.yoff,p.z/n.zz+n.zoff))
1080 *1.8f;
1081
1082 b += n.den/100 -.4f + val(pDensity) -1;
1083 c = blendColor(c,lx.hsb(lxh()+n.hue,100,c1c(b)),ADD);
1084 }
1085 return c;
1086 }
1087 }
1088 //----------------------------------------------------------------------------------------------------------------------------------
1089 public class Play extends DPat
1090 {
1091 public class rAngle {
1092 float prvA, dstA, c;
1093 float prvR, dstR, r;
1094 float _cos, _sin, x, y;
1095 public float fixAngle (float a, float b) { return a<b ?
1096 (abs(a-b) > abs(a+2*PI-b) ? a : a+2*PI) :
1097 (abs(a-b) > abs(a-2*PI-b) ? a : a-2*PI) ; }
1098 public float getX(float r) { return mCtr.x + _cos*r; }
1099 public float getY(float r) { return mCtr.y + _sin*r; }
1100 public void move() { c = interp(t,prvA,dstA);
1101 r = interp(t,prvR,dstR);
1102 _cos = cos(c); _sin = sin(c);
1103 x = getX(r); y = getY(r); }
1104 public void set() { prvA = dstA; dstA = random(2*PI); prvA = fixAngle(prvA, dstA);
1105 prvR = dstR; dstR = random(mCtr.y); }
1106 }
1107
1108 BasicParameter pAmp, pRadius, pBounce;
1109 Pick pTimePattern, pTempoMult, pShape;
1110
1111 ArrayList<rWave> waves = new ArrayList<rWave>(10);
1112
1113 int nBeats = 0;
1114 float t,amp,rad,bnc,zTheta=0;
1115
1116 rAngle a1 = new rAngle(), a2 = new rAngle(),
1117 a3 = new rAngle(), a4 = new rAngle();
1118 PVector cPrev = new PVector(), cRand = new PVector(),
1119 cMid = new PVector(), V = new PVector(),
1120 theta = new PVector(), tSin = new PVector(),
1121 tCos = new PVector(), cMidNorm = new PVector(),
1122 Pn = new PVector();
1123 float LastBeat=3, LastMeasure=3;
1124 int curRandTempo = 1, curRandTPat = 1;
1125
1126 Play(GLucose glucose) {
1127 super(glucose);
1128 pRadius = addParam("Rad" , .1f );
1129 pBounce = addParam("Bnc" , .2f );
1130 pAmp = addParam("Amp" , .2f );
1131 pTempoMult = addPick ("TMult" , 5 , 5 , new String[] {"1x", "2x", "4x", "8x", "16x", "Rand" } );
1132 pTimePattern= addPick ("TPat" , 7 , 7 , new String[] {"Bounce", "Sin", "Roll", "Quant", "Accel", "Deccel", "Slide", "Rand"} );
1133 pShape = addPick ("Shape" , 7 , 15 , new String[] {"Line", "Tap", "V", "RandV",
1134 "Pyramid", "Wings", "W2", "Clock",
1135 "Triangle", "Quad", "Sphere", "Cone",
1136 "Noise", "Wave", "?", "?"} );
1137 }
1138
1139 public class rWave {
1140 float v0, a0, x0, t,damp,a;
1141 boolean bDone=false;
1142 final float len=8;
1143 rWave(float _x0, float _a0, float _v0, float _damp) { x0=_x0*len; a0=_a0; v0=_v0; t=0; damp = _damp; }
1144 public void move(double deltaMs) {
1145 t += deltaMs*.001f;
1146 if (t>4) bDone=true;
1147 }
1148 public float val(float _x) {
1149 _x*=len;
1150 float dist = t*v0 - abs(_x-x0);
1151 if (dist<0) { a=1; return 0; }
1152 a = a0*exp(-dist*damp) * exp(-abs(_x-x0)/(.2f*len)); // * max(0,1-t/dur)
1153 return -a*sin(dist);
1154 }
1155 }
1156
1157 public void onReset() { zTheta=0; super.onReset(); }
1158 public void onActive() {
1159 zTheta=0;
1160 while (lx.tempo.bpm() > 40) lx.tempo.setBpm(lx.tempo.bpm()/2);
1161 }
1162
1163 int KeyPressed = -1;
1164 public boolean noteOn(Note note) {
1165 int row = note.getPitch(), col = note.getChannel();
1166 if (row == 57) {KeyPressed = col; return true; }
1167 return super.noteOn(note);
1168 }
1169
1170 public void StartRun(double deltaMs) {
1171 t = lx.tempo.rampf();
1172 amp = pAmp .getValuef();
1173 rad = pRadius .getValuef();
1174 bnc = pBounce .getValuef();
1175 zTheta += deltaMs*(val(pSpin)-.5f)*.01f;
1176
1177 theta .set(val(pRotX)*PI*2, val(pRotY)*PI*2, val(pRotZ)*PI*2 + zTheta);
1178 tSin .set(sin(theta.x), sin(theta.y), sin(theta.z));
1179 tCos .set(cos(theta.x), cos(theta.y), cos(theta.z));
1180
1181 if (t<LastMeasure) {
1182 if (random(3) < 1) { curRandTempo = PApplet.parseInt(random(4)); if (curRandTempo == 3) curRandTempo = PApplet.parseInt(random(4)); }
1183 if (random(3) < 1) { curRandTPat = pShape.Cur() > 6 ? 2+PApplet.parseInt(random(5)) : PApplet.parseInt(random(7)); }
1184 } LastMeasure = t;
1185
1186 int nTempo = pTempoMult .Cur(); if (nTempo == 5) nTempo = curRandTempo;
1187 int nTPat = pTimePattern.Cur(); if (nTPat == 7) nTPat = curRandTPat ;
1188
1189 switch (nTempo) {
1190 case 0: t = t; break;
1191 case 1: t = (t*2.f )%1.f; break;
1192 case 2: t = (t*4.f )%1.f; break;
1193 case 3: t = (t*8.f )%1.f; break;
1194 case 4: t = (t*16.f)%1.f; break;
1195 }
1196
1197 int i=0; while (i< waves.size()) {
1198 rWave w = waves.get(i);
1199 w.move(deltaMs); if (w.bDone) waves.remove(i); else i++;
1200 }
1201
1202 if ((t<LastBeat && pShape.Cur()!=14) || KeyPressed>-1) {
1203 waves.add(new rWave(
1204 KeyPressed>-1 ? map(KeyPressed,0,7,0,1) : random(1), // location
1205 bnc*10, // bounciness
1206 7, // velocity
1207 2*(1-amp))); // dampiness
1208 KeyPressed=-1;
1209 if (waves.size() > 5) waves.remove(0);
1210 }
1211
1212 if (t<LastBeat) {
1213 cPrev.set(cRand); setRand(cRand);
1214 a1.set(); a2.set(); a3.set(); a4.set();
1215 } LastBeat = t;
1216
1217 switch (nTPat) {
1218 case 0: t = sin(PI*t); break; // bounce
1219 case 1: t = norm(sin(2*PI*(t+PI/2)),-1,1); break; // sin
1220 case 2: t = t; break; // roll
1221 case 3: t = constrain(PApplet.parseInt(t*8)/7.f,0,1); break; // quant
1222 case 4: t = t*t*t; break; // accel
1223 case 5: t = sin(PI*t*.5f); break; // deccel
1224 case 6: t = .5f*(1-cos(PI*t)); break; // slide
1225 }
1226
1227 cMid.set (cPrev); interpolate(t,cMid,cRand);
1228 cMidNorm.set (cMid); setNorm(cMidNorm);
1229 a1.move(); a2.move(); a3.move(); a4.move();
1230 }
1231
1232 public int CalcPoint(PVector Px) {
1233 if (theta.x != 0) rotateX(Px, mCtr, tSin.x, tCos.x);
1234 if (theta.y != 0) rotateY(Px, mCtr, tSin.y, tCos.y);
1235 if (theta.z != 0) rotateZ(Px, mCtr, tSin.z, tCos.z);
1236
1237 Pn.set(Px); setNorm(Pn);
1238
1239 float mp = min(Pn.x, Pn.z);
1240 float yt = map(t,0,1,.5f-bnc/2,.5f+bnc/2);
1241 float r,d;
1242
1243 switch (pShape.Cur()) {
1244 case 0: V.set(Pn.x, yt , Pn.z); break; // bouncing line
1245 case 1: V.set(Pn.x, map(cos(PI*t * Pn.x),-1,1,0,1) , Pn.z); break; // top tap
1246 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
1247 case 3: V.set(Pn.x, Pn.x < cMidNorm.x ? map(Pn.x,0,cMidNorm.x, .5f,yt) :
1248 map(Pn.x,cMidNorm.x,1, yt,.5f), Pn.z); break; // Random V shape
1249
1250 case 4: V.set(Pn.x, .5f*(Pn.x < cMidNorm.x ? map(Pn.x,0,cMidNorm.x, .5f,yt) :
1251 map(Pn.x,cMidNorm.x,1, yt,.5f)) +
1252 .5f*(Pn.z < cMidNorm.z ? map(Pn.z,0,cMidNorm.z, .5f,yt) :
1253 map(Pn.z,cMidNorm.z,1, yt,.5f)), Pn.z); break; // Random Pyramid shape
1254
1255 case 5: V.set(Pn.x, bnc*map((Pn.x-.5f)*(Pn.x-.5f),0,.25f,0,t-.5f)+.5f, Pn.z); break; // wings
1256 case 6: V.set(Pn.x, bnc*map((mp -.5f)*(mp -.5f),0,.25f,0,t-.5f)+.5f, Pn.z); break; // wings
1257
1258 case 7: d = min(
1259 distToSeg(Px.x, Px.y, a1.getX(70),a1.getY(70), mCtr.x, mCtr.y),
1260 distToSeg(Px.x, Px.y, a2.getX(40),a2.getY(40), mCtr.x, mCtr.y));
1261 d = constrain(30*(rad*40-d),0,100);
1262 return lx.hsb(lxh(),100, d); // clock
1263
1264 case 8: r = amp*200 * map(bnc,0,1,1,sin(PI*t));
1265 d = min(
1266 distToSeg(Px.x, Px.y, a1.getX(r),a1.getY(r), a2.getX(r),a2.getY(r)),
1267 distToSeg(Px.x, Px.y, a2.getX(r),a2.getY(r), a3.getX(r),a3.getY(r)),
1268 distToSeg(Px.x, Px.y, a3.getX(r),a3.getY(r), a1.getX(r),a1.getY(r)) // triangle
1269 );
1270 d = constrain(30*(rad*40-d),0,100);
1271 return lx.hsb(lxh(),100, d); // clock
1272
1273 case 9: r = amp*200 * map(bnc,0,1,1,sin(PI*t));
1274 d = min(
1275 distToSeg(Px.x, Px.y, a1.getX(r),a1.getY(r), a2.getX(r),a2.getY(r)),
1276 distToSeg(Px.x, Px.y, a2.getX(r),a2.getY(r), a3.getX(r),a3.getY(r)),
1277 distToSeg(Px.x, Px.y, a3.getX(r),a3.getY(r), a4.getX(r),a4.getY(r)),
1278 distToSeg(Px.x, Px.y, a4.getX(r),a4.getY(r), a1.getX(r),a1.getY(r)) // quad
1279 );
1280 d = constrain(30*(rad*40-d),0,100);
1281 return lx.hsb(lxh(),100, d); // clock
1282
1283 case 10:
1284 r = map(bnc,0,1,a1.r,amp*200*sin(PI*t));
1285 return lx.hsb(lxh(),100,c1c(.9f+2*rad - dist(Px.x,Px.y,a1.getX(r),a1.getY(r))*.03f) ); // sphere
1286
1287 case 11:
1288 Px.z=mCtr.z; cMid.z=mCtr.z;
1289 return lx.hsb(lxh(),100,c1c(1 - calcCone(Px,cMid,mCtr) * 0.02f > .5f?1:0)); // cone
1290
1291 case 12: return lx.hsb(lxh() + noise(Pn.x,Pn.y,Pn.z + (NoiseMove+50000)/1000.f)*200,
1292 85,c1c(Pn.y < noise(Pn.x + NoiseMove/2000.f,Pn.z)*(1+amp)-amp/2.f-.1f ? 1 : 0)); // noise
1293
1294 case 13:
1295 case 14: float y=0; for (rWave w : waves) y += .5f*w.val(Pn.x); // wave
1296 V.set(Pn.x, .7f+y, Pn.z);
1297 break;
1298
1299 default: return lx.hsb(0,0,0);
1300 }
1301
1302 return lx.hsb(lxh(), 100, c1c(1 - V.dist(Pn)/rad));
1303 }
1304 }
1305 //----------------------------------------------------------------------------------------------------------------------------------
1306 boolean dDebug = false;
1307 class dCursor {
1308 dVertex vCur, vNext, vDest;
1309 float destSpeed;
1310 int posStop, pos,posNext; // 0 - 65535
1311 int clr;
1312
1313 dCursor() {}
1314
1315 public boolean isDone () { return pos==posStop; }
1316 public boolean atDest () { return vCur.s==vDest.s ||
1317 xyDist(vCur.getPoint(0), vDest.getPoint(0)) < 12 ||
1318 xyDist(vCur.getPoint(0), vDest.getPoint(15))< 12;}
1319 public void setCur (dVertex _v, int _p) { p2=null; vCur=_v; pos=_p; pickNext(); }
1320 public void setCur (dPixel _p) { setCur(_p.v, _p.pos); }
1321 public void setNext (dVertex _v, int _p, int _s) { vNext = _v; posNext = _p<<12; posStop = _s<<12; }
1322 public void setDest (dVertex _v, float _speed) { vDest = _v; destSpeed = _speed; }
1323 public void onDone () { setCur(vNext, posNext); pickNext(); }
1324
1325 float minDist;
1326 int nTurns;
1327 boolean bRandEval;
1328
1329 public void evaluate(dVertex v, int p, int s) {
1330 if (v == null) return; ++nTurns;
1331 if (bRandEval) {
1332 if (random(nTurns) < 1) setNext(v,p,s); return; }
1333 else {
1334 float d = xyDist(v.getPoint(15), vDest.getPoint(0));
1335 if (d < minDist) { minDist=d; setNext(v,p,s); }
1336 if (d == minDist && random(2)<1) { minDist=d; setNext(v,p,s); }
1337 }
1338 }
1339
1340 public void evalTurn(dTurn t) {
1341 if (t == null || t.pos0<<12 <= pos) return;
1342 evaluate(t.v , t.pos1, t.pos0);
1343 evaluate(t.v.opp, 16-t.pos1, t.pos0);
1344 }
1345
1346 public void pickNext() {
1347 bRandEval = random(.05f+destSpeed) < .05f; minDist=500; nTurns=0;
1348 evaluate(vCur.c0, 0, 16); evaluate(vCur.c1, 0, 16);
1349 evaluate(vCur.c2, 0, 16); evaluate(vCur.c3, 0, 16);
1350 evalTurn(vCur.t0); evalTurn(vCur.t1);
1351 evalTurn(vCur.t2); evalTurn(vCur.t3);
1352 }
1353
1354 Point p1, p2; int i2;
1355
1356 public int draw(int nAmount, SCPattern pat) {
1357 int nFrom = (pos ) >> 12;
1358 int nMv = min(nAmount, posStop-pos);
1359 int nTo = min(15,(pos+nMv) >> 12);
1360 dVertex v = vCur;
1361
1362 if (dDebug) { p1 = v.getPoint(nFrom); float d = (p2 == null ? 0 : pointDist(p1,p2)); if (d>5) { println("too wide! quitting: " + d); exit(); }}
1363 for (int i = nFrom; i <= nTo; i++) { pat.getColors()[v.ci + v.dir*i ] = clr; }
1364 if (v.same != null) for (int i = nFrom; i <= nTo; i++) { pat.getColors()[v.same.ci + v.same.dir*i] = clr; }
1365
1366 if (dDebug) { p2 = v.getPoint(nTo); i2 = nTo; }
1367
1368 pos += nMv; return nAmount - nMv;
1369 }
1370 }
1371
1372 //----------------------------------------------------------------------------------------------------------------------------------
1373 class Worms extends SCPattern {
1374 float StripsPerSec = 10;
1375 float TrailTime = 3000;
1376 int numCursors = 50;
1377 ArrayList<dCursor> cur = new ArrayList<dCursor>(30);
1378
1379 private GraphicEQ eq = null;
1380
1381 private BasicParameter pBeat = new BasicParameter("BEAT", 0);
1382 private BasicParameter pSpeed = new BasicParameter("FAST", .2f);
1383 private BasicParameter pBlur = new BasicParameter("BLUR", .3f);
1384 private BasicParameter pWorms = new BasicParameter("WRMS", .3f);
1385 private BasicParameter pConfusion = new BasicParameter("CONF", .1f);
1386 private BasicParameter pEQ = new BasicParameter("EQ" , 0);
1387 private BasicParameter pSpawn = new BasicParameter("DIR" , 0);
1388 private BasicParameter pColor = new BasicParameter("CLR" , .1f);
1389
1390 float zMidLat = 82.f;
1391 float nConfusion;
1392 private final Click moveChase = new Click(1000);
1393
1394 PVector middle;
1395 public int AnimNum() { return floor(pSpawn.getValuef()*(4-.01f)); }
1396 public float randX() { return random(model.xMax-model.xMin)+model.xMin; }
1397 public float randY() { return random(model.yMax-model.yMin)+model.yMin; }
1398 public PVector randEdge() {
1399 return random(2) < 1 ? new PVector(random(2)<1 ? model.xMin:model.xMax, randY(), zMidLat) :
1400 new PVector(randX(), random(2)<1 ? model.yMin:model.yMax, zMidLat) ;
1401 }
1402
1403 Worms(GLucose glucose) {
1404 super(glucose);
1405 addModulator(moveChase).start();
1406 addParameter(pBeat); addParameter(pSpeed);
1407 addParameter(pBlur); addParameter(pWorms);
1408 addParameter(pEQ); addParameter(pConfusion);
1409 addParameter(pSpawn); addParameter(pColor);
1410
1411 middle = new PVector(1.5f*model.cx, 1.5f*model.cy, 71);
1412 if (lattice == null) lattice = new dLattice();
1413 for (int i=0; i<numCursors; i++) { dCursor c = new dCursor(); reset(c); cur.add(c); }
1414 onParameterChanged(pEQ); setNewDest();
1415 }
1416
1417 public void onParameterChanged(LXParameter parameter) {
1418 super.onParameterChanged(parameter);
1419 nConfusion = 1-pConfusion.getValuef();
1420 for (int i=0; i<numCursors; i++) {
1421 if (parameter==pSpawn) reset(cur.get(i));
1422 cur.get(i).destSpeed = nConfusion;
1423 }
1424 }
1425
1426 public float getClr() { return lx.getBaseHuef() + random(pColor.getValuef()*300); }
1427 public void reset(dCursor c) {
1428 switch(AnimNum()) {
1429 case 0: c.clr = lx.hsb(getClr(),100,100); // middle to edges
1430 c.setDest(lattice.getClosest(randEdge()).v, nConfusion);
1431 c.setCur (lattice.getClosest(middle));
1432 break;
1433
1434 case 1: c.clr = lx.hsb(getClr(),100,100); // top to bottom
1435 float xLin = randX();
1436 c.setDest(lattice.getClosest(new PVector(xLin, 0 , zMidLat)).v, nConfusion);
1437 c.setCur (lattice.getClosest(new PVector(xLin, model.yMax, zMidLat)));
1438 break;
1439
1440 case 2: c.clr = lx.hsb(getClr(),100,100); break; // chase a point around
1441
1442 case 3: boolean bLeft = random(2)<1;
1443 c.clr = lx.hsb(getClr()+random(120),100,100); // sideways
1444 float yLin = randX();
1445 c.setDest(lattice.getClosest(new PVector(bLeft ? 0 : model.xMax,yLin,zMidLat)).v, nConfusion);
1446 c.setCur (lattice.getClosest(new PVector(bLeft ? model.xMax : 0,yLin,zMidLat)));
1447 break;
1448 }
1449 if (pBlur.getValuef() == 1 && random(2)<1) c.clr = lx.hsb(0,0,0);
1450 }
1451
1452 public void setNewDest() {
1453 if (AnimNum() != 2) return;
1454 PVector dest = new PVector(randX(), randY(), zMidLat);
1455 for (int i=0; i<numCursors; i++) {
1456 cur.get(i).setDest(lattice.getClosest(dest).v, nConfusion);
1457 cur.get(i).clr = lx.hsb(getClr()+75,100,100); // chase a point around
1458 }
1459 }
1460
1461 public void run(double deltaMs) {
1462 if (deltaMs > 100) return;
1463 if (moveChase.click()) setNewDest();
1464
1465 float fBass=0, fTreble=0;
1466 if (pEQ.getValuef()>0) { // EQ
1467 eq.run(deltaMs);
1468 fBass = eq.getAverageLevel(0, 4);
1469 fTreble = eq.getAverageLevel(eq.numBands-7, 7);
1470 }
1471
1472 if (pBlur.getValuef() < 1) { // trails
1473 for (int i=0,s=model.points.size(); i<s; i++) {
1474 int c = colors[i]; float b = lx.b(c);
1475 if (b>0) colors[i] = lx.hsb(lx.h(c), lx.s(c), constrain((float)(b-100*deltaMs/(pBlur.getValuef()*TrailTime)),0,100));
1476 }
1477 }
1478
1479 int nWorms = floor(pWorms.getValuef() * numCursors *
1480 map(pEQ.getValuef(),0,1,1,constrain(2*fTreble,0,1)));
1481
1482 for (int i=0; i<nWorms; i++) {
1483 dCursor c = cur.get(i);
1484 int nLeft = floor((float)deltaMs*.001f*StripsPerSec * 65536 * (5*pSpeed.getValuef()));
1485 nLeft *= (1 - lx.tempo.rampf()*pBeat.getValuef());
1486 while(nLeft > 0) {
1487 nLeft = c.draw(nLeft,this); if (!c.isDone()) continue;
1488 c.onDone(); if (c.atDest()) reset(c);
1489 }
1490 }
1491 }
1492
1493
1494 public void onActive() { if (eq == null) {
1495 eq = new GraphicEQ(lx, 16); eq.slope.setValue(0.6f);
1496 eq.level.setValue(0.65f); eq.range.setValue(0.35f);
1497 eq.release.setValue(0.4f);
1498 }}
1499 }
1500 //----------------------------------------------------------------------------------------------------------------------------------
1501 class GenericController {
1502 GenericController(){}
1503 public void RotateKnob(int type, int num, float val){
1504 LXParameter p = null;
1505 if(type==0) {
1506 p = glucose.patternKnobs.get(num);
1507 if(p!=null) { p.setValue(val); }
1508 }
1509 if(type==1) {
1510 p = glucose.transitionKnobs.get(num);
1511 if(p!=null) { p.setValue(val); }
1512 }
1513 if(type==2) {
1514 p = glucose.effectKnobs.get(num);
1515 if(p!=null) { p.setValue(val); }
1516 }
1517 }
1518 }
1519
1520 class MidiController extends GenericController {
1521 MidiController() {
1522 super();
1523 }
1524 }
1525 //PApplet xparent; // be sure to set
1526
1527
1528
1529 OscP5 listener;
1530 // Setup OSC
1531 //listener = new OscP5(this,7022);
1532
1533 //boolean[] noteState = new boolean[16];
1534 //
1535 //void controllerChangeReceived(rwmidi.Controller cc) {
1536 // if (debugMode) {
1537 // println("CC: " + cc.toString());
1538 // }
1539 // if(cc.getCC()==1){
1540 // for(int i=0; i<16; i++){
1541 // if(noteState[i] && i<8) { LXParameter p = glucose.patternKnobs.get(i); p.setValue(cc.getValue()/127.0); }
1542 // else if(noteState[i] && i<12) { LXParameter p = glucose.transitionKnobs.get(i-8); p.setValue(cc.getValue()/127.0); }
1543 // else if(noteState[i] && i<16) { LXParameter p = glucose.effectKnobs.get(i-12); p.setValue(cc.getValue()/127.0); }
1544 // }
1545 // }
1546 //}
1547 //
1548 //void noteOnReceived(Note note) {
1549 // if (debugMode) {
1550 // println("Note On: " + note.toString());
1551 // }
1552 // int pitch = note.getPitch();
1553 // if(pitch>=36 && pitch <36+16){
1554 // noteState[pitch-36]=true;
1555 // }
1556 //}
1557 //
1558 //void noteOffReceived(Note note) {
1559 // if (debugMode) {
1560 // println("Note Off: " + note.toString());
1561 // }
1562 // int pitch = note.getPitch();
1563 // if(pitch>=36 && pitch <36+16){
1564 // noteState[pitch-36]=false;
1565 // }
1566 //}
1567 //
1568 //void oscEvent(OscMessage theOscMessage) {
1569 // println(theOscMessage);
1570 // LXPattern currentPattern = lx.getPattern();
1571 // if (currentPattern instanceof OSCPattern) {
1572 // ((OSCPattern)currentPattern).oscEvent(theOscMessage);
1573 // }
1574 //}
1575 //
1576
1577
1578 class ObjectMuckerEffect extends SCEffect {
1579 ObjectMuckerEffect(GLucose glucose) {
1580 super(glucose);
1581 }
1582 public void apply(int[] colors){
1583 /*for(Strip s: model.strips){
1584 for(int i=0; i<s.points.size(); i++){
1585 int index = s.points.get(i).index;
1586 color c = colors[index];
1587 colors[index] = lx.hsb((i*22.5), saturation(c), brightness(c));
1588 }
1589 }*/
1590 }
1591 }
1592
1593 class BlendFrames extends SCEffect {
1594 int fcount;
1595 int frames[][];
1596 int maxfbuf;
1597 int blendfactor;
1598 BlendFrames(GLucose glucose) {
1599 super(glucose);
1600 maxfbuf = 30;
1601 blendfactor=30;
1602 fcount=0;
1603 frames = new int[maxfbuf][];
1604 for(int i=0; i<maxfbuf; i++){
1605 frames[i] = new int[model.points.size()];
1606 }
1607 }
1608 public void apply(int[] colors) {
1609 if(fcount<maxfbuf){
1610 for(int i=0; i<colors.length; i++){
1611 frames[(maxfbuf-1)-fcount][i]=colors[i];
1612 }
1613 fcount++;
1614 return;
1615 } else {
1616 for(int i=maxfbuf-1; i>0; i--){
1617 frames[i] = frames[i-1];
1618 }
1619 frames[0] = new int[model.points.size()];
1620
1621 for(int i=0; i<colors.length; i++){
1622 int r,g,b;
1623 r=g=b=0;
1624 for(int j=0; j<blendfactor; j++){
1625 if(j==0) { frames[0][i] = colors[i]; }
1626 r += ((frames[j][i] >> 16) & 0xFF);
1627 g += ((frames[j][i] >> 8) & 0xFF);
1628 b += ((frames[j][i] >> 0) & 0xFF);
1629 }
1630 r/=blendfactor;
1631 g/=blendfactor;
1632 b/=blendfactor;
1633 colorMode(ARGB);
1634 colors[i] = (0xFF << 24) | (r << 16) | (g << 8) | b;
1635 colorMode(HSB);
1636 }
1637
1638 }
1639 }
1640 }
1641
1642
1643
1644
1645
1646
1647
1648 abstract class OSCPattern extends SCPattern {
1649 public OSCPattern(GLucose glucose){super(glucose);}
1650 public abstract void oscEvent(OscMessage msg);
1651 }
1652
1653 class Ball {
1654 public int lastSeen;
1655 public float x,y;
1656 public Ball(){
1657 x=y=lastSeen=0;
1658 }
1659 }
1660
1661 class OSC_Balls extends OSCPattern {
1662 Ball[] balls;
1663 public OSC_Balls(GLucose glucose){
1664 super(glucose);
1665 balls = new Ball[20];
1666 for(int i=0; i<balls.length; i++) { balls[i] = new Ball(); }
1667 }
1668 public void oscEvent(OscMessage msg){
1669 String pattern[] = split(msg.addrPattern(), "/");
1670 int ballnum = PApplet.parseInt(pattern[3]);
1671 balls[ballnum].lastSeen=millis();
1672 balls[ballnum].x = msg.get(0).floatValue();
1673 balls[ballnum].y = msg.get(1).floatValue();
1674 }
1675
1676 public void run(double deltaMs){
1677 for(Point p: model.points){ colors[p.index]=0; }
1678 for(int i=1; i<balls.length; i++){
1679 if(millis() - balls[i].lastSeen < 1000) {
1680 for(Point p: model.points){
1681 int x = PApplet.parseInt(balls[i].x * 255.0f);
1682 int y = PApplet.parseInt(balls[i].y * 127.0f);
1683 if(p.x < x+4 && p.x > x-4 && p.y < y+4 && p.y > y-4) { colors[p.index] = 0xffFF0000; }
1684 }
1685 }
1686 }
1687 }
1688 }
1689
1690
1691
1692
1693 /*class ScreenScrape extends SCPattern {
1694 PImage pret;
1695 ScreenShot ss;
1696 public ScreenScrape(GLucose glucose) {
1697 super(glucose);
1698 System.loadLibrary("ScreenShot");
1699 pret = new PImage(8, 128, ARGB);
1700 ss = new ScreenShot();
1701 }
1702 void run(double deltaMs){
1703 int x=(1366/2)+516;
1704 int y=768-516;
1705 int w=8;
1706 int h=128;
1707 pret.pixels = ss.getScreenShotJNI2(x, y, w, h);
1708 //for(int i=0; i<px.length; i++){ pret.pixels[i] = px[i]; }
1709 //println(pret.get(10,10));
1710 for(Point p: model.points){
1711 colors[p.index] = pret.get((int(p.x)/8)*8, 128-int(p.y));
1712 }
1713 }
1714 }*/
1715
1716 //----------------------------------------------------------------------------------------------------------------------------------
1717 int NumApcRows=4, NumApcCols=8;
1718
1719 public boolean btwn (int a,int b,int c) { return a >= b && a <= c; }
1720 public boolean btwn (double a,double b,double c) { return a >= b && a <= c; }
1721 public float interp (float a, float b, float c) { return (1-a)*b + a*c; }
1722 public float randctr (float a) { return random(a) - a*.5f; }
1723 public float min (float a, float b, float c, float d) { return min(min(a,b),min(c,d)); }
1724 public float pointDist(Point p1, Point p2) { return dist(p1.x,p1.y,p1.z,p2.x,p2.y,p2.z); }
1725 public float xyDist (Point p1, Point p2) { return dist(p1.x,p1.y,p2.x,p2.y); }
1726 public float distToSeg(float x, float y, float x1, float y1, float x2, float y2) {
1727 float A = x - x1, B = y - y1, C = x2 - x1, D = y2 - y1;
1728 float dot = A * C + B * D, len_sq = C * C + D * D;
1729 float xx, yy,param = dot / len_sq;
1730
1731 if (param < 0 || (x1 == x2 && y1 == y2)) { xx = x1; yy = y1; }
1732 else if (param > 1) { xx = x2; yy = y2; }
1733 else { xx = x1 + param * C;
1734 yy = y1 + param * D; }
1735 float dx = x - xx, dy = y - yy;
1736 return sqrt(dx * dx + dy * dy);
1737 }
1738
1739 public class Pick {
1740 int NumPicks, Default ,
1741 CurRow , CurCol ,
1742 StartRow, EndRow ;
1743 String tag , Desc[] ;
1744
1745 Pick (String label, int _Def, int _Num, int nStart, String d[]) {
1746 NumPicks = _Num; Default = _Def;
1747 StartRow = nStart; EndRow = StartRow + floor((NumPicks-1) / NumApcCols);
1748 tag = label; Desc = d;
1749 reset();
1750 }
1751
1752 public int Cur() { return (CurRow-StartRow)*NumApcCols + CurCol; }
1753 public String CurDesc() { return Desc[Cur()]; }
1754 public void reset() { CurCol = Default % NumApcCols; CurRow = StartRow + Default / NumApcCols; }
1755
1756 public boolean set(int r, int c) {
1757 if (!btwn(r,StartRow,EndRow) || !btwn(c,0,NumApcCols-1) ||
1758 !btwn((r-StartRow)*NumApcCols + c,0,NumPicks-1)) return false;
1759 CurRow=r; CurCol=c; return true;
1760 }
1761 }
1762
1763 public class DBool {
1764 boolean def, b;
1765 String tag;
1766 int row, col;
1767 public void reset() { b = def; }
1768 public boolean set (int r, int c, boolean val) { if (r != row || c != col) return false; b = val; return true; }
1769 DBool(String _tag, boolean _def, int _row, int _col) {
1770 def = _def; b = _def; tag = _tag; row = _row; col = _col;
1771 }
1772 }
1773 //----------------------------------------------------------------------------------------------------------------------------------
1774 public class DPat extends SCPattern
1775 {
1776 ArrayList<Pick> picks = new ArrayList<Pick> ();
1777 ArrayList<DBool> bools = new ArrayList<DBool> ();
1778
1779 PVector mMax, mCtr, mHalf;
1780
1781 MidiOutput APCOut;
1782 int nMaxRow = 53;
1783 float LastJog = -1;
1784 float[] xWaveNz, yWaveNz;
1785 int nPoint , nPoints;
1786 PVector xyzJog = new PVector(), modmin;
1787
1788 float NoiseMove = random(10000);
1789 BasicParameter pSpark, pWave, pRotX, pRotY, pRotZ, pSpin, pTransX, pTransY;
1790 DBool pXsym, pYsym, pRsym, pXdup, pXtrip, pJog, pGrey;
1791
1792 public float lxh () { return lx.getBaseHuef(); }
1793 public int c1c (float a) { return round(100*constrain(a,0,1)); }
1794 public float interpWv(float i, float[] vals) { return interp(i-floor(i), vals[floor(i)], vals[ceil(i)]); }
1795 public void setNorm (PVector vec) { vec.set(vec.x/mMax.x, vec.y/mMax.y, vec.z/mMax.z); }
1796 public void setRand (PVector vec) { vec.set(random(mMax.x), random(mMax.y), random(mMax.z)); }
1797 public void setVec (PVector vec, Point p) { vec.set(p.x, p.y, p.z); }
1798 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)); }
1799 public void StartRun(double deltaMs) { }
1800 public float val (BasicParameter p) { return p.getValuef(); }
1801 public int CalcPoint(PVector p) { return lx.hsb(0,0,0); }
1802 public int blend3(int c1, int c2, int c3) { return blendColor(c1,blendColor(c2,c3,ADD),ADD); }
1803
1804 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); }
1805 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 ); }
1806 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 ); }
1807
1808 public BasicParameter addParam(String label, double value) { BasicParameter p = new BasicParameter(label, value); addParameter(p); return p; }
1809
1810 PVector vT1 = new PVector(), vT2 = new PVector();
1811 public float calcCone (PVector v1, PVector v2, PVector c) { vT1.set(v1); vT2.set(v2); vT1.sub(c); vT2.sub(c);
1812 return degrees(PVector.angleBetween(vT1,vT2)); }
1813
1814 public Pick addPick(String name, int def, int _max, String[] desc) {
1815 Pick P = new Pick(name, def, _max+1, nMaxRow, desc);
1816 nMaxRow = P.EndRow + 1;
1817 picks.add(P);
1818 return P;
1819 }
1820
1821 public boolean noteOff(Note note) {
1822 int row = note.getPitch(), col = note.getChannel();
1823 for (int i=0; i<bools.size(); i++) if (bools.get(i).set(row, col, false)) { presetManager.dirty(this); return true; }
1824 updateLights(); return false;
1825 }
1826
1827 public boolean noteOn(Note note) {
1828 int row = note.getPitch(), col = note.getChannel();
1829 for (int i=0; i<picks.size(); i++) if (picks.get(i).set(row, col)) { presetManager.dirty(this); return true; }
1830 for (int i=0; i<bools.size(); i++) if (bools.get(i).set(row, col, true)) { presetManager.dirty(this); return true; }
1831 println("row: " + row + " col: " + col); return false;
1832 }
1833
1834 public void onInactive() { uiDebugText.setText(""); }
1835 public void onReset() {
1836 for (int i=0; i<bools .size(); i++) bools.get(i).reset();
1837 for (int i=0; i<picks .size(); i++) picks.get(i).reset();
1838 presetManager.dirty(this);
1839 updateLights();
1840 }
1841
1842 DPat(GLucose glucose) {
1843 super(glucose);
1844
1845 pSpark = addParam("Sprk", 0);
1846 pWave = addParam("Wave", 0);
1847 pTransX = addParam("TrnX", .5f);
1848 pTransY = addParam("TrnY", .5f);
1849 pRotX = addParam("RotX", .5f);
1850 pRotY = addParam("RotY", .5f);
1851 pRotZ = addParam("RotZ", .5f);
1852 pSpin = addParam("Spin", .5f);
1853
1854 nPoints = model.points.size();
1855 pXsym = new DBool("X-SYM", false, 48, 0); bools.add(pXsym );
1856 pYsym = new DBool("Y-SYM", false, 48, 1); bools.add(pYsym );
1857 pRsym = new DBool("R-SYM", false, 48, 2); bools.add(pRsym );
1858 pXdup = new DBool("X-DUP", false, 48, 3); bools.add(pXdup );
1859 pJog = new DBool("JOG" , false, 48, 4); bools.add(pJog );
1860 pGrey = new DBool("GREY" , false, 48, 5); bools.add(pGrey );
1861
1862 modmin = new PVector(model.xMin, model.yMin, model.zMin);
1863 mMax = new PVector(model.xMax, model.yMax, model.zMax); mMax.sub(modmin);
1864 mCtr = new PVector(); mCtr.set(mMax); mCtr.mult(.5f);
1865 mHalf = new PVector(.5f,.5f,.5f);
1866 xWaveNz = new float[ceil(mMax.y)+1];
1867 yWaveNz = new float[ceil(mMax.x)+1];
1868
1869 //println (model.xMin + " " + model.yMin + " " + model.zMin);
1870 //println (model.xMax + " " + model.yMax + " " + model.zMax);
1871 //for (MidiOutputDevice o: RWMidi.getOutputDevices()) { if (o.toString().contains("APC")) { APCOut = o.createOutput(); break;}}
1872 }
1873
1874 public float spin() {
1875 float raw = val(pSpin);
1876 if (raw <= 0.45f) {
1877 return raw + 0.05f;
1878 } else if (raw >= 0.55f) {
1879 return raw - 0.05f;
1880 }
1881 return 0.5f;
1882 }
1883
1884 public void setAPCOutput(MidiOutput output) {
1885 APCOut = output;
1886 }
1887
1888 public void updateLights() { if (APCOut == null) return;
1889 for (int i = 0; i < NumApcRows; ++i)
1890 for (int j = 0; j < 8; ++j) APCOut.sendNoteOn(j, 53+i, 0);
1891 for (int i=0; i<picks .size(); i++) APCOut.sendNoteOn(picks.get(i).CurCol, picks.get(i).CurRow, 3);
1892 for (int i=0; i<bools .size(); i++) if (bools.get(i).b) APCOut.sendNoteOn (bools.get(i).col, bools.get(i).row, 1);
1893 else APCOut.sendNoteOff (bools.get(i).col, bools.get(i).row, 0);
1894 }
1895
1896 public void run(double deltaMs)
1897 {
1898 if (deltaMs > 100) return;
1899
1900 if (this == midiEngine.getFocusedDeck().getActivePattern()) {
1901 String Text1="", Text2="";
1902 for (int i=0; i<bools.size(); i++) if (bools.get(i).b) Text1 += " " + bools.get(i).tag + " ";
1903 for (int i=0; i<picks.size(); i++) Text1 += picks.get(i).tag + ": " + picks.get(i).CurDesc() + " ";
1904 uiDebugText.setText(Text1, Text2);
1905 }
1906
1907 NoiseMove += deltaMs; NoiseMove = NoiseMove % 1e7f;
1908 StartRun (deltaMs);
1909 PVector P = new PVector(), tP = new PVector(), pSave = new PVector();
1910 PVector pTrans = new PVector(val(pTransX)*200-100, val(pTransY)*100-50,0);
1911 nPoint = 0;
1912
1913 if (pJog.b) {
1914 float tRamp = (lx.tempo.rampf() % .25f);
1915 if (tRamp < LastJog) xyzJog.set(randctr(mMax.x*.2f), randctr(mMax.y*.2f), randctr(mMax.z*.2f));
1916 LastJog = tRamp;
1917 }
1918
1919 // precalculate this stuff
1920 float wvAmp = val(pWave), sprk = val(pSpark);
1921 if (wvAmp > 0) {
1922 for (int i=0; i<ceil(mMax.x)+1; i++)
1923 yWaveNz[i] = wvAmp * (noise(i/(mMax.x*.3f)-(2e3f+NoiseMove)/1500.f) - .5f) * (mMax.y/2.f);
1924
1925 for (int i=0; i<ceil(mMax.y)+1; i++)
1926 xWaveNz[i] = wvAmp * (noise(i/(mMax.y*.3f)-(1e3f+NoiseMove)/1500.f) - .5f) * (mMax.x/2.f);
1927 }
1928
1929 for (Point p : model.points) { nPoint++;
1930 setVec(P,p);
1931 P.sub(modmin);
1932 P.sub(pTrans);
1933 if (sprk > 0) {P.y += sprk*randctr(50); P.x += sprk*randctr(50); P.z += sprk*randctr(50); }
1934 if (wvAmp > 0) P.y += interpWv(p.x-modmin.x, yWaveNz);
1935 if (wvAmp > 0) P.x += interpWv(p.y-modmin.y, xWaveNz);
1936 if (pJog.b) P.add(xyzJog);
1937
1938
1939 int cNew, cOld = colors[p.index];
1940 { tP.set(P); cNew = CalcPoint(tP); }
1941 if (pXsym.b) { tP.set(mMax.x-P.x,P.y,P.z); cNew = blendColor(cNew, CalcPoint(tP), ADD); }
1942 if (pYsym.b) { tP.set(P.x,mMax.y-P.y,P.z); cNew = blendColor(cNew, CalcPoint(tP), ADD); }
1943 if (pRsym.b) { tP.set(mMax.x-P.x,mMax.y-P.y,mMax.z-P.z); cNew = blendColor(cNew, CalcPoint(tP), ADD); }
1944 if (pXdup.b) { tP.set((P.x+mMax.x*.5f)%mMax.x,P.y,P.z); cNew = blendColor(cNew, CalcPoint(tP), ADD); }
1945 if (pGrey.b) { cNew = lx.hsb(0, 0, lx.b(cNew)); }
1946 colors[p.index] = cNew;
1947 }
1948 }
1949 }
1950 //----------------------------------------------------------------------------------------------------------------------------------
1951 class dTurn {
1952 dVertex v;
1953 int pos0, pos1;
1954 dTurn(int _pos0, dVertex _v, int _pos1) { v = _v; pos0 = _pos0; pos1 = _pos1; }
1955 }
1956
1957 class dVertex {
1958 dVertex c0, c1, c2, c3, // connections on the cube
1959 opp, same; // opp - same strip, opp direction
1960 // same - same strut, diff strip, dir
1961 dTurn t0, t1, t2, t3;
1962 Strip s;
1963 int dir, ci; // dir -- 1 or -1.
1964 // ci -- color index
1965
1966 dVertex(Strip _s, Point _p) { s = _s; ci = _p.index; }
1967 public Point getPoint(int i) { return s.points.get(dir>0 ? i : 15-i); }
1968 public void setOpp(dVertex _opp) { opp = _opp; dir = (ci < opp.ci ? 1 : -1); }
1969 }
1970 //----------------------------------------------------------------------------------------------------------------------------------
1971 class dPixel { dVertex v; int pos; dPixel(dVertex _v, int _pos) { v=_v; pos=_pos; } }
1972 class dLattice {
1973 public void addTurn (dVertex v0, int pos0, dVertex v1, int pos1) { dTurn t = new dTurn(pos0, v1, pos1);
1974 if (v0.t0 == null) { v0.t0=t; return; }
1975 if (v0.t1 == null) { v0.t1=t; return; }
1976 if (v0.t2 == null) { v0.t2=t; return; }
1977 if (v0.t3 == null) { v0.t3=t; return; }
1978 }
1979 public float dist2 (Strip s1, int pos1, Strip s2, int pos2) { return pointDist(s1.points.get(pos1), s2.points.get(pos2)); }
1980 public float pd2 (Point p1, float x, float y, float z) { return dist(p1.x,p1.y,p1.z,x,y,z); }
1981 public boolean sameSame (Strip s1, Strip s2) { return max(dist2(s1, 0, s2, 0), dist2(s1,15, s2,15)) < 5 ; } // same strut, same direction
1982 public boolean sameOpp (Strip s1, Strip s2) { return max(dist2(s1, 0, s2,15), dist2(s1,15, s2,0 )) < 5 ; } // same strut, opp direction
1983 public boolean sameBar (Strip s1, Strip s2) { return sameSame(s1,s2) || sameOpp(s1,s2); } // 2 strips on same strut
1984
1985
1986 public void addJoint (dVertex v1, dVertex v2) {
1987 // should probably replace parallel but further with the new one
1988 if (v1.c0 != null && sameBar(v2.s, v1.c0.s)) return;
1989 if (v1.c1 != null && sameBar(v2.s, v1.c1.s)) return;
1990 if (v1.c2 != null && sameBar(v2.s, v1.c2.s)) return;
1991 if (v1.c3 != null && sameBar(v2.s, v1.c3.s)) return;
1992
1993 if (v1.c0 == null) v1.c0 = v2;
1994 else if (v1.c1 == null) v1.c1 = v2;
1995 else if (v1.c2 == null) v1.c2 = v2;
1996 else if (v1.c3 == null) v1.c3 = v2;
1997 }
1998
1999 public dVertex v0(Strip s) { return (dVertex)s.obj1; }
2000 public dVertex v1(Strip s) { return (dVertex)s.obj2; }
2001
2002 public dPixel getClosest(PVector p) {
2003 dVertex v = null; int pos=0; float d = 500;
2004
2005 for (Strip s : glucose.model.strips) {
2006 float nd = pd2(s.points.get(0),p.x,p.y,p.z); if (nd < d) { v=v0(s); d=nd; pos=0; }
2007 if (nd > 30) continue;
2008 for (int k=0; k<=15; k++) {
2009 nd = pd2(s.points.get(k),p.x,p.y,p.z); if (nd < d) { v =v0(s); d=nd; pos=k; }
2010 }
2011 }
2012 return random(2) < 1 ? new dPixel(v,pos) : new dPixel(v.opp,15-pos);
2013 }
2014
2015 dLattice() {
2016 lattice=this;
2017
2018 for (Strip s : glucose.model.strips) {
2019 dVertex vrtx0 = new dVertex(s,s.points.get(0 )); s.obj1=vrtx0;
2020 dVertex vrtx1 = new dVertex(s,s.points.get(15)); s.obj2=vrtx1;
2021 vrtx0.setOpp(vrtx1); vrtx1.setOpp(vrtx0);
2022 }
2023
2024 for (Strip s1 : glucose.model.strips) { for (Strip s2 : glucose.model.strips) {
2025 if (s1.points.get(0).index < s2.points.get(0).index) continue;
2026 int c=0;
2027 if (sameSame(s1,s2)) { v0(s1).same = v0(s2); v1(s1).same = v1(s2);
2028 v0(s2).same = v0(s1); v1(s2).same = v1(s1); continue; } // parallel
2029 if (sameOpp (s1,s2)) { v0(s1).same = v1(s2); v1(s1).same = v0(s2);
2030 v0(s2).same = v1(s1); v1(s2).same = v0(s1); continue; } // parallel
2031 if (dist2(s1, 0, s2, 0) < 5) { c++; addJoint(v1(s1), v0(s2)); addJoint(v1(s2), v0(s1)); }
2032 if (dist2(s1, 0, s2,15) < 5) { c++; addJoint(v1(s1), v1(s2)); addJoint(v0(s2), v0(s1)); }
2033 if (dist2(s1,15, s2, 0) < 5) { c++; addJoint(v0(s1), v0(s2)); addJoint(v1(s2), v1(s1)); }
2034 if (dist2(s1,15, s2,15) < 5) { c++; addJoint(v0(s1), v1(s2)); addJoint(v0(s2), v1(s1)); }
2035 if (c>0) continue;
2036
2037 // Are they touching at all?
2038 int pos1=0, pos2=0; float d = 100;
2039
2040 while (pos1 < 15 || pos2 < 15) {
2041 float oldD = d;
2042 if (pos1<15) { float d2 = dist2(s1, pos1+1, s2, pos2+0); if (d2 < d) { d=d2; pos1++; } }
2043 if (pos2<15) { float d2 = dist2(s1, pos1+0, s2, pos2+1); if (d2 < d) { d=d2; pos2++; } }
2044 if (d > 50 || oldD == d) break ;
2045 }
2046
2047 if (d>5) continue;
2048 addTurn(v0(s1), pos1, v0(s2), pos2); addTurn(v1(s1), 15-pos1, v0(s2), pos2);
2049 addTurn(v0(s2), pos2, v0(s1), pos1); addTurn(v1(s2), 15-pos2, v0(s1), pos1);
2050 }}
2051 }
2052 }
2053
2054 dLattice lattice;
2055 //----------------------------------------------------------------------------------------------------------------------------------
2056
2057 class Graphic
2058 {
2059 public boolean changed = false;
2060 public int position = 0;
2061 public ArrayList<Integer> graphicBuffer;
2062 Graphic()
2063 {
2064 graphicBuffer = new ArrayList<Integer>();
2065 }
2066 public int width()
2067 {
2068 return graphicBuffer.size();
2069 }
2070
2071
2072 };
2073 class Granim extends Graphic
2074 {
2075 HashMap<String,Graphic> displayList;
2076
2077 Granim()
2078 {
2079 displayList = new HashMap<String,Graphic>();
2080 }
2081 public Graphic addGraphic(String name, Graphic g)
2082 {
2083 while(width()< g.position+1)
2084 {
2085 graphicBuffer.add(lx.hsb(0,0,0));
2086 }
2087 drawAll();
2088 displayList.put(name , g);
2089 changed =true;
2090 return g;
2091 }
2092
2093 public Graphic getGraphicByName(String name)
2094 {
2095 return displayList.get(name);
2096 }
2097
2098 public void update()
2099 {
2100
2101 for(Graphic g : displayList.values())
2102 {
2103 if(g instanceof Granim)
2104 {
2105 ((Granim) g).update();
2106
2107 }
2108 changed = changed || g.changed;
2109 if(changed)
2110 {
2111 while(width()< g.position + g.width())
2112 {
2113 graphicBuffer.add(lx.hsb(0,0,0));
2114 }
2115 if(g.changed)
2116 {
2117 drawOne(g);
2118 g.changed =false;
2119 }
2120 }
2121 }
2122 changed = false;
2123
2124 }
2125 public void drawOne(Graphic g)
2126 {
2127 graphicBuffer.addAll(g.position,g.graphicBuffer);
2128 }
2129 public void drawAll()
2130 {
2131 }
2132 };
2133 class GranimPattern extends SCPattern
2134 {
2135 HashMap<String,Graphic> displayList;
2136
2137 GranimPattern(GLucose glucose)
2138 {
2139 super(glucose);
2140 displayList = new HashMap<String,Graphic>();
2141 }
2142
2143 public Graphic addGraphic(String name, Graphic g)
2144 {
2145 displayList.put(name,g);
2146 return g;
2147 }
2148
2149 public Graphic getGraphicByName(String name)
2150 {
2151 return displayList.get(name);
2152 }
2153
2154 public void run(double deltaMs)
2155 {
2156 drawToPointList();
2157 }
2158 private Integer[] gbuffer;
2159 public void drawToPointList()
2160 {
2161 for(Graphic g : displayList.values())
2162 {
2163 if(g instanceof Granim)
2164 {
2165 ((Granim) g).update();
2166 }
2167 List<Point> drawList = model.points.subList(Math.min(g.position,colors.length-1), Math.min(g.position + g.width(),colors.length-1));
2168 //println("drawlistsize "+drawList.size());
2169
2170 gbuffer = g.graphicBuffer.toArray(new Integer[0]);
2171
2172 for (int i=0; i < drawList.size(); i++)
2173 {
2174 colors[drawList.get(i).index] = gbuffer[i];
2175 }
2176 g.changed = false;
2177 }
2178 }
2179
2180 };
2181
2182 class RedsGraphic extends Graphic
2183 {
2184 RedsGraphic()
2185 {
2186 super();
2187 drawit(10);
2188 }
2189 RedsGraphic(int len)
2190 {
2191 super();
2192 drawit(len);
2193
2194 }
2195 public void drawit(int len)
2196 {
2197 for(int i = 0; i < len ;i++)
2198 {
2199 graphicBuffer.add(lx.hsb(0,255,255));
2200 }
2201 }
2202 };
2203
2204 class RedsGranim extends Granim
2205 {
2206 RedsGranim()
2207 {
2208 super();
2209 addGraphic("myreds", new RedsGraphic(10));
2210 }
2211 RedsGranim(int len)
2212 {
2213 super();
2214 addGraphic("myreds", new RedsGraphic(len));
2215 }
2216 public float count = 0.0f;
2217 public void update()
2218 {
2219 Graphic g=getGraphicByName("myreds");
2220 g.position = Math.round(sin(count)*20)+100;
2221 count+= 0.1f;
2222 if(count>Math.PI*2)
2223 {
2224 count=0;
2225 }
2226 super.update();
2227 }
2228
2229 };
2230
2231 class RandomsGranim extends Granim
2232 {
2233 private int _len =0 ;
2234 RandomsGranim()
2235 {
2236 super();
2237 _len =100;
2238 addGraphic("myrandoms", makeGraphic(_len));
2239 }
2240 RandomsGranim(int len)
2241 {
2242 super();
2243 _len=len;
2244 addGraphic("myrandoms", makeGraphic(len));
2245 }
2246 int colorLid=0;
2247 public Graphic makeGraphic(int len)
2248 {
2249
2250 int[] colors= new int[len];
2251 for(int i =0;i<len;i++)
2252 {
2253 colors[i]=(int) Math.round(Math.random()*80)+colorLid;
2254
2255 }
2256 colorLid+=4;
2257 return new ColorDotsGraphic(colors);
2258 }
2259 private int count =1;
2260 private int instanceCount =0;
2261 public void update()
2262 {
2263
2264 if(instanceCount<90 && count % 20==0)
2265 {
2266 instanceCount++;
2267 Graphic h=addGraphic("myrandoms_"+instanceCount, makeGraphic(_len));
2268 h.position = instanceCount*(_len+100);
2269 //println("one more " + instanceCount+" at "+h.position);
2270 count=0;
2271 changed = true;
2272 }
2273 count++;
2274 super.update();
2275 }
2276
2277 };
2278
2279
2280 class ColorDotsGraphic extends Graphic
2281 {
2282 ColorDotsGraphic(int[] colorSequence)
2283 {
2284 super();
2285 for (int colorVal : colorSequence)
2286 {
2287 graphicBuffer.add(lx.hsb(colorVal, 255, 255));
2288 }
2289 changed = true;
2290 }
2291 };
2292 int BLACK = 0xff000000;
2293
2294 class Gimbal extends SCPattern {
2295
2296 private final boolean DEBUG_MANUAL_ABG = false;
2297 private final int MAXIMUM_BEATS_PER_REVOLUTION = 100;
2298
2299 private boolean first_run = true;
2300 private final Projection projection;
2301 private final BasicParameter beatsPerRevolutionParam = new BasicParameter("SLOW", 20.f/MAXIMUM_BEATS_PER_REVOLUTION);
2302 private final BasicParameter hueDeltaParam = new BasicParameter("HUED", 60.f/360);
2303 private final BasicParameter fadeFromCoreParam = new BasicParameter("FADE", 1);
2304 private final BasicParameter girthParam = new BasicParameter("GRTH", .18f);
2305 private final BasicParameter ringExtendParam = new BasicParameter("XTND", 1);
2306 private final BasicParameter relativeSpeedParam = new BasicParameter("RLSP", .83f);
2307 private final BasicParameter sizeParam = new BasicParameter("SIZE", .9f);
2308
2309 private final BasicParameter aP = new BasicParameter("a", 0);
2310 private final BasicParameter bP = new BasicParameter("b", 0);
2311 private final BasicParameter gP = new BasicParameter("g", 0);
2312
2313 Gimbal(GLucose glucose) {
2314 super(glucose);
2315 projection = new Projection(model);
2316 addParameter(beatsPerRevolutionParam);
2317 addParameter(hueDeltaParam);
2318 addParameter(fadeFromCoreParam);
2319 addParameter(girthParam);
2320 addParameter(ringExtendParam);
2321 addParameter(relativeSpeedParam);
2322 addParameter(sizeParam);
2323
2324 if (DEBUG_MANUAL_ABG) {
2325 addParameter(aP);
2326 addParameter(bP);
2327 addParameter(gP);
2328 }
2329 }
2330
2331 float a = 0, b = 0, g = 0;
2332
2333 public void run(double deltaMs) {
2334
2335 if (DEBUG_MANUAL_ABG) {
2336 a = aP.getValuef() * (2 * PI);
2337 b = bP.getValuef() * (2 * PI);
2338 g = gP.getValuef() * (2 * PI);
2339 } else {
2340 float relativeSpeed = relativeSpeedParam.getValuef();
2341 float time = millis() / 1000.f;
2342
2343 int beatsPerRevolution = (int) (beatsPerRevolutionParam.getValuef() * MAXIMUM_BEATS_PER_REVOLUTION) + 1;
2344 float radiansPerMs = 2 * PI // radians / revolution
2345 / beatsPerRevolution // beats / revolution
2346 * lx.tempo.bpmf() // BPM beats / min
2347 / 60 // sec / min
2348 / 1000; // ms / sec
2349
2350 a += deltaMs * radiansPerMs * pow(relativeSpeed, 0);
2351 b += deltaMs * radiansPerMs * pow(relativeSpeed, 1);
2352 g += deltaMs * radiansPerMs * pow(relativeSpeed, 2);
2353 a %= 2 * PI;
2354 b %= 2 * PI;
2355 g %= 2 * PI;
2356 }
2357
2358 float hue = lx.getBaseHuef();
2359 float hue_delta = hueDeltaParam.getValuef() * 360;
2360
2361 float radius1 = model.xMax / 2 * sizeParam.getValuef();
2362 float radius2 = ((model.xMax + model.yMax) / 2) / 2 * sizeParam.getValuef();
2363 float radius3 = model.yMax / 2 * sizeParam.getValuef();
2364 float girth = model.xMax * girthParam.getValuef();
2365 Ring ring1 = new Ring((hue + hue_delta * 0) % 360, radius1, girth);
2366 Ring ring2 = new Ring((hue + hue_delta * 1) % 360, radius2, girth);
2367 Ring ring3 = new Ring((hue + hue_delta * 2) % 360, radius3, girth);
2368
2369 projection.reset(model)
2370 // Translate so the center of the car is the origin
2371 .translateCenter(model, 0, 0, 0);
2372
2373 for (Coord c : projection) {
2374 //if (first_run) println(c.x + "," + c.y + "," + c.z);
2375
2376 rotate3d(c, a, 0, 0);
2377 rotate3d(c, PI/4, PI/4, PI/4);
2378 int color1 = ring1.colorFor(c);
2379
2380 rotate3d(c, 0, b, 0);
2381 int color2 = ring2.colorFor(c);
2382
2383 rotate3d(c, 0, 0, g);
2384 int color3 = ring3.colorFor(c);
2385
2386 colors[c.index] = specialBlend(color1, color2, color3);
2387 }
2388
2389 first_run = false;
2390 }
2391
2392 class Ring {
2393
2394 float hue;
2395 float radius, girth;
2396
2397 public Ring(float hue, float radius, float girth) {
2398 this.hue = hue;
2399 this.radius = radius;
2400 this.girth = girth;
2401 }
2402
2403 public int colorFor(Coord c) {
2404 float theta = atan2(c.y, c.x);
2405 float nearest_circle_x = cos(theta) * radius;
2406 float nearest_circle_y = sin(theta) * radius;
2407 float nearest_circle_z = 0;
2408
2409 float distance_to_circle
2410 = sqrt(pow(nearest_circle_x - c.x, 2)
2411 + pow(nearest_circle_y - c.y, 2)
2412 + pow(nearest_circle_z - c.z * ringExtendParam.getValuef(), 2));
2413
2414 float xy_distance = sqrt(c.x*c.x + c.y*c.y);
2415 return lx.hsb(this.hue, 100, (1 - distance_to_circle / girth * fadeFromCoreParam.getValuef()) * 100);
2416 }
2417
2418 }
2419
2420 }
2421
2422
2423
2424
2425
2426
2427 class Zebra extends SCPattern {
2428
2429 private final Projection projection;
2430 SinLFO angleM = new SinLFO(0, PI * 2, 30000);
2431
2432 /*
2433 SinLFO x, y, z, dx, dy, dz;
2434 float cRad;
2435 _P size;
2436 */
2437
2438 Zebra(GLucose glucose) {
2439 super(glucose);
2440 projection = new Projection(model);
2441
2442 addModulator(angleM).trigger();
2443 }
2444
2445 public int colorFor(Coord c) {
2446 float hue = lx.getBaseHuef();
2447
2448
2449
2450
2451 /* SLIDE ALONG
2452 c.x = c.x + millis() / 100.f;
2453 */
2454
2455
2456
2457 int stripe_count = 12;
2458 float stripe_width = model.xMax / (float)stripe_count;
2459 if (Math.floor((c.x) / stripe_width) % 2 == 0) {
2460 return lx.hsb(hue, 100, 100);
2461 } else {
2462 return lx.hsb((hue + 90) % 360, 100, 100);
2463 }
2464
2465
2466 /* OCTANTS
2467
2468 if ((isPositiveBit(c.x) + isPositiveBit(c.y) + isPositiveBit(c.z)) % 2 == 0) {
2469 return lx.hsb(lx.getBaseHuef(), 100, 100);
2470 } else {
2471 return lx.hsb(0, 0, 0);
2472 }
2473 */
2474 }
2475
2476 public int isPositiveBit(float f) {
2477 return f > 0 ? 1 : 0;
2478 }
2479
2480 public void run(double deltaMs) {
2481 float a = (millis() / 1000.f) % (2 * PI);
2482 float b = (millis() / 1200.f) % (2 * PI);
2483 float g = (millis() / 1600.f) % (2 * PI);
2484
2485 projection.reset(model)
2486 // Translate so the center of the car is the origin
2487 .translateCenter(model, 0, 0, 0);
2488
2489 for (Coord c : projection) {
2490 // rotate3d(c, a, b, g);
2491 colors[c.index] = colorFor(c);
2492 }
2493
2494 first_run = false;
2495 }
2496
2497
2498 // Utility!
2499 boolean first_run = true;
2500 private void log(String s) {
2501 if (first_run) {
2502 println(s);
2503 }
2504 }
2505
2506
2507 }
2508
2509 public void rotate3d(Coord c, float a /* roll */, float b /* pitch */, float g /* yaw */) {
2510 float cosa = cos(a);
2511 float cosb = cos(b);
2512 float cosg = cos(g);
2513 float sina = sin(a);
2514 float sinb = sin(b);
2515 float sing = sin(g);
2516
2517 float a1 = cosa*cosb;
2518 float a2 = cosa*sinb*sing - sina*cosg;
2519 float a3 = cosa*sinb*cosg + sina*sing;
2520 float b1 = sina*cosb;
2521 float b2 = sina*sinb*sing + cosa*cosg;
2522 float b3 = sina*sinb*cosg - cosa*sing;
2523 float c1 = -sinb;
2524 float c2 = cosb*sing;
2525 float c3 = cosb*cosg;
2526
2527 float[] cArray = { c.x, c.y, c.z };
2528 c.x = dotProduct(new float[] {a1, a2, a3}, cArray);
2529 c.y = dotProduct(new float[] {b1, b2, b3}, cArray);
2530 c.z = dotProduct(new float[] {c1, c2, c3}, cArray);
2531 }
2532
2533 public float dotProduct(float[] a, float[] b) {
2534 float ret = 0;
2535 for (int i = 0 ; i < a.length; ++i) {
2536 ret += a[i] * b[i];
2537 }
2538 return ret;
2539 }
2540
2541 public int specialBlend(int c1, int c2, int c3) {
2542 float h1 = hue(c1);
2543 float h2 = hue(c2);
2544 float h3 = hue(c3);
2545
2546 // force h1 < h2 < h3
2547 while (h2 < h1) {
2548 h2 += 360;
2549 }
2550 while (h3 < h2) {
2551 h3 += 360;
2552 }
2553
2554 float s1 = saturation(c1);
2555 float s2 = saturation(c2);
2556 float s3 = saturation(c3);
2557
2558 float b1 = brightness(c1);
2559 float b2 = brightness(c2);
2560 float b3 = brightness(c3);
2561 float relative_b1 = b1 / (b1 + b2 + b3);
2562 float relative_b2 = b2 / (b1 + b2 + b3);
2563 float relative_b3 = b3 / (b1 + b2 + b3);
2564
2565 return lx.hsb(
2566 (h1 * relative_b1 + h2 * relative_b1 + h3 * relative_b3) % 360,
2567 s1 * relative_b1 + s2 * relative_b2 + s3 * relative_b3,
2568 max(max(b1, b2), b3)
2569 );
2570 }
2571
2572 /**
2573 * A Projection of sin wave in 3d space.
2574 * It sort of looks like an animal swiming around in water.
2575 * Angle sliders are sort of a work in progress that allow yo to change the crazy ways it moves around.
2576 * Hue slider allows you to control how different the colors are along the wave.
2577 *
2578 * This code copied heavily from Tim and Slee.
2579 */
2580 class Swim extends SCPattern {
2581
2582 // Projection stuff
2583 private final Projection projection;
2584 SawLFO rotation = new SawLFO(0, TWO_PI, 19000);
2585 SinLFO yPos = new SinLFO(-25, 25, 12323);
2586 final BasicParameter xAngle = new BasicParameter("XANG", 0.9f);
2587 final BasicParameter yAngle = new BasicParameter("YANG", 0.3f);
2588 final BasicParameter zAngle = new BasicParameter("ZANG", 0.3f);
2589
2590 final BasicParameter hueScale = new BasicParameter("HUE", 0.3f);
2591
2592 public Swim(GLucose glucose) {
2593 super(glucose);
2594 projection = new Projection(model);
2595
2596 addParameter(xAngle);
2597 addParameter(yAngle);
2598 addParameter(zAngle);
2599 addParameter(hueScale);
2600
2601 addModulator(rotation).trigger();
2602 addModulator(yPos).trigger();
2603 }
2604
2605
2606 int beat = 0;
2607 float prevRamp = 0;
2608 public void run(double deltaMs) {
2609
2610 // Sync to the beat
2611 float ramp = (float)lx.tempo.ramp();
2612 if (ramp < prevRamp) {
2613 beat = (beat + 1) % 4;
2614 }
2615 prevRamp = ramp;
2616 float phase = (beat+ramp) / 2.0f * 2 * PI;
2617
2618 float denominator = max(xAngle.getValuef() + yAngle.getValuef() + zAngle.getValuef(), 1);
2619
2620 projection.reset(model)
2621 // Swim around the world
2622 .rotate(rotation.getValuef(), xAngle.getValuef() / denominator, yAngle.getValuef() / denominator, zAngle.getValuef() / denominator)
2623 .translateCenter(model, 0, 50 + yPos.getValuef(), 0);
2624
2625 float model_height = model.yMax - model.yMin;
2626 float model_width = model.xMax - model.xMin;
2627 for (Coord p : projection) {
2628 float x_percentage = (p.x - model.xMin)/model_width;
2629
2630 // Multiply by 1.4 to shrink the size of the sin wave to be less than the height of the cubes.
2631 float y_in_range = 1.4f * (2*p.y - model.yMax - model.yMin) / model_height;
2632 float sin_x = sin(phase + 2 * PI * x_percentage);
2633
2634 // Color fade near the top of the sin wave
2635 float v1 = sin_x > y_in_range ? (100 + 100*(y_in_range - sin_x)) : 0;
2636
2637 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;
2638 colors[p.index] = lx.hsb(hue_color, 70, v1);
2639 }
2640 }
2641 }
2642
2643 /**
2644 * 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.
2645 * This is not done.
2646 */
2647 class Balance extends SCPattern {
2648
2649 final BasicParameter hueScale = new BasicParameter("Hue", 0.4f);
2650
2651 class Sphere {
2652 float x, y, z;
2653 }
2654
2655
2656 // Projection stuff
2657 private final Projection projection;
2658
2659 SinLFO sphere1Z = new SinLFO(0, 0, 15323);
2660 SinLFO sphere2Z = new SinLFO(0, 0, 8323);
2661 SinLFO rotationX = new SinLFO(-PI/32, PI/32, 9000);
2662 SinLFO rotationY = new SinLFO(-PI/16, PI/16, 7000);
2663 SinLFO rotationZ = new SinLFO(-PI/16, PI/16, 11000);
2664 SawLFO phaseLFO = new SawLFO(0, 2 * PI, 5000 - 4500 * 0.5f);
2665 final BasicParameter phaseParam = new BasicParameter("Spd", 0.5f);
2666 final BasicParameter crazyParam = new BasicParameter("Crzy", 0.2f);
2667
2668
2669 private final Sphere[] spheres;
2670 private final float centerX, centerY, centerZ, modelHeight, modelWidth, modelDepth;
2671 SinLFO heightMod = new SinLFO(0.8f, 1.9f, 17298);
2672
2673 public Balance(GLucose glucose) {
2674 super(glucose);
2675
2676 projection = new Projection(model);
2677
2678 addParameter(hueScale);
2679 addParameter(phaseParam);
2680 addParameter(crazyParam);
2681
2682 spheres = new Sphere[2];
2683 centerX = (model.xMax + model.xMin) / 2;
2684 centerY = (model.yMax + model.yMin) / 2;
2685 centerZ = (model.zMax + model.zMin) / 2;
2686 modelHeight = model.yMax - model.yMin;
2687 modelWidth = model.xMax - model.xMin;
2688 modelDepth = model.zMax - model.zMin;
2689
2690 spheres[0] = new Sphere();
2691 spheres[0].x = 1*modelWidth/2 + model.xMin;
2692 spheres[0].y = centerY + 20;
2693 spheres[0].z = centerZ;
2694
2695 spheres[1] = new Sphere();
2696 spheres[1].x = model.xMin;
2697 spheres[1].y = centerY - 20;
2698 spheres[1].z = centerZ;
2699
2700 addModulator(rotationX).trigger();
2701 addModulator(rotationY).trigger();
2702 addModulator(rotationZ).trigger();
2703
2704
2705 addModulator(sphere1Z).trigger();
2706 addModulator(sphere2Z).trigger();
2707 addModulator(phaseLFO).trigger();
2708
2709 addModulator(heightMod).trigger();
2710 }
2711
2712 public void onParameterChanged(LXParameter parameter) {
2713 if (parameter == phaseParam) {
2714 phaseLFO.setDuration(5000 - 4500 * parameter.getValuef());
2715 }
2716 }
2717
2718 int beat = 0;
2719 float prevRamp = 0;
2720 public void run(double deltaMs) {
2721
2722 // Sync to the beat
2723 float ramp = (float)lx.tempo.ramp();
2724 if (ramp < prevRamp) {
2725 beat = (beat + 1) % 4;
2726 }
2727 prevRamp = ramp;
2728 float phase = phaseLFO.getValuef();
2729
2730 float crazy_factor = crazyParam.getValuef() / 0.2f;
2731 projection.reset(model)
2732 .rotate(rotationZ.getValuef() * crazy_factor, 0, 1, 0)
2733 .rotate(rotationX.getValuef() * crazy_factor, 0, 0, 1)
2734 .rotate(rotationY.getValuef() * crazy_factor, 0, 1, 0);
2735
2736 for (Coord p : projection) {
2737 float x_percentage = (p.x - model.xMin)/modelWidth;
2738
2739 float y_in_range = heightMod.getValuef() * (2*p.y - model.yMax - model.yMin) / modelHeight;
2740 float sin_x = sin(PI / 2 + phase + 2 * PI * x_percentage);
2741
2742 // Color fade near the top of the sin wave
2743 float v1 = max(0, 100 * (1 - 4*abs(sin_x - y_in_range)));
2744
2745 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;
2746 int c = lx.hsb(hue_color, 80, v1);
2747
2748 // Now draw the spheres
2749 for (Sphere s : spheres) {
2750 float phase_x = (s.x - phase / (2 * PI) * modelWidth) % modelWidth;
2751 float x_dist = LXUtils.wrapdistf(p.x, phase_x, modelWidth);
2752
2753 float sphere_z = (s == spheres[0]) ? (s.z + sphere1Z.getValuef()) : (s.z - sphere2Z.getValuef());
2754
2755
2756 float d = sqrt(pow(x_dist, 2) + pow(p.y - s.y, 2) + pow(p.z - sphere_z, 2));
2757
2758 float distance_from_beat = (beat % 2 == 1) ? 1 - ramp : ramp;
2759
2760 min(ramp, 1-ramp);
2761
2762 float r = 40 - pow(distance_from_beat, 0.75f) * 20;
2763
2764 float distance_value = max(0, 1 - max(0, d - r) / 10);
2765 float beat_value = 1.0f;
2766
2767 float value = min(beat_value, distance_value);
2768
2769 float sphere_color = (lx.getBaseHuef() - (1 - hueScale.getValuef()) * d/r * 45) % 360;
2770
2771 c = blendColor(c, lx.hsb((sphere_color + 270) % 360, 60, min(1, value) * 100), ADD);
2772 }
2773 colors[p.index] = c;
2774 }
2775 }
2776 }
2777 class Cathedrals extends SCPattern {
2778
2779 private final BasicParameter xpos = new BasicParameter("XPOS", 0.5f);
2780 private final BasicParameter wid = new BasicParameter("WID", 0.5f);
2781 private final BasicParameter arms = new BasicParameter("ARMS", 0.5f);
2782 private final BasicParameter sat = new BasicParameter("SAT", 0.5f);
2783 private GraphicEQ eq;
2784
2785 Cathedrals(GLucose glucose) {
2786 super(glucose);
2787 addParameter(xpos);
2788 addParameter(wid);
2789 addParameter(arms);
2790 addParameter(sat);
2791 }
2792
2793 protected void onActive() {
2794 if (eq == null) {
2795 eq = new GraphicEQ(lx, 16);
2796 eq.slope.setValue(0.7f);
2797 eq.range.setValue(0.4f);
2798 eq.attack.setValue(0.4f);
2799 eq.release.setValue(0.4f);
2800 addParameter(eq.level);
2801 addParameter(eq.range);
2802 addParameter(eq.attack);
2803 addParameter(eq.release);
2804 addParameter(eq.slope);
2805 }
2806 }
2807
2808
2809 public void run(double deltaMs) {
2810 eq.run(deltaMs);
2811 float bassLevel = eq.getAverageLevel(0, 4);
2812 float trebleLevel = eq.getAverageLevel(8, 6);
2813
2814 float falloff = 100 / (2 + 14*wid.getValuef());
2815 float cx = model.xMin + (model.xMax-model.xMin) * xpos.getValuef();
2816 float barm = 12 + 60*arms.getValuef()*max(0, 2*(bassLevel-0.1f));
2817 float tarm = 12 + 60*arms.getValuef()*max(0, 2*(trebleLevel-0.1f));
2818
2819 float arm = 0;
2820 float middle = 0;
2821
2822 float sf = 100.f / (70 - 69.9f*sat.getValuef());
2823
2824 for (Point p : model.points) {
2825 float d = MAX_FLOAT;
2826 if (p.y > model.cy) {
2827 arm = tarm;
2828 middle = model.yMax * 3/5.f;
2829 } else {
2830 arm = barm;
2831 middle = model.yMax * 1/5.f;
2832 }
2833 if (abs(p.x - cx) < arm) {
2834 d = min(abs(p.x - cx), abs(p.y - middle));
2835 }
2836 colors[p.index] = color(
2837 (lx.getBaseHuef() + .2f*abs(p.y - model.cy)) % 360,
2838 min(100, sf*dist(abs(p.x - cx), p.y, arm, middle)),
2839 max(0, 120 - d*falloff));
2840 }
2841 }
2842 }
2843
2844 class MidiMusic extends SCPattern {
2845
2846 private final Stack<LXLayer> newLayers = new Stack<LXLayer>();
2847
2848 private final Map<Integer, LightUp> lightMap = new HashMap<Integer, LightUp>();
2849 private final List<LightUp> lights = new ArrayList<LightUp>();
2850 private final BasicParameter lightSize = new BasicParameter("SIZE", 0.5f);
2851
2852 private final List<Sweep> sweeps = new ArrayList<Sweep>();
2853
2854 private final LinearEnvelope sparkle = new LinearEnvelope(0, 1, 500);
2855 private boolean sparkleDirection = true;
2856 private float sparkleBright = 100;
2857
2858 private final BasicParameter wave = new BasicParameter("WAVE", 0);
2859
2860 MidiMusic(GLucose glucose) {
2861 super(glucose);
2862 addParameter(lightSize);
2863 addParameter(wave);
2864 addModulator(sparkle).setValue(1);
2865 }
2866
2867 public void onReset() {
2868 for (LightUp light : lights) {
2869 light.noteOff(null);
2870 }
2871 }
2872
2873 class Sweep extends LXLayer {
2874
2875 final LinearEnvelope position = new LinearEnvelope(0, 1, 1000);
2876 float bright = 100;
2877 float falloff = 10;
2878
2879 Sweep() {
2880 addModulator(position);
2881 }
2882
2883 public void run(double deltaMs, int[] colors) {
2884 if (!position.isRunning()) {
2885 return;
2886 }
2887 float posf = position.getValuef();
2888 for (Point p : model.points) {
2889 colors[p.index] = blendColor(colors[p.index], color(
2890 (lx.getBaseHuef() + .2f*abs(p.x - model.cx) + .2f*abs(p.y - model.cy)) % 360,
2891 100,
2892 max(0, bright - posf*100 - falloff*abs(p.y - posf*model.yMax))
2893 ), ADD);
2894 }
2895 }
2896 }
2897
2898 class LightUp extends LXLayer {
2899
2900 private final LinearEnvelope brt = new LinearEnvelope(0, 0, 0);
2901 private final Accelerator yPos = new Accelerator(0, 0, 0);
2902 private float xPos;
2903
2904 LightUp() {
2905 addModulator(brt);
2906 addModulator(yPos);
2907 }
2908
2909 public boolean isAvailable() {
2910 return brt.getValuef() <= 0;
2911 }
2912
2913 public void noteOn(Note note) {
2914 xPos = lerp(0, model.xMax, constrain(0.5f + (note.getPitch() - 60) / 28.f, 0, 1));
2915 yPos.setValue(lerp(20, model.yMax*.72f, note.getVelocity() / 127.f)).stop();
2916 brt.setRangeFromHereTo(lerp(40, 100, note.getVelocity() / 127.f), 20).start();
2917 }
2918
2919 public void noteOff(Note note) {
2920 yPos.setVelocity(0).setAcceleration(-380).start();
2921 brt.setRangeFromHereTo(0, 1000).start();
2922 }
2923
2924 public void run(double deltaMs, int[] colors) {
2925 float bVal = brt.getValuef();
2926 if (bVal <= 0) {
2927 return;
2928 }
2929 float yVal = yPos.getValuef();
2930 for (Point p : model.points) {
2931 float falloff = 6 - 5*lightSize.getValuef();
2932 float b = max(0, bVal - falloff*dist(p.x, p.y, xPos, yVal));
2933 if (b > 0) {
2934 colors[p.index] = blendColor(colors[p.index], lx.hsb(
2935 (lx.getBaseHuef() + .2f*abs(p.x - model.cx) + .2f*abs(p.y - model.cy)) % 360,
2936 100,
2937 b
2938 ), ADD);
2939 }
2940 }
2941 }
2942 }
2943
2944 private LightUp getLight() {
2945 for (LightUp light : lights) {
2946 if (light.isAvailable()) {
2947 return light;
2948 }
2949 }
2950 LightUp newLight = new LightUp();
2951 lights.add(newLight);
2952 synchronized(newLayers) {
2953 newLayers.push(newLight);
2954 }
2955 return newLight;
2956 }
2957
2958 private Sweep getSweep() {
2959 for (Sweep s : sweeps) {
2960 if (!s.position.isRunning()) {
2961 return s;
2962 }
2963 }
2964 Sweep newSweep = new Sweep();
2965 sweeps.add(newSweep);
2966 synchronized(newLayers) {
2967 newLayers.push(newSweep);
2968 }
2969 return newSweep;
2970 }
2971
2972 public synchronized boolean noteOn(Note note) {
2973 if (note.getChannel() == 0) {
2974 LightUp light = getLight();
2975 lightMap.put(note.getPitch(), light);
2976 light.noteOn(note);
2977 } else if (note.getChannel() == 1) {
2978 } else if (note.getChannel() == 9) {
2979 if (note.getVelocity() > 0) {
2980 switch (note.getPitch()) {
2981 case 36:
2982 Sweep s = getSweep();
2983 s.bright = 50 + note.getVelocity() / 127.f * 50;
2984 s.falloff = 20 - note.getVelocity() / 127.f * 17;
2985 s.position.trigger();
2986 break;
2987 case 37:
2988 sparkleBright = note.getVelocity() / 127.f * 100;
2989 sparkleDirection = true;
2990 sparkle.trigger();
2991 break;
2992 case 38:
2993 sparkleBright = note.getVelocity() / 127.f * 100;
2994 sparkleDirection = false;
2995 sparkle.trigger();
2996 break;
2997 case 39:
2998 effects.boom.trigger();
2999 break;
3000 case 40:
3001 effects.flash.trigger();
3002 break;
3003 }
3004 }
3005 }
3006 return true;
3007 }
3008
3009 public synchronized boolean noteOff(Note note) {
3010 if (note.getChannel() == 0) {
3011 LightUp light = lightMap.get(note.getPitch());
3012 if (light != null) {
3013 light.noteOff(note);
3014 }
3015 }
3016 return true;
3017 }
3018
3019 final float[] wval = new float[16];
3020 float wavoff = 0;
3021
3022 public synchronized void run(double deltaMs) {
3023 wavoff += deltaMs * .001f;
3024 for (int i = 0; i < wval.length; ++i) {
3025 wval[i] = model.cy + 0.2f * model.yMax/2.f * sin(wavoff + i / 1.9f);
3026 }
3027 float sparklePos = (sparkleDirection ? sparkle.getValuef() : (1 - sparkle.getValuef())) * (Cube.POINTS_PER_STRIP)/2.f;
3028 float maxBright = sparkleBright * (1 - sparkle.getValuef());
3029 for (Strip s : model.strips) {
3030 int i = 0;
3031 for (Point p : s.points) {
3032 int wavi = (int) constrain(p.x / model.xMax * wval.length, 0, wval.length-1);
3033 float wavb = max(0, wave.getValuef()*100.f - 8.f*abs(p.y - wval[wavi]));
3034 colors[p.index] = color(
3035 (lx.getBaseHuef() + .2f*abs(p.x - model.cx) + .2f*abs(p.y - model.cy)) % 360,
3036 100,
3037 constrain(wavb + max(0, maxBright - 40.f*abs(sparklePos - abs(i - (Cube.POINTS_PER_STRIP-1)/2.f))), 0, 100)
3038 );
3039 ++i;
3040 }
3041 }
3042
3043 if (!newLayers.isEmpty()) {
3044 synchronized(newLayers) {
3045 while (!newLayers.isEmpty()) {
3046 addLayer(newLayers.pop());
3047 }
3048 }
3049 }
3050 }
3051 }
3052
3053 class Pulley extends SCPattern {
3054
3055 final int NUM_DIVISIONS = 16;
3056 private final Accelerator[] gravity = new Accelerator[NUM_DIVISIONS];
3057 private final Click[] delays = new Click[NUM_DIVISIONS];
3058
3059 private final Click reset = new Click(9000);
3060 private boolean isRising = false;
3061
3062 private BasicParameter sz = new BasicParameter("SIZE", 0.5f);
3063 private BasicParameter beatAmount = new BasicParameter("BEAT", 0);
3064
3065 Pulley(GLucose glucose) {
3066 super(glucose);
3067 for (int i = 0; i < NUM_DIVISIONS; ++i) {
3068 addModulator(gravity[i] = new Accelerator(0, 0, 0));
3069 addModulator(delays[i] = new Click(0));
3070 }
3071 addModulator(reset).start();
3072 addParameter(sz);
3073 addParameter(beatAmount);
3074 trigger();
3075
3076 }
3077
3078 private void trigger() {
3079 isRising = !isRising;
3080 int i = 0;
3081 for (Accelerator g : gravity) {
3082 if (isRising) {
3083 g.setSpeed(random(20, 33), 0).start();
3084 } else {
3085 g.setVelocity(0).setAcceleration(-420);
3086 delays[i].setDuration(random(0, 500)).trigger();
3087 }
3088 ++i;
3089 }
3090 }
3091
3092 public void run(double deltaMs) {
3093 if (reset.click()) {
3094 trigger();
3095 }
3096
3097 if (isRising) {
3098 // Fucking A, had to comment this all out because of that bizarre
3099 // Processing bug where some simple loop takes an absurd amount of
3100 // time, must be some pre-processor bug
3101 // for (Accelerator g : gravity) {
3102 // if (g.getValuef() > model.yMax) {
3103 // g.stop();
3104 // } else if (g.getValuef() > model.yMax*.55) {
3105 // if (g.getVelocityf() > 10) {
3106 // g.setAcceleration(-16);
3107 // } else {
3108 // g.setAcceleration(0);
3109 // }
3110 // }
3111 // }
3112 } else {
3113 int j = 0;
3114 for (Click d : delays) {
3115 if (d.click()) {
3116 gravity[j].start();
3117 d.stop();
3118 }
3119 ++j;
3120 }
3121 for (Accelerator g : gravity) {
3122 if (g.getValuef() < 0) {
3123 g.setValue(-g.getValuef());
3124 g.setVelocity(-g.getVelocityf() * random(0.74f, 0.84f));
3125 }
3126 }
3127 }
3128
3129 // A little silliness to test the grid API
3130 if (midiEngine != null && midiEngine.getFocusedPattern() == this) {
3131 for (int i = 0; i < 5; ++i) {
3132 for (int j = 0; j < 8; ++j) {
3133 int gi = (int) constrain(j * NUM_DIVISIONS / 8, 0, NUM_DIVISIONS-1);
3134 float b = 1 - 4.f*abs((6-i)/6.f - gravity[gi].getValuef() / model.yMax);
3135 midiEngine.grid.setState(i, j, (b < 0) ? 0 : 3);
3136 }
3137 }
3138 }
3139
3140 float fPos = 1 - lx.tempo.rampf();
3141 if (fPos < .2f) {
3142 fPos = .2f + 4 * (.2f - fPos);
3143 }
3144 float falloff = 100.f / (3 + sz.getValuef() * 36 + fPos * beatAmount.getValuef()*48);
3145 for (Point p : model.points) {
3146 int gi = (int) constrain((p.x - model.xMin) * NUM_DIVISIONS / (model.xMax - model.xMin), 0, NUM_DIVISIONS-1);
3147 colors[p.index] = lx.hsb(
3148 (lx.getBaseHuef() + abs(p.x - model.cx)*.8f + p.y*.4f) % 360,
3149 constrain(130 - p.y*.8f, 0, 100),
3150 max(0, 100 - abs(p.y - gravity[gi].getValuef())*falloff)
3151 );
3152 }
3153 }
3154 }
3155
3156 class ViolinWave extends SCPattern {
3157
3158 BasicParameter level = new BasicParameter("LVL", 0.45f);
3159 BasicParameter range = new BasicParameter("RNG", 0.5f);
3160 BasicParameter edge = new BasicParameter("EDG", 0.5f);
3161 BasicParameter release = new BasicParameter("RLS", 0.5f);
3162 BasicParameter speed = new BasicParameter("SPD", 0.5f);
3163 BasicParameter amp = new BasicParameter("AMP", 0.25f);
3164 BasicParameter period = new BasicParameter("WAVE", 0.5f);
3165 BasicParameter pSize = new BasicParameter("PSIZE", 0.5f);
3166 BasicParameter pSpeed = new BasicParameter("PSPD", 0.5f);
3167 BasicParameter pDensity = new BasicParameter("PDENS", 0.25f);
3168
3169 LinearEnvelope dbValue = new LinearEnvelope(0, 0, 10);
3170
3171 ViolinWave(GLucose glucose) {
3172 super(glucose);
3173 addParameter(level);
3174 addParameter(edge);
3175 addParameter(range);
3176 addParameter(release);
3177 addParameter(speed);
3178 addParameter(amp);
3179 addParameter(period);
3180 addParameter(pSize);
3181 addParameter(pSpeed);
3182 addParameter(pDensity);
3183
3184 addModulator(dbValue);
3185 }
3186
3187 final List<Particle> particles = new ArrayList<Particle>();
3188
3189 class Particle {
3190
3191 LinearEnvelope x = new LinearEnvelope(0, 0, 0);
3192 LinearEnvelope y = new LinearEnvelope(0, 0, 0);
3193
3194 Particle() {
3195 addModulator(x);
3196 addModulator(y);
3197 }
3198
3199 public Particle trigger(boolean direction) {
3200 float xInit = random(model.xMin, model.xMax);
3201 float time = 3000 - 2500*pSpeed.getValuef();
3202 x.setRange(xInit, xInit + random(-40, 40), time).trigger();
3203 y.setRange(model.cy + 10, direction ? model.yMax + 50 : model.yMin - 50, time).trigger();
3204 return this;
3205 }
3206
3207 public boolean isActive() {
3208 return x.isRunning() || y.isRunning();
3209 }
3210
3211 public void run(double deltaMs) {
3212 if (!isActive()) {
3213 return;
3214 }
3215
3216 float pFalloff = (30 - 27*pSize.getValuef());
3217 for (Point p : model.points) {
3218 float b = 100 - pFalloff * (abs(p.x - x.getValuef()) + abs(p.y - y.getValuef()));
3219 if (b > 0) {
3220 colors[p.index] = blendColor(colors[p.index], lx.hsb(
3221 lx.getBaseHuef(), 20, b
3222 ), ADD);
3223 }
3224 }
3225 }
3226 }
3227
3228 float[] centers = new float[30];
3229 double accum = 0;
3230 boolean rising = true;
3231
3232 public void fireParticle(boolean direction) {
3233 boolean gotOne = false;
3234 for (Particle p : particles) {
3235 if (!p.isActive()) {
3236 p.trigger(direction);
3237 return;
3238 }
3239 }
3240 particles.add(new Particle().trigger(direction));
3241 }
3242
3243 public void run(double deltaMs) {
3244 accum += deltaMs / (1000.f - 900.f*speed.getValuef());
3245 for (int i = 0; i < centers.length; ++i) {
3246 centers[i] = model.cy + 30*amp.getValuef()*sin((float) (accum + (i-centers.length/2.f)/(1.f + 9.f*period.getValuef())));
3247 }
3248
3249 float zeroDBReference = pow(10, (50 - 190*level.getValuef())/20.f);
3250 float dB = 20*GraphicEQ.log10(lx.audioInput().mix.level() / zeroDBReference);
3251 if (dB > dbValue.getValuef()) {
3252 rising = true;
3253 dbValue.setRangeFromHereTo(dB, 10).trigger();
3254 } else {
3255 if (rising) {
3256 for (int j = 0; j < pDensity.getValuef()*3; ++j) {
3257 fireParticle(true);
3258 fireParticle(false);
3259 }
3260 }
3261 rising = false;
3262 dbValue.setRangeFromHereTo(max(dB, -96), 50 + 1000*release.getValuef()).trigger();
3263 }
3264 float edg = 1 + edge.getValuef() * 40;
3265 float rng = (78 - 64 * range.getValuef()) / (model.yMax - model.cy);
3266 float val = max(2, dbValue.getValuef());
3267
3268 for (Point p : model.points) {
3269 int ci = (int) lerp(0, centers.length-1, (p.x - model.xMin) / (model.xMax - model.xMin));
3270 float rFactor = 1.0f - 0.9f * abs(p.x - model.cx) / (model.xMax - model.cx);
3271 colors[p.index] = lx.hsb(
3272 (lx.getBaseHuef() + abs(p.x - model.cx)) % 360,
3273 min(100, 20 + 8*abs(p.y - centers[ci])),
3274 constrain(edg*(val*rFactor - rng * abs(p.y-centers[ci])), 0, 100)
3275 );
3276 }
3277
3278 for (Particle p : particles) {
3279 p.run(deltaMs);
3280 }
3281 }
3282 }
3283
3284 class BouncyBalls extends SCPattern {
3285
3286 static final int NUM_BALLS = 6;
3287
3288 class BouncyBall {
3289
3290 Accelerator yPos;
3291 TriangleLFO xPos = new TriangleLFO(0, model.xMax, random(8000, 19000));
3292 float zPos;
3293
3294 BouncyBall(int i) {
3295 addModulator(xPos.setBasis(random(0, TWO_PI)).start());
3296 addModulator(yPos = new Accelerator(0, 0, 0));
3297 zPos = lerp(model.zMin, model.zMax, (i+2.f) / (NUM_BALLS + 4.f));
3298 }
3299
3300 public void bounce(float midiVel) {
3301 float v = 100 + 8*midiVel;
3302 yPos.setSpeed(v, getAccel(v, 60 / lx.tempo.bpmf())).start();
3303 }
3304
3305 public float getAccel(float v, float oneBeat) {
3306 return -2*v / oneBeat;
3307 }
3308
3309 public void run(double deltaMs) {
3310 float flrLevel = flr.getValuef() * model.xMax/2.f;
3311 if (yPos.getValuef() < flrLevel) {
3312 if (yPos.getVelocity() < -50) {
3313 yPos.setValue(2*flrLevel-yPos.getValuef());
3314 float v = -yPos.getVelocityf() * bounce.getValuef();
3315 yPos.setSpeed(v, getAccel(v, 60 / lx.tempo.bpmf()));
3316 } else {
3317 yPos.setValue(flrLevel).stop();
3318 }
3319 }
3320 float falloff = 130.f / (12 + blobSize.getValuef() * 36);
3321 float xv = xPos.getValuef();
3322 float yv = yPos.getValuef();
3323
3324 for (Point p : model.points) {
3325 float d = sqrt((p.x-xv)*(p.x-xv) + (p.y-yv)*(p.y-yv) + .1f*(p.z-zPos)*(p.z-zPos));
3326 float b = constrain(130 - falloff*d, 0, 100);
3327 if (b > 0) {
3328 colors[p.index] = blendColor(colors[p.index], lx.hsb(
3329 (lx.getBaseHuef() + p.y*.5f + abs(model.cx - p.x) * .5f) % 360,
3330 max(0, 100 - .45f*(p.y - flrLevel)),
3331 b
3332 ), ADD);
3333 }
3334 }
3335 }
3336 }
3337
3338 final BouncyBall[] balls = new BouncyBall[NUM_BALLS];
3339
3340 final BasicParameter bounce = new BasicParameter("BNC", .8f);
3341 final BasicParameter flr = new BasicParameter("FLR", 0);
3342 final BasicParameter blobSize = new BasicParameter("SIZE", 0.5f);
3343
3344 BouncyBalls(GLucose glucose) {
3345 super(glucose);
3346 for (int i = 0; i < balls.length; ++i) {
3347 balls[i] = new BouncyBall(i);
3348 }
3349 addParameter(bounce);
3350 addParameter(flr);
3351 addParameter(blobSize);
3352 }
3353
3354 public void run(double deltaMs) {
3355 setColors(0xff000000);
3356 for (BouncyBall b : balls) {
3357 b.run(deltaMs);
3358 }
3359 }
3360
3361 public boolean noteOn(Note note) {
3362 int pitch = (note.getPitch() + note.getChannel()) % NUM_BALLS;
3363 balls[pitch].bounce(note.getVelocity());
3364 return true;
3365 }
3366 }
3367
3368 class SpaceTime extends SCPattern {
3369
3370 SinLFO pos = new SinLFO(0, 1, 3000);
3371 SinLFO rate = new SinLFO(1000, 9000, 13000);
3372 SinLFO falloff = new SinLFO(10, 70, 5000);
3373 float angle = 0;
3374
3375 BasicParameter rateParameter = new BasicParameter("RATE", 0.5f);
3376 BasicParameter sizeParameter = new BasicParameter("SIZE", 0.5f);
3377
3378
3379 public SpaceTime(GLucose glucose) {
3380 super(glucose);
3381
3382 addModulator(pos).trigger();
3383 addModulator(rate).trigger();
3384 addModulator(falloff).trigger();
3385 pos.modulateDurationBy(rate);
3386 addParameter(rateParameter);
3387 addParameter(sizeParameter);
3388 }
3389
3390 public void onParameterChanged(LXParameter parameter) {
3391 if (parameter == rateParameter) {
3392 rate.stop().setValue(9000 - 8000*parameter.getValuef());
3393 } else if (parameter == sizeParameter) {
3394 falloff.stop().setValue(70 - 60*parameter.getValuef());
3395 }
3396 }
3397
3398 public void run(double deltaMs) {
3399 angle += deltaMs * 0.0007f;
3400 float sVal1 = model.strips.size() * (0.5f + 0.5f*sin(angle));
3401 float sVal2 = model.strips.size() * (0.5f + 0.5f*cos(angle));
3402
3403 float pVal = pos.getValuef();
3404 float fVal = falloff.getValuef();
3405
3406 int s = 0;
3407 for (Strip strip : model.strips) {
3408 int i = 0;
3409 for (Point p : strip.points) {
3410 colors[p.index] = lx.hsb(
3411 (lx.getBaseHuef() + 360 - p.x*.2f + p.y * .3f) % 360,
3412 constrain(.4f * min(abs(s - sVal1), abs(s - sVal2)), 20, 100),
3413 max(0, 100 - fVal*abs(i - pVal*(strip.metrics.numPoints - 1)))
3414 );
3415 ++i;
3416 }
3417 ++s;
3418 }
3419 }
3420 }
3421
3422 class Swarm extends SCPattern {
3423
3424 SawLFO offset = new SawLFO(0, 1, 1000);
3425 SinLFO rate = new SinLFO(350, 1200, 63000);
3426 SinLFO falloff = new SinLFO(15, 50, 17000);
3427 SinLFO fX = new SinLFO(0, model.xMax, 19000);
3428 SinLFO fY = new SinLFO(0, model.yMax, 11000);
3429 SinLFO hOffX = new SinLFO(0, model.xMax, 13000);
3430
3431 public Swarm(GLucose glucose) {
3432 super(glucose);
3433
3434 addModulator(offset).trigger();
3435 addModulator(rate).trigger();
3436 addModulator(falloff).trigger();
3437 addModulator(fX).trigger();
3438 addModulator(fY).trigger();
3439 addModulator(hOffX).trigger();
3440 offset.modulateDurationBy(rate);
3441 }
3442
3443 public float modDist(float v1, float v2, float mod) {
3444 v1 = v1 % mod;
3445 v2 = v2 % mod;
3446 if (v2 > v1) {
3447 return min(v2-v1, v1+mod-v2);
3448 }
3449 else {
3450 return min(v1-v2, v2+mod-v1);
3451 }
3452 }
3453
3454 public void run(double deltaMs) {
3455 float s = 0;
3456 for (Strip strip : model.strips ) {
3457 int i = 0;
3458 for (Point p : strip.points) {
3459 float fV = max(-1, 1 - dist(p.x/2.f, p.y, fX.getValuef()/2.f, fY.getValuef()) / 64.f);
3460 colors[p.index] = lx.hsb(
3461 (lx.getBaseHuef() + 0.3f * abs(p.x - hOffX.getValuef())) % 360,
3462 constrain(80 + 40 * fV, 0, 100),
3463 constrain(100 - (30 - fV * falloff.getValuef()) * modDist(i + (s*63)%61, offset.getValuef() * strip.metrics.numPoints, strip.metrics.numPoints), 0, 100)
3464 );
3465 ++i;
3466 }
3467 ++s;
3468 }
3469 }
3470 }
3471
3472 class SwipeTransition extends SCTransition {
3473
3474 final BasicParameter bleed = new BasicParameter("WIDTH", 0.5f);
3475
3476 SwipeTransition(GLucose glucose) {
3477 super(glucose);
3478 setDuration(5000);
3479 addParameter(bleed);
3480 }
3481
3482 public void computeBlend(int[] c1, int[] c2, double progress) {
3483 float bleedf = 10 + bleed.getValuef() * 200.f;
3484 float xPos = (float) (-bleedf + progress * (model.xMax + bleedf));
3485 for (Point p : model.points) {
3486 float d = (p.x - xPos) / bleedf;
3487 if (d < 0) {
3488 colors[p.index] = c2[p.index];
3489 } else if (d > 1) {
3490 colors[p.index] = c1[p.index];
3491 } else {
3492 colors[p.index] = lerpColor(c2[p.index], c1[p.index], d, RGB);
3493 }
3494 }
3495 }
3496 }
3497
3498 abstract class BlendTransition extends SCTransition {
3499
3500 final int blendType;
3501
3502 BlendTransition(GLucose glucose, int blendType) {
3503 super(glucose);
3504 this.blendType = blendType;
3505 }
3506
3507 public void computeBlend(int[] c1, int[] c2, double progress) {
3508 if (progress < 0.5f) {
3509 for (int i = 0; i < c1.length; ++i) {
3510 colors[i] = lerpColor(
3511 c1[i],
3512 blendColor(c1[i], c2[i], blendType),
3513 (float) (2.f*progress),
3514 RGB);
3515 }
3516 } else {
3517 for (int i = 0; i < c1.length; ++i) {
3518 colors[i] = lerpColor(
3519 c2[i],
3520 blendColor(c1[i], c2[i], blendType),
3521 (float) (2.f*(1.f - progress)),
3522 RGB);
3523 }
3524 }
3525 }
3526 }
3527
3528 class MultiplyTransition extends BlendTransition {
3529 MultiplyTransition(GLucose glucose) {
3530 super(glucose, MULTIPLY);
3531 }
3532 }
3533
3534 class ScreenTransition extends BlendTransition {
3535 ScreenTransition(GLucose glucose) {
3536 super(glucose, SCREEN);
3537 }
3538 }
3539
3540 class BurnTransition extends BlendTransition {
3541 BurnTransition(GLucose glucose) {
3542 super(glucose, BURN);
3543 }
3544 }
3545
3546 class DodgeTransition extends BlendTransition {
3547 DodgeTransition(GLucose glucose) {
3548 super(glucose, DODGE);
3549 }
3550 }
3551
3552 class OverlayTransition extends BlendTransition {
3553 OverlayTransition(GLucose glucose) {
3554 super(glucose, OVERLAY);
3555 }
3556 }
3557
3558 class AddTransition extends BlendTransition {
3559 AddTransition(GLucose glucose) {
3560 super(glucose, ADD);
3561 }
3562 }
3563
3564 class SubtractTransition extends BlendTransition {
3565 SubtractTransition(GLucose glucose) {
3566 super(glucose, SUBTRACT);
3567 }
3568 }
3569
3570 class SoftLightTransition extends BlendTransition {
3571 SoftLightTransition(GLucose glucose) {
3572 super(glucose, SOFT_LIGHT);
3573 }
3574 }
3575
3576 class BassPod extends SCPattern {
3577
3578 private GraphicEQ eq = null;
3579
3580 private final BasicParameter clr = new BasicParameter("CLR", 0.5f);
3581
3582 public BassPod(GLucose glucose) {
3583 super(glucose);
3584 addParameter(clr);
3585 }
3586
3587 protected void onActive() {
3588 if (eq == null) {
3589 eq = new GraphicEQ(lx, 16);
3590 eq.range.setValue(0.4f);
3591 eq.level.setValue(0.4f);
3592 eq.slope.setValue(0.6f);
3593 addParameter(eq.level);
3594 addParameter(eq.range);
3595 addParameter(eq.attack);
3596 addParameter(eq.release);
3597 addParameter(eq.slope);
3598 }
3599 }
3600
3601 public void run(double deltaMs) {
3602 eq.run(deltaMs);
3603
3604 float bassLevel = eq.getAverageLevel(0, 5);
3605
3606 float satBase = bassLevel*480*clr.getValuef();
3607
3608 for (Point p : model.points) {
3609 int avgIndex = (int) constrain(1 + abs(p.x-model.cx)/(model.cx)*(eq.numBands-5), 0, eq.numBands-5);
3610 float value = 0;
3611 for (int i = avgIndex; i < avgIndex + 5; ++i) {
3612 value += eq.getLevel(i);
3613 }
3614 value /= 5.f;
3615
3616 float b = constrain(8 * (value*model.yMax - abs(p.y-model.yMax/2.f)), 0, 100);
3617 colors[p.index] = lx.hsb(
3618 (lx.getBaseHuef() + abs(p.y - model.cy) + abs(p.x - model.cx)) % 360,
3619 constrain(satBase - .6f*dist(p.x, p.y, model.cx, model.cy), 0, 100),
3620 b
3621 );
3622 }
3623 }
3624 }
3625
3626
3627 class CubeEQ extends SCPattern {
3628
3629 private GraphicEQ eq = null;
3630
3631 private final BasicParameter edge = new BasicParameter("EDGE", 0.5f);
3632 private final BasicParameter clr = new BasicParameter("CLR", 0.5f);
3633 private final BasicParameter blockiness = new BasicParameter("BLK", 0.5f);
3634
3635 public CubeEQ(GLucose glucose) {
3636 super(glucose);
3637 }
3638
3639 protected void onActive() {
3640 if (eq == null) {
3641 eq = new GraphicEQ(lx, 16);
3642 addParameter(eq.level);
3643 addParameter(eq.range);
3644 addParameter(eq.attack);
3645 addParameter(eq.release);
3646 addParameter(eq.slope);
3647 addParameter(edge);
3648 addParameter(clr);
3649 addParameter(blockiness);
3650 }
3651 }
3652
3653 public void run(double deltaMs) {
3654 eq.run(deltaMs);
3655
3656 float edgeConst = 2 + 30*edge.getValuef();
3657 float clrConst = 1.1f + clr.getValuef();
3658
3659 for (Point p : model.points) {
3660 float avgIndex = constrain(2 + p.x / model.xMax * (eq.numBands-4), 0, eq.numBands-4);
3661 int avgFloor = (int) avgIndex;
3662
3663 float leftVal = eq.getLevel(avgFloor);
3664 float rightVal = eq.getLevel(avgFloor+1);
3665 float smoothValue = lerp(leftVal, rightVal, avgIndex-avgFloor);
3666
3667 float chunkyValue = (
3668 eq.getLevel(avgFloor/4*4) +
3669 eq.getLevel(avgFloor/4*4 + 1) +
3670 eq.getLevel(avgFloor/4*4 + 2) +
3671 eq.getLevel(avgFloor/4*4 + 3)
3672 ) / 4.f;
3673
3674 float value = lerp(smoothValue, chunkyValue, blockiness.getValuef());
3675
3676 float b = constrain(edgeConst * (value*model.yMax - p.y), 0, 100);
3677 colors[p.index] = lx.hsb(
3678 (480 + lx.getBaseHuef() - min(clrConst*p.y, 120)) % 360,
3679 100,
3680 b
3681 );
3682 }
3683 }
3684 }
3685
3686 class BoomEffect extends SCEffect {
3687
3688 final BasicParameter falloff = new BasicParameter("WIDTH", 0.5f);
3689 final BasicParameter speed = new BasicParameter("SPD", 0.5f);
3690 final BasicParameter bright = new BasicParameter("BRT", 1.0f);
3691 final BasicParameter sat = new BasicParameter("SAT", 0.2f);
3692 List<Layer> layers = new ArrayList<Layer>();
3693 final float maxr = sqrt(model.xMax*model.xMax + model.yMax*model.yMax + model.zMax*model.zMax) + 10;
3694
3695 class Layer {
3696 LinearEnvelope boom = new LinearEnvelope(-40, 500, 1300);
3697
3698 Layer() {
3699 addModulator(boom);
3700 trigger();
3701 }
3702
3703 public void trigger() {
3704 float falloffv = falloffv();
3705 boom.setRange(-100 / falloffv, maxr + 100/falloffv, 4000 - speed.getValuef() * 3300);
3706 boom.trigger();
3707 }
3708
3709 public void apply(int[] colors) {
3710 float brightv = 100 * bright.getValuef();
3711 float falloffv = falloffv();
3712 float satv = sat.getValuef() * 100;
3713 float huev = lx.getBaseHuef();
3714 for (Point p : model.points) {
3715 colors[p.index] = blendColor(
3716 colors[p.index],
3717 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)),
3718 ADD);
3719 }
3720 }
3721 }
3722
3723 BoomEffect(GLucose glucose) {
3724 super(glucose, true);
3725 addParameter(falloff);
3726 addParameter(speed);
3727 addParameter(bright);
3728 addParameter(sat);
3729 }
3730
3731 public void onEnable() {
3732 for (Layer l : layers) {
3733 if (!l.boom.isRunning()) {
3734 l.trigger();
3735 return;
3736 }
3737 }
3738 layers.add(new Layer());
3739 }
3740
3741 private float falloffv() {
3742 return 20 - 19 * falloff.getValuef();
3743 }
3744
3745 public void onTrigger() {
3746 onEnable();
3747 }
3748
3749 public void apply(int[] colors) {
3750 for (Layer l : layers) {
3751 if (l.boom.isRunning()) {
3752 l.apply(colors);
3753 }
3754 }
3755 }
3756 }
3757
3758 public class PianoKeyPattern extends SCPattern {
3759
3760 final LinearEnvelope[] cubeBrt;
3761 final SinLFO base[];
3762 final BasicParameter attack = new BasicParameter("ATK", 0.1f);
3763 final BasicParameter release = new BasicParameter("REL", 0.5f);
3764 final BasicParameter level = new BasicParameter("AMB", 0.6f);
3765
3766 PianoKeyPattern(GLucose glucose) {
3767 super(glucose);
3768
3769 addParameter(attack);
3770 addParameter(release);
3771 addParameter(level);
3772 cubeBrt = new LinearEnvelope[model.cubes.size() / 4];
3773 for (int i = 0; i < cubeBrt.length; ++i) {
3774 addModulator(cubeBrt[i] = new LinearEnvelope(0, 0, 100));
3775 }
3776 base = new SinLFO[model.cubes.size() / 12];
3777 for (int i = 0; i < base.length; ++i) {
3778 addModulator(base[i] = new SinLFO(0, 1, 7000 + 1000*i)).trigger();
3779 }
3780 }
3781
3782 private float getAttackTime() {
3783 return 15 + attack.getValuef()*attack.getValuef() * 2000;
3784 }
3785
3786 private float getReleaseTime() {
3787 return 15 + release.getValuef() * 3000;
3788 }
3789
3790 private LinearEnvelope getEnvelope(int index) {
3791 return cubeBrt[index % cubeBrt.length];
3792 }
3793
3794 private SinLFO getBase(int index) {
3795 return base[index % base.length];
3796 }
3797
3798 public boolean noteOn(Note note) {
3799 LinearEnvelope env = getEnvelope(note.getPitch());
3800 env.setEndVal(min(1, env.getValuef() + (note.getVelocity() / 127.f)), getAttackTime()).start();
3801 return true;
3802 }
3803
3804 public boolean noteOff(Note note) {
3805 getEnvelope(note.getPitch()).setEndVal(0, getReleaseTime()).start();
3806 return true;
3807 }
3808
3809 public void run(double deltaMs) {
3810 int i = 0;
3811 float huef = lx.getBaseHuef();
3812 float levelf = level.getValuef();
3813 for (Cube c : model.cubes) {
3814 float v = max(getBase(i).getValuef() * levelf/4.f, getEnvelope(i++).getValuef());
3815 setColor(c, lx.hsb(
3816 (huef + 20*v + abs(c.cx-model.xMax/2.f)*.3f + c.cy) % 360,
3817 min(100, 120*v),
3818 100*v
3819 ));
3820 }
3821 }
3822 }
3823
3824 class CrossSections extends SCPattern {
3825
3826 final SinLFO x = new SinLFO(0, model.xMax, 5000);
3827 final SinLFO y = new SinLFO(0, model.yMax, 6000);
3828 final SinLFO z = new SinLFO(0, model.zMax, 7000);
3829
3830 final BasicParameter xw = new BasicParameter("XWID", 0.3f);
3831 final BasicParameter yw = new BasicParameter("YWID", 0.3f);
3832 final BasicParameter zw = new BasicParameter("ZWID", 0.3f);
3833 final BasicParameter xr = new BasicParameter("XRAT", 0.7f);
3834 final BasicParameter yr = new BasicParameter("YRAT", 0.6f);
3835 final BasicParameter zr = new BasicParameter("ZRAT", 0.5f);
3836 final BasicParameter xl = new BasicParameter("XLEV", 1);
3837 final BasicParameter yl = new BasicParameter("YLEV", 1);
3838 final BasicParameter zl = new BasicParameter("ZLEV", 0.5f);
3839
3840
3841 CrossSections(GLucose glucose) {
3842 super(glucose);
3843 addModulator(x).trigger();
3844 addModulator(y).trigger();
3845 addModulator(z).trigger();
3846 addParams();
3847 }
3848
3849 protected void addParams() {
3850 addParameter(xr);
3851 addParameter(yr);
3852 addParameter(zr);
3853 addParameter(xw);
3854 addParameter(xl);
3855 addParameter(yl);
3856 addParameter(zl);
3857 addParameter(yw);
3858 addParameter(zw);
3859 }
3860
3861 public void onParameterChanged(LXParameter p) {
3862 if (p == xr) {
3863 x.setDuration(10000 - 8800*p.getValuef());
3864 } else if (p == yr) {
3865 y.setDuration(10000 - 9000*p.getValuef());
3866 } else if (p == zr) {
3867 z.setDuration(10000 - 9000*p.getValuef());
3868 }
3869 }
3870
3871 float xv, yv, zv;
3872
3873 protected void updateXYZVals() {
3874 xv = x.getValuef();
3875 yv = y.getValuef();
3876 zv = z.getValuef();
3877 }
3878
3879 public void run(double deltaMs) {
3880 updateXYZVals();
3881
3882 float xlv = 100*xl.getValuef();
3883 float ylv = 100*yl.getValuef();
3884 float zlv = 100*zl.getValuef();
3885
3886 float xwv = 100.f / (10 + 40*xw.getValuef());
3887 float ywv = 100.f / (10 + 40*yw.getValuef());
3888 float zwv = 100.f / (10 + 40*zw.getValuef());
3889
3890 for (Point p : model.points) {
3891 int c = 0;
3892 c = blendColor(c, lx.hsb(
3893 (lx.getBaseHuef() + p.x/10 + p.y/3) % 360,
3894 constrain(140 - 1.1f*abs(p.x - model.xMax/2.f), 0, 100),
3895 max(0, xlv - xwv*abs(p.x - xv))
3896 ), ADD);
3897 c = blendColor(c, lx.hsb(
3898 (lx.getBaseHuef() + 80 + p.y/10) % 360,
3899 constrain(140 - 2.2f*abs(p.y - model.yMax/2.f), 0, 100),
3900 max(0, ylv - ywv*abs(p.y - yv))
3901 ), ADD);
3902 c = blendColor(c, lx.hsb(
3903 (lx.getBaseHuef() + 160 + p.z / 10 + p.y/2) % 360,
3904 constrain(140 - 2.2f*abs(p.z - model.zMax/2.f), 0, 100),
3905 max(0, zlv - zwv*abs(p.z - zv))
3906 ), ADD);
3907 colors[p.index] = c;
3908 }
3909 }
3910 }
3911
3912 class Blinders extends SCPattern {
3913
3914 final SinLFO[] m;
3915 final TriangleLFO r;
3916 final SinLFO s;
3917 final TriangleLFO hs;
3918
3919 public Blinders(GLucose glucose) {
3920 super(glucose);
3921 m = new SinLFO[12];
3922 for (int i = 0; i < m.length; ++i) {
3923 addModulator(m[i] = new SinLFO(0.5f, 120, (120000.f / (3+i)))).trigger();
3924 }
3925 addModulator(r = new TriangleLFO(9000, 15000, 29000)).trigger();
3926 addModulator(s = new SinLFO(-20, 275, 11000)).trigger();
3927 addModulator(hs = new TriangleLFO(0.1f, 0.5f, 15000)).trigger();
3928 s.modulateDurationBy(r);
3929 }
3930
3931 public void run(double deltaMs) {
3932 float hv = lx.getBaseHuef();
3933 int si = 0;
3934 for (Strip strip : model.strips) {
3935 int i = 0;
3936 float mv = m[si % m.length].getValuef();
3937 for (Point p : strip.points) {
3938 colors[p.index] = lx.hsb(
3939 (hv + p.z + p.y*hs.getValuef()) % 360,
3940 min(100, abs(p.x - s.getValuef())/2.f),
3941 max(0, 100 - mv/2.f - mv * abs(i - (strip.metrics.length-1)/2.f))
3942 );
3943 ++i;
3944 }
3945 ++si;
3946 }
3947 }
3948 }
3949
3950 class Psychedelia extends SCPattern {
3951
3952 final int NUM = 3;
3953 SinLFO m = new SinLFO(-0.5f, NUM-0.5f, 9000);
3954 SinLFO s = new SinLFO(-20, 147, 11000);
3955 TriangleLFO h = new TriangleLFO(0, 240, 19000);
3956 SinLFO c = new SinLFO(-.2f, .8f, 31000);
3957
3958 Psychedelia(GLucose glucose) {
3959 super(glucose);
3960 addModulator(m).trigger();
3961 addModulator(s).trigger();
3962 addModulator(h).trigger();
3963 addModulator(c).trigger();
3964 }
3965
3966 public void run(double deltaMs) {
3967 float huev = h.getValuef();
3968 float cv = c.getValuef();
3969 float sv = s.getValuef();
3970 float mv = m.getValuef();
3971 int i = 0;
3972 for (Strip strip : model.strips) {
3973 for (Point p : strip.points) {
3974 colors[p.index] = lx.hsb(
3975 (huev + i*constrain(cv, 0, 2) + p.z/2.f + p.x/4.f) % 360,
3976 min(100, abs(p.y-sv)),
3977 max(0, 100 - 50*abs((i%NUM) - mv))
3978 );
3979 }
3980 ++i;
3981 }
3982 }
3983 }
3984
3985 class AskewPlanes extends SCPattern {
3986
3987 class Plane {
3988 private final SinLFO a;
3989 private final SinLFO b;
3990 private final SinLFO c;
3991 float av = 1;
3992 float bv = 1;
3993 float cv = 1;
3994 float denom = 0.1f;
3995
3996 Plane(int i) {
3997 addModulator(a = new SinLFO(-1, 1, 4000 + 1029*i)).trigger();
3998 addModulator(b = new SinLFO(-1, 1, 11000 - 1104*i)).trigger();
3999 addModulator(c = new SinLFO(-50, 50, 4000 + 1000*i * ((i % 2 == 0) ? 1 : -1))).trigger();
4000 }
4001
4002 public void run(double deltaMs) {
4003 av = a.getValuef();
4004 bv = b.getValuef();
4005 cv = c.getValuef();
4006 denom = sqrt(av*av + bv*bv);
4007 }
4008 }
4009
4010 final Plane[] planes;
4011 final int NUM_PLANES = 3;
4012
4013 AskewPlanes(GLucose glucose) {
4014 super(glucose);
4015 planes = new Plane[NUM_PLANES];
4016 for (int i = 0; i < planes.length; ++i) {
4017 planes[i] = new Plane(i);
4018 }
4019 }
4020
4021 public void run(double deltaMs) {
4022 float huev = lx.getBaseHuef();
4023
4024 // This is super fucking bizarre. But if this is a for loop, the framerate
4025 // tanks to like 30FPS, instead of 60. Call them manually and it works fine.
4026 // Doesn't make ANY sense... there must be some weird side effect going on
4027 // with the Processing internals perhaps?
4028 // for (Plane plane : planes) {
4029 // plane.run(deltaMs);
4030 // }
4031 planes[0].run(deltaMs);
4032 planes[1].run(deltaMs);
4033 planes[2].run(deltaMs);
4034
4035 for (Point p : model.points) {
4036 float d = MAX_FLOAT;
4037 for (Plane plane : planes) {
4038 if (plane.denom != 0) {
4039 d = min(d, abs(plane.av*(p.x-model.cx) + plane.bv*(p.y-model.cy) + plane.cv) / plane.denom);
4040 }
4041 }
4042 colors[p.index] = lx.hsb(
4043 (huev + abs(p.x-model.cx)*.3f + p.y*.8f) % 360,
4044 max(0, 100 - .8f*abs(p.x - model.cx)),
4045 constrain(140 - 10.f*d, 0, 100)
4046 );
4047 }
4048 }
4049 }
4050
4051 class ShiftingPlane extends SCPattern {
4052
4053 final SinLFO a = new SinLFO(-.2f, .2f, 5300);
4054 final SinLFO b = new SinLFO(1, -1, 13300);
4055 final SinLFO c = new SinLFO(-1.4f, 1.4f, 5700);
4056 final SinLFO d = new SinLFO(-10, 10, 9500);
4057
4058 ShiftingPlane(GLucose glucose) {
4059 super(glucose);
4060 addModulator(a).trigger();
4061 addModulator(b).trigger();
4062 addModulator(c).trigger();
4063 addModulator(d).trigger();
4064 }
4065
4066 public void run(double deltaMs) {
4067 float hv = lx.getBaseHuef();
4068 float av = a.getValuef();
4069 float bv = b.getValuef();
4070 float cv = c.getValuef();
4071 float dv = d.getValuef();
4072 float denom = sqrt(av*av + bv*bv + cv*cv);
4073 for (Point p : model.points) {
4074 float d = abs(av*(p.x-model.cx) + bv*(p.y-model.cy) + cv*(p.z-model.cz) + dv) / denom;
4075 colors[p.index] = lx.hsb(
4076 (hv + abs(p.x-model.cx)*.6f + abs(p.y-model.cy)*.9f + abs(p.z - model.cz)) % 360,
4077 constrain(110 - d*6, 0, 100),
4078 constrain(130 - 7*d, 0, 100)
4079 );
4080 }
4081 }
4082 }
4083
4084 class Traktor extends SCPattern {
4085
4086 final int FRAME_WIDTH = 60;
4087
4088 final BasicParameter speed = new BasicParameter("SPD", 0.5f);
4089
4090 private float[] bass = new float[FRAME_WIDTH];
4091 private float[] treble = new float[FRAME_WIDTH];
4092
4093 private int index = 0;
4094 private GraphicEQ eq = null;
4095
4096 public Traktor(GLucose glucose) {
4097 super(glucose);
4098 for (int i = 0; i < FRAME_WIDTH; ++i) {
4099 bass[i] = 0;
4100 treble[i] = 0;
4101 }
4102 addParameter(speed);
4103 }
4104
4105 public void onActive() {
4106 if (eq == null) {
4107 eq = new GraphicEQ(lx, 16);
4108 eq.slope.setValue(0.6f);
4109 eq.level.setValue(0.65f);
4110 eq.range.setValue(0.35f);
4111 eq.release.setValue(0.4f);
4112 addParameter(eq.level);
4113 addParameter(eq.range);
4114 addParameter(eq.attack);
4115 addParameter(eq.release);
4116 addParameter(eq.slope);
4117 }
4118 }
4119
4120 int counter = 0;
4121
4122 public void run(double deltaMs) {
4123 eq.run(deltaMs);
4124
4125 int stepThresh = (int) (40 - 39*speed.getValuef());
4126 counter += deltaMs;
4127 if (counter < stepThresh) {
4128 return;
4129 }
4130 counter = counter % stepThresh;
4131
4132 index = (index + 1) % FRAME_WIDTH;
4133
4134 float rawBass = eq.getAverageLevel(0, 4);
4135 float rawTreble = eq.getAverageLevel(eq.numBands-7, 7);
4136
4137 bass[index] = rawBass * rawBass * rawBass * rawBass;
4138 treble[index] = rawTreble * rawTreble;
4139
4140 for (Point p : model.points) {
4141 int i = (int) constrain((model.xMax - p.x) / model.xMax * FRAME_WIDTH, 0, FRAME_WIDTH-1);
4142 int pos = (index + FRAME_WIDTH - i) % FRAME_WIDTH;
4143
4144 colors[p.index] = lx.hsb(
4145 (360 + lx.getBaseHuef() + .8f*abs(p.x-model.cx)) % 360,
4146 100,
4147 constrain(9 * (bass[pos]*model.cy - abs(p.y - model.cy + 5)), 0, 100)
4148 );
4149 colors[p.index] = blendColor(colors[p.index], lx.hsb(
4150 (400 + lx.getBaseHuef() + .5f*abs(p.x-model.cx)) % 360,
4151 60,
4152 constrain(5 * (treble[pos]*.6f*model.cy - abs(p.y - model.cy)), 0, 100)
4153
4154 ), ADD);
4155 }
4156 }
4157 }
4158
4159 class ColorFuckerEffect extends SCEffect {
4160
4161 final BasicParameter level = new BasicParameter("BRT", 1);
4162 final BasicParameter desat = new BasicParameter("DSAT", 0);
4163 final BasicParameter hueShift = new BasicParameter("HSHFT", 0);
4164 final BasicParameter sharp = new BasicParameter("SHARP", 0);
4165 final BasicParameter soft = new BasicParameter("SOFT", 0);
4166 final BasicParameter mono = new BasicParameter("MONO", 0);
4167 final BasicParameter invert = new BasicParameter("INVERT", 0);
4168
4169
4170 float[] hsb = new float[3];
4171
4172 ColorFuckerEffect(GLucose glucose) {
4173 super(glucose);
4174 addParameter(level);
4175 addParameter(desat);
4176 addParameter(sharp);
4177 addParameter(hueShift);
4178 addParameter(soft);
4179 addParameter(mono);
4180 addParameter(invert);
4181 }
4182
4183 public void apply(int[] colors) {
4184 if (!enabled) {
4185 return;
4186 }
4187 float bMod = level.getValuef();
4188 float sMod = 1 - desat.getValuef();
4189 float hMod = hueShift.getValuef();
4190 float fSharp = 1/(1.0001f-sharp.getValuef());
4191 float fSoft = soft.getValuef();
4192 boolean mon = mono.getValuef() > 0.5f;
4193 boolean ivt = invert.getValuef() > 0.5f;
4194 if (bMod < 1 || sMod < 1 || hMod > 0 || fSharp > 0 || ivt || mon || fSoft > 0) {
4195 for (int i = 0; i < colors.length; ++i) {
4196 lx.RGBtoHSB(colors[i], hsb);
4197 if (mon) {
4198 hsb[0] = lx.getBaseHuef() / 360.f;
4199 }
4200 if (ivt) {
4201 hsb[2] = 1 - hsb[2];
4202 }
4203 if (fSharp > 0) {
4204 hsb[2] = hsb[2] < .5f ? pow(hsb[2],fSharp) : 1-pow(1-hsb[2],fSharp);
4205 }
4206 if (fSoft > 0) {
4207 if (hsb[2] > 0.5f) {
4208 hsb[2] = lerp(hsb[2], 0.5f + 2 * (hsb[2]-0.5f)*(hsb[2]-0.5f), fSoft);
4209 } else {
4210 hsb[2] = lerp(hsb[2], 0.5f * sqrt(2*hsb[2]), fSoft);
4211 }
4212 }
4213 colors[i] = lx.hsb(
4214 (360.f * hsb[0] + hMod*360.f) % 360,
4215 100.f * hsb[1] * sMod,
4216 100.f * hsb[2] * bMod
4217 );
4218 }
4219 }
4220 }
4221 }
4222
4223 class QuantizeEffect extends SCEffect {
4224
4225 int[] quantizedFrame;
4226 float lastQuant;
4227 final BasicParameter amount = new BasicParameter("AMT", 0);
4228
4229 QuantizeEffect(GLucose glucose) {
4230 super(glucose);
4231 quantizedFrame = new int[glucose.lx.total];
4232 lastQuant = 0;
4233 }
4234
4235 public void apply(int[] colors) {
4236 float fQuant = amount.getValuef();
4237 if (fQuant > 0) {
4238 float tRamp = (lx.tempo.rampf() % (1.f/pow(2,floor((1-fQuant) * 4))));
4239 float f = lastQuant;
4240 lastQuant = tRamp;
4241 if (tRamp > f) {
4242 for (int i = 0; i < colors.length; ++i) {
4243 colors[i] = quantizedFrame[i];
4244 }
4245 return;
4246 }
4247 }
4248 for (int i = 0; i < colors.length; ++i) {
4249 quantizedFrame[i] = colors[i];
4250 }
4251 }
4252 }
4253
4254 class BlurEffect extends SCEffect {
4255
4256 final LXParameter amount = new BasicParameter("AMT", 0);
4257 final int[] frame;
4258 final LinearEnvelope env = new LinearEnvelope(0, 1, 100);
4259
4260 BlurEffect(GLucose glucose) {
4261 super(glucose);
4262 addParameter(amount);
4263 addModulator(env);
4264 frame = new int[lx.total];
4265 for (int i = 0; i < frame.length; ++i) {
4266 frame[i] = 0xff000000;
4267 }
4268 }
4269
4270 public void onEnable() {
4271 env.setRangeFromHereTo(1, 400).start();
4272 for (int i = 0; i < frame.length; ++i) {
4273 frame[i] = 0xff000000;
4274 }
4275 }
4276
4277 public void onDisable() {
4278 env.setRangeFromHereTo(0, 1000).start();
4279 }
4280
4281 public void apply(int[] colors) {
4282 float amt = env.getValuef() * amount.getValuef();
4283 if (amt > 0) {
4284 amt = (1 - amt);
4285 amt = 1 - (amt*amt*amt);
4286 for (int i = 0; i < colors.length; ++i) {
4287 // frame[i] = colors[i] = blendColor(colors[i], lerpColor(#000000, frame[i], amt, RGB), SCREEN);
4288 frame[i] = colors[i] = lerpColor(colors[i], blendColor(colors[i], frame[i], SCREEN), amt, RGB);
4289 }
4290 }
4291
4292 }
4293 }
4294 abstract class SamPattern extends SCPattern {
4295 public SamPattern(GLucose glucose) {
4296 super(glucose);
4297 setEligible(false);
4298 }
4299 }
4300
4301 class JazzRainbow extends SamPattern {
4302 public JazzRainbow(GLucose glucose) {
4303 super(glucose);
4304 }
4305
4306
4307 public void run(double deltaMs) {
4308 // Access the core master hue via this method call
4309 float hv = lx.getBaseHuef();
4310 for (int i = 0; i < colors.length*5; i=i+27) {
4311 float a = hv%250;
4312 if (i%2 == 0) {
4313 for (int b = 0; b < 70; b++) {
4314 colors[(i+b)%colors.length] = lx.hsb(a+i%250, 100, b*a%100);
4315 }
4316 }
4317 }
4318 }
4319 }
4320
4321
4322
4323 class HelixPattern extends SCPattern {
4324
4325 // Stores a line in point + vector form
4326 private class Line {
4327 private final PVector origin;
4328 private final PVector vector;
4329
4330 Line(PVector pt, PVector v) {
4331 origin = pt;
4332 vector = v.get();
4333 vector.normalize();
4334 }
4335
4336 public PVector getPoint() {
4337 return origin;
4338 }
4339
4340 public PVector getVector() {
4341 return vector;
4342 }
4343
4344 public PVector getPointAt(final float t) {
4345 return PVector.add(origin, PVector.mult(vector, t));
4346 }
4347
4348 public boolean isColinear(final PVector pt) {
4349 PVector projected = projectPoint(pt);
4350 return projected.x==pt.x && projected.y==pt.y && projected.z==pt.z;
4351 }
4352
4353 public float getTValue(final PVector pt) {
4354 PVector subtraction = PVector.sub(pt, origin);
4355 return subtraction.dot(vector);
4356 }
4357
4358 public PVector projectPoint(final PVector pt) {
4359 return getPointAt(getTValue(pt));
4360 }
4361
4362 public PVector rotatePoint(final PVector p, final float t) {
4363 final PVector o = origin;
4364 final PVector v = vector;
4365
4366 final float cost = cos(t);
4367 final float sint = sin(t);
4368
4369 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;
4370 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;
4371 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;
4372 return new PVector(x, y, z);
4373 }
4374 }
4375
4376 private class Helix {
4377 private final Line axis;
4378 private final float period; // period of coil
4379 private final float rotationPeriod; // animation period
4380 private final float radius; // radius of coil
4381 private final float girth; // girth of coil
4382 private final PVector referencePoint;
4383 private float phase;
4384 private PVector phaseNormal;
4385
4386 Helix(Line axis, float period, float radius, float girth, float phase, float rotationPeriod) {
4387 this.axis = axis;
4388 this.period = period;
4389 this.radius = radius;
4390 this.girth = girth;
4391 this.phase = phase;
4392 this.rotationPeriod = rotationPeriod;
4393
4394 // Generate a normal that will rotate to
4395 // produce the helical shape.
4396 PVector pt = new PVector(0, 1, 0);
4397 if (this.axis.isColinear(pt)) {
4398 pt = new PVector(0, 0, 1);
4399 if (this.axis.isColinear(pt)) {
4400 pt = new PVector(0, 1, 1);
4401 }
4402 }
4403
4404 this.referencePoint = pt;
4405
4406 // The normal is calculated by the cross product of the axis
4407 // and a random point that is not colinear with it.
4408 phaseNormal = axis.getVector().cross(referencePoint);
4409 phaseNormal.normalize();
4410 phaseNormal.mult(radius);
4411 }
4412
4413 public Line getAxis() {
4414 return axis;
4415 }
4416
4417 public PVector getPhaseNormal() {
4418 return phaseNormal;
4419 }
4420
4421 public float getPhase() {
4422 return phase;
4423 }
4424
4425 public void step(double deltaMs) {
4426 // Rotate
4427 if (rotationPeriod != 0) {
4428 this.phase = (phase + ((float)deltaMs / (float)rotationPeriod) * TWO_PI);
4429 }
4430 }
4431
4432 public PVector pointOnToroidalAxis(float t) {
4433 PVector p = axis.getPointAt(t);
4434 PVector middle = PVector.add(p, phaseNormal);
4435 return axis.rotatePoint(middle, (t / period) * TWO_PI + phase);
4436 }
4437
4438 private float myDist(PVector p1, PVector p2) {
4439 final float x = p2.x-p1.x;
4440 final float y = p2.y-p1.y;
4441 final float z = p2.z-p1.z;
4442 return sqrt(x*x + y*y + z*z);
4443 }
4444
4445 public int colorOfPoint(final PVector p) {
4446 final float t = axis.getTValue(p);
4447 final PVector axisPoint = axis.getPointAt(t);
4448
4449 // For performance reasons, cut out points that are outside of
4450 // the tube where the toroidal coil lives.
4451 if (abs(myDist(p, axisPoint) - radius) > girth*.5f) {
4452 return lx.hsb(0,0,0);
4453 }
4454
4455 // Find the appropriate point for the current rotation
4456 // of the helix.
4457 PVector toroidPoint = axisPoint;
4458 toroidPoint.add(phaseNormal);
4459 toroidPoint = axis.rotatePoint(toroidPoint, (t / period) * TWO_PI + phase);
4460
4461 // The rotated point represents the middle of the girth of
4462 // the helix. Figure out if the current point is inside that
4463 // region.
4464 float d = myDist(p, toroidPoint);
4465
4466 // Soften edges by fading brightness.
4467 float b = constrain(100*(1 - ((d-.5f*girth)/(girth*.5f))), 0, 100);
4468 return lx.hsb((lx.getBaseHuef() + (360*(phase / TWO_PI)))%360, 80, b);
4469 }
4470 }
4471
4472 private class BasePairInfo {
4473 Line line;
4474 float colorPhase1;
4475 float colorPhase2;
4476
4477 BasePairInfo(Line line, float colorPhase1, float colorPhase2) {
4478 this.line = line;
4479 this.colorPhase1 = colorPhase1;
4480 this.colorPhase2 = colorPhase2;
4481 }
4482 }
4483
4484 private final Helix h1;
4485 private final Helix h2;
4486 private final BasePairInfo[] basePairs;
4487
4488 private final BasicParameter helix1On = new BasicParameter("H1ON", 1);
4489 private final BasicParameter helix2On = new BasicParameter("H2ON", 1);
4490 private final BasicParameter basePairsOn = new BasicParameter("BPON", 1);
4491
4492 private static final float helixCoilPeriod = 100;
4493 private static final float helixCoilRadius = 50;
4494 private static final float helixCoilGirth = 30;
4495 private static final float helixCoilRotationPeriod = 5000;
4496
4497 private static final float spokePeriod = 40;
4498 private static final float spokeGirth = 20;
4499 private static final float spokePhase = 10;
4500 private static final float spokeRadius = helixCoilRadius - helixCoilGirth*.5f;
4501
4502 private static final float tMin = -200;
4503 private static final float tMax = 200;
4504
4505 public HelixPattern(GLucose glucose) {
4506 super(glucose);
4507
4508 addParameter(helix1On);
4509 addParameter(helix2On);
4510 addParameter(basePairsOn);
4511
4512 PVector origin = new PVector(100, 50, 55);
4513 PVector axis = new PVector(1,0,0);
4514
4515 h1 = new Helix(
4516 new Line(origin, axis),
4517 helixCoilPeriod,
4518 helixCoilRadius,
4519 helixCoilGirth,
4520 0,
4521 helixCoilRotationPeriod);
4522 h2 = new Helix(
4523 new Line(origin, axis),
4524 helixCoilPeriod,
4525 helixCoilRadius,
4526 helixCoilGirth,
4527 PI,
4528 helixCoilRotationPeriod);
4529
4530 basePairs = new BasePairInfo[(int)floor((tMax - tMin)/spokePeriod)];
4531 }
4532
4533 private void calculateSpokes() {
4534 float colorPhase = PI/6;
4535 for (float t = tMin + spokePhase; t < tMax; t += spokePeriod) {
4536 int spokeIndex = (int)floor((t - tMin)/spokePeriod);
4537 PVector h1point = h1.pointOnToroidalAxis(t);
4538 PVector spokeCenter = h1.getAxis().getPointAt(t);
4539 PVector spokeVector = PVector.sub(h1point, spokeCenter);
4540 Line spokeLine = new Line(spokeCenter, spokeVector);
4541 basePairs[spokeIndex] = new BasePairInfo(spokeLine, colorPhase * spokeIndex, colorPhase * (spokeIndex + 1));
4542 }
4543 }
4544
4545 private int calculateSpokeColor(final PVector pt) {
4546 // Find the closest spoke's t-value and calculate its
4547 // axis. Until everything animates in the model reference
4548 // frame, this has to be calculated at every step because
4549 // the helices rotate.
4550 Line axis = h1.getAxis();
4551 float t = axis.getTValue(pt) + spokePhase;
4552 int spokeIndex = (int)floor((t - tMin + spokePeriod/2) / spokePeriod);
4553 if (spokeIndex < 0 || spokeIndex >= basePairs.length) {
4554 return lx.hsb(0,0,0);
4555 }
4556 BasePairInfo basePair = basePairs[spokeIndex];
4557 Line spokeLine = basePair.line;
4558 PVector pointOnSpoke = spokeLine.projectPoint(pt);
4559 float d = PVector.dist(pt, pointOnSpoke);
4560 float b = (PVector.dist(pointOnSpoke, spokeLine.getPoint()) < spokeRadius) ? constrain(100*(1 - ((d-.5f*spokeGirth)/(spokeGirth*.5f))), 0, 100) : 0.f;
4561 float phase = spokeLine.getTValue(pointOnSpoke) < 0 ? basePair.colorPhase1 : basePair.colorPhase2;
4562 return lx.hsb((lx.getBaseHuef() + (360*(phase / TWO_PI)))%360, 80.f, b);
4563 }
4564
4565 public void run(double deltaMs) {
4566 boolean h1on = helix1On.getValue() > 0.5f;
4567 boolean h2on = helix2On.getValue() > 0.5f;
4568 boolean spokesOn = (float)basePairsOn.getValue() > 0.5f;
4569
4570 h1.step(deltaMs);
4571 h2.step(deltaMs);
4572 calculateSpokes();
4573
4574 for (Point p : model.points) {
4575 PVector pt = new PVector(p.x,p.y,p.z);
4576 int h1c = h1.colorOfPoint(pt);
4577 int h2c = h2.colorOfPoint(pt);
4578 int spokeColor = calculateSpokeColor(pt);
4579
4580 if (!h1on) {
4581 h1c = lx.hsb(0,0,0);
4582 }
4583
4584 if (!h2on) {
4585 h2c = lx.hsb(0,0,0);
4586 }
4587
4588 if (!spokesOn) {
4589 spokeColor = lx.hsb(0,0,0);
4590 }
4591
4592 // The helices are positioned to not overlap. If that changes,
4593 // a better blending formula is probably needed.
4594 colors[p.index] = blendColor(blendColor(h1c, h2c, ADD), spokeColor, ADD);
4595 }
4596 }
4597 }
4598
4599 class BlankPattern extends SCPattern {
4600 BlankPattern(GLucose glucose) {
4601 super(glucose);
4602 }
4603
4604 public void run(double deltaMs) {
4605 setColors(0xff000000);
4606 }
4607 }
4608
4609 abstract class TestPattern extends SCPattern {
4610 public TestPattern(GLucose glucose) {
4611 super(glucose);
4612 setEligible(false);
4613 }
4614 }
4615
4616 class TestSpeakerMapping extends TestPattern {
4617 TestSpeakerMapping(GLucose glucose) {
4618 super(glucose);
4619 }
4620
4621 public void run(double deltaMs) {
4622 int h = 0;
4623 for (Speaker speaker : model.speakers) {
4624 for (Strip strip : speaker.strips) {
4625 float b = 100;
4626 for (Point p : strip.points) {
4627 colors[p.index] = lx.hsb(h % 360, 100, b);
4628 b = max(0, b - 10);
4629 }
4630 h += 70;
4631 }
4632 }
4633 }
4634
4635 }
4636
4637 class TestBassMapping extends TestPattern {
4638 TestBassMapping(GLucose glucose) {
4639 super(glucose);
4640 }
4641
4642 public void run(double deltaMs) {
4643 int[] strips = { 2, 1, 0, 3, 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6 };
4644 int h = 0;
4645 for (int si : strips) {
4646 float b = 100;
4647 for (Point p : model.bassBox.strips.get(si).points) {
4648 colors[p.index] = lx.hsb(h % 360, 100, b);
4649 b = max(0, b - 10);
4650 }
4651 h += 70;
4652 }
4653 }
4654 }
4655
4656 class TestFloorMapping extends TestPattern {
4657 TestFloorMapping(GLucose glucose) {
4658 super(glucose);
4659 }
4660
4661 public void run(double deltaMs) {
4662 int[] strutIndices = {6, 5, 4, 3, 2, 1, 0, 7};
4663 int h = 0;
4664 for (int si : strutIndices) {
4665 float b = 100;
4666 for (Point p : model.bassBox.struts.get(si).points) {
4667 colors[p.index] = lx.hsb(h % 360, 100, b);
4668 b = max(0, b - 10);
4669 }
4670 h += 50;
4671 }
4672 int[] floorIndices = {0, 1, 2, 3};
4673 h = 0;
4674 for (int fi : floorIndices) {
4675 float b = 100;
4676 for (Point p : model.boothFloor.strips.get(fi).points) {
4677 colors[p.index] = lx.hsb(h, 100, b);
4678 b = max(0, b - 3);
4679 }
4680 h += 90;
4681 }
4682 }
4683 }
4684
4685 class TestPerformancePattern extends TestPattern {
4686
4687 final BasicParameter ops = new BasicParameter("OPS", 0);
4688 final BasicParameter iter = new BasicParameter("ITER", 0);
4689
4690 TestPerformancePattern(GLucose glucose) {
4691 super(glucose);
4692 addParameter(ops);
4693 addParameter(iter);
4694 }
4695
4696 public void run(double deltaMs) {
4697 float x = 1;
4698 for (int j = 0; j < ops.getValuef() * 400000; ++j) {
4699 x *= random(0, 1);
4700 }
4701
4702 if (iter.getValuef() < 0.25f) {
4703 for (Point p : model.points) {
4704 colors[p.index] = lx.hsb(
4705 (p.x*.1f + p.y*.1f) % 360,
4706 100,
4707 100
4708 );
4709 }
4710 } else if (iter.getValuef() < 0.5f) {
4711 for (int i = 0; i < colors.length; ++i) {
4712 colors[i] = lx.hsb(
4713 (90 + model.px[i]*.1f + model.py[i]*.1f) % 360,
4714 100,
4715 100
4716 );
4717 }
4718 } else if (iter.getValuef() < 0.75f) {
4719 for (int i = 0; i < colors.length; ++i) {
4720 colors[i] = lx.hsb(
4721 (180 + model.p[3*i]*.1f + model.p[3*i+1]*.1f) % 360,
4722 100,
4723 100
4724 );
4725 }
4726 } else {
4727 for (int i = 0; i < colors.length; ++i) {
4728 colors[i] = lx.hsb(
4729 (270 + model.x(i)*.1f + model.y(i)*.1f) % 360,
4730 100,
4731 100
4732 );
4733 }
4734 }
4735 }
4736 }
4737
4738 class TestStripPattern extends TestPattern {
4739
4740 SinLFO d = new SinLFO(4, 40, 4000);
4741
4742 public TestStripPattern(GLucose glucose) {
4743 super(glucose);
4744 addModulator(d).trigger();
4745 }
4746
4747 public void run(double deltaMs) {
4748 for (Strip s : model.strips) {
4749 for (Point p : s.points) {
4750 colors[p.index] = lx.hsb(
4751 lx.getBaseHuef(),
4752 100,
4753 max(0, 100 - d.getValuef()*dist(p.x, p.y, s.cx, s.cy))
4754 );
4755 }
4756 }
4757 }
4758 }
4759
4760 /**
4761 * Simplest demonstration of using the rotating master hue.
4762 * All pixels are full-on the same color.
4763 */
4764 class TestHuePattern extends TestPattern {
4765 public TestHuePattern(GLucose glucose) {
4766 super(glucose);
4767 }
4768
4769 public void run(double deltaMs) {
4770 // Access the core master hue via this method call
4771 float hv = lx.getBaseHuef();
4772 for (int i = 0; i < colors.length; ++i) {
4773 colors[i] = lx.hsb(hv, 100, 100);
4774 }
4775 }
4776 }
4777
4778 /**
4779 * Test of a wave moving across the X axis.
4780 */
4781 class TestXPattern extends TestPattern {
4782 private final SinLFO xPos = new SinLFO(0, model.xMax, 4000);
4783 public TestXPattern(GLucose glucose) {
4784 super(glucose);
4785 addModulator(xPos).trigger();
4786 }
4787 public void run(double deltaMs) {
4788 float hv = lx.getBaseHuef();
4789 for (Point p : model.points) {
4790 // This is a common technique for modulating brightness.
4791 // You can use abs() to determine the distance between two
4792 // values. The further away this point is from an exact
4793 // point, the more we decrease its brightness
4794 float bv = max(0, 100 - abs(p.x - xPos.getValuef()));
4795 colors[p.index] = lx.hsb(hv, 100, bv);
4796 }
4797 }
4798 }
4799
4800 /**
4801 * Test of a wave on the Y axis.
4802 */
4803 class TestYPattern extends TestPattern {
4804 private final SinLFO yPos = new SinLFO(0, model.yMax, 4000);
4805 public TestYPattern(GLucose glucose) {
4806 super(glucose);
4807 addModulator(yPos).trigger();
4808 }
4809 public void run(double deltaMs) {
4810 float hv = lx.getBaseHuef();
4811 for (Point p : model.points) {
4812 float bv = max(0, 100 - abs(p.y - yPos.getValuef()));
4813 colors[p.index] = lx.hsb(hv, 100, bv);
4814 }
4815 }
4816 }
4817
4818 /**
4819 * Test of a wave on the Z axis.
4820 */
4821 class TestZPattern extends TestPattern {
4822 private final SinLFO zPos = new SinLFO(0, model.zMax, 4000);
4823 public TestZPattern(GLucose glucose) {
4824 super(glucose);
4825 addModulator(zPos).trigger();
4826 }
4827 public void run(double deltaMs) {
4828 float hv = lx.getBaseHuef();
4829 for (Point p : model.points) {
4830 float bv = max(0, 100 - abs(p.z - zPos.getValuef()));
4831 colors[p.index] = lx.hsb(hv, 100, bv);
4832 }
4833 }
4834 }
4835
4836 /**
4837 * This shows how to iterate over towers, enumerated in the model.
4838 */
4839 class TestTowerPattern extends TestPattern {
4840 private final SawLFO towerIndex = new SawLFO(0, model.towers.size(), 1000*model.towers.size());
4841
4842 public TestTowerPattern(GLucose glucose) {
4843 super(glucose);
4844 addModulator(towerIndex).trigger();
4845 }
4846
4847 public void run(double deltaMs) {
4848 int ti = 0;
4849 for (Tower t : model.towers) {
4850 for (Point p : t.points) {
4851 colors[p.index] = lx.hsb(
4852 lx.getBaseHuef(),
4853 100,
4854 max(0, 100 - 80*LXUtils.wrapdistf(ti, towerIndex.getValuef(), model.towers.size()))
4855 );
4856 }
4857 ++ti;
4858 }
4859 }
4860
4861 }
4862
4863 /**
4864 * This is a demonstration of how to use the projection library. A projection
4865 * creates a mutation of the coordinates of all the points in the model, creating
4866 * virtual x,y,z coordinates. In effect, this is like virtually rotating the entire
4867 * art car. However, since in reality the car does not move, the result is that
4868 * it appears that the object we are drawing on the car is actually moving.
4869 *
4870 * Keep in mind that what we are creating a projection of is the view coordinates.
4871 * Depending on your intuition, some operations may feel backwards. For instance,
4872 * if you translate the view to the right, it will make it seem that the object
4873 * you are drawing has moved to the left. If you scale the view up 2x, objects
4874 * drawn with the same absolute values will seem to be half the size.
4875 *
4876 * If this feels counterintuitive at first, don't worry. Just remember that you
4877 * are moving the pixels, not the structure. We're dealing with a finite set
4878 * of sparse, non-uniformly spaced pixels. Mutating the structure would move
4879 * things to a space where there are no pixels in 99% of the cases.
4880 */
4881 class TestProjectionPattern extends TestPattern {
4882
4883 private final Projection projection;
4884 private final SawLFO angle = new SawLFO(0, TWO_PI, 9000);
4885 private final SinLFO yPos = new SinLFO(-20, 40, 5000);
4886
4887 public TestProjectionPattern(GLucose glucose) {
4888 super(glucose);
4889 projection = new Projection(model);
4890 addModulator(angle).trigger();
4891 addModulator(yPos).trigger();
4892 }
4893
4894 public void run(double deltaMs) {
4895 // For the same reasons described above, it may logically feel to you that
4896 // some of these operations are in reverse order. Again, just keep in mind that
4897 // the car itself is what's moving, not the object
4898 projection.reset(model)
4899
4900 // Translate so the center of the car is the origin, offset by yPos
4901 .translateCenter(model, 0, yPos.getValuef(), 0)
4902
4903 // Rotate around the origin (now the center of the car) about an X-vector
4904 .rotate(angle.getValuef(), 1, 0, 0)
4905
4906 // Scale up the Y axis (objects will look smaller in that access)
4907 .scale(1, 1.5f, 1);
4908
4909 float hv = lx.getBaseHuef();
4910 for (Coord c : projection) {
4911 float d = sqrt(c.x*c.x + c.y*c.y + c.z*c.z); // distance from origin
4912 // d = abs(d-60) + max(0, abs(c.z) - 20); // life saver / ring thing
4913 d = max(0, abs(c.y) - 10 + .1f*abs(c.z) + .02f*abs(c.x)); // plane / spear thing
4914 colors[c.index] = lx.hsb(
4915 (hv + .6f*abs(c.x) + abs(c.z)) % 360,
4916 100,
4917 constrain(140 - 40*d, 0, 100)
4918 );
4919 }
4920 }
4921 }
4922
4923 class TestCubePattern extends TestPattern {
4924
4925 private SawLFO index = new SawLFO(0, Cube.POINTS_PER_CUBE, Cube.POINTS_PER_CUBE*60);
4926
4927 TestCubePattern(GLucose glucose) {
4928 super(glucose);
4929 addModulator(index).start();
4930 }
4931
4932 public void run(double deltaMs) {
4933 for (Cube c : model.cubes) {
4934 int i = 0;
4935 for (Point p : c.points) {
4936 colors[p.index] = lx.hsb(
4937 lx.getBaseHuef(),
4938 100,
4939 max(0, 100 - 80.f*abs(i - index.getValuef()))
4940 );
4941 ++i;
4942 }
4943 }
4944 }
4945 }
4946
4947 class MappingTool extends TestPattern {
4948
4949 private int cubeIndex = 0;
4950 private int stripIndex = 0;
4951 private int channelIndex = 0;
4952
4953 public final int MAPPING_MODE_ALL = 0;
4954 public final int MAPPING_MODE_CHANNEL = 1;
4955 public final int MAPPING_MODE_SINGLE_CUBE = 2;
4956 public int mappingMode = MAPPING_MODE_ALL;
4957
4958 public final int CUBE_MODE_ALL = 0;
4959 public final int CUBE_MODE_SINGLE_STRIP = 1;
4960 public final int CUBE_MODE_STRIP_PATTERN = 2;
4961 public int cubeMode = CUBE_MODE_ALL;
4962
4963 public boolean channelModeRed = true;
4964 public boolean channelModeGreen = false;
4965 public boolean channelModeBlue = false;
4966
4967 private final int numChannels;
4968
4969 private final PandaMapping[] pandaMappings;
4970 private PandaMapping activePanda;
4971 private ChannelMapping activeChannel;
4972
4973 MappingTool(GLucose glucose, PandaMapping[] pandaMappings) {
4974 super(glucose);
4975 this.pandaMappings = pandaMappings;
4976 numChannels = pandaMappings.length * PandaMapping.CHANNELS_PER_BOARD;
4977 setChannel();
4978 }
4979
4980 public int numChannels() {
4981 return numChannels;
4982 }
4983
4984 private void setChannel() {
4985 activePanda = pandaMappings[channelIndex / PandaMapping.CHANNELS_PER_BOARD];
4986 activeChannel = activePanda.channelList[channelIndex % PandaMapping.CHANNELS_PER_BOARD];
4987 }
4988
4989 private int indexOfCubeInChannel(Cube c) {
4990 if (activeChannel.mode == ChannelMapping.MODE_CUBES) {
4991 int i = 1;
4992 for (int index : activeChannel.objectIndices) {
4993 if ((index >= 0) && (c == model.getCubeByRawIndex(index))) {
4994 return i;
4995 }
4996 ++i;
4997 }
4998 }
4999 return 0;
5000 }
5001
5002 private void printInfo() {
5003 println("Cube:" + cubeIndex + " Strip:" + (stripIndex+1));
5004 }
5005
5006 public void cube(int delta) {
5007 int len = model.cubes.size();
5008 cubeIndex = (len + cubeIndex + delta) % len;
5009 printInfo();
5010 }
5011
5012 public void strip(int delta) {
5013 int len = Cube.STRIPS_PER_CUBE;
5014 stripIndex = (len + stripIndex + delta) % len;
5015 printInfo();
5016 }
5017
5018 public void run(double deltaMs) {
5019 int off = 0xff000000;
5020 int c = off;
5021 int r = 0xffFF0000;
5022 int g = 0xff00FF00;
5023 int b = 0xff0000FF;
5024 if (channelModeRed) c |= r;
5025 if (channelModeGreen) c |= g;
5026 if (channelModeBlue) c |= b;
5027
5028 int ci = 0;
5029 for (Cube cube : model.cubes) {
5030 boolean cubeOn = false;
5031 int indexOfCubeInChannel = indexOfCubeInChannel(cube);
5032 switch (mappingMode) {
5033 case MAPPING_MODE_ALL: cubeOn = true; break;
5034 case MAPPING_MODE_SINGLE_CUBE: cubeOn = (cubeIndex == ci); break;
5035 case MAPPING_MODE_CHANNEL: cubeOn = (indexOfCubeInChannel > 0); break;
5036 }
5037 if (cubeOn) {
5038 if (mappingMode == MAPPING_MODE_CHANNEL) {
5039 int cc = off;
5040 switch (indexOfCubeInChannel) {
5041 case 1: cc = r; break;
5042 case 2: cc = r|g; break;
5043 case 3: cc = g; break;
5044 case 4: cc = b; break;
5045 case 5: cc = r|b; break;
5046 }
5047 setColor(cube, cc);
5048 } else if (cubeMode == CUBE_MODE_STRIP_PATTERN) {
5049 int si = 0;
5050 int sc = off;
5051 for (Strip strip : cube.strips) {
5052 int faceI = si / Face.STRIPS_PER_FACE;
5053 switch (faceI) {
5054 case 0: sc = r; break;
5055 case 1: sc = g; break;
5056 case 2: sc = b; break;
5057 case 3: sc = r|g|b; break;
5058 }
5059 if (si % Face.STRIPS_PER_FACE == 2) {
5060 sc = r|g;
5061 }
5062 setColor(strip, sc);
5063 ++si;
5064 }
5065 } else if (cubeMode == CUBE_MODE_SINGLE_STRIP) {
5066 setColor(cube, off);
5067 setColor(cube.strips.get(stripIndex), c);
5068 } else {
5069 setColor(cube, c);
5070 }
5071 } else {
5072 setColor(cube, off);
5073 }
5074 ++ci;
5075 }
5076 }
5077
5078 public void setCube(int index) {
5079 cubeIndex = index % model.cubes.size();
5080 }
5081
5082 public void incCube() {
5083 cubeIndex = (cubeIndex + 1) % model.cubes.size();
5084 }
5085
5086 public void decCube() {
5087 --cubeIndex;
5088 if (cubeIndex < 0) {
5089 cubeIndex += model.cubes.size();
5090 }
5091 }
5092
5093 public void setChannel(int index) {
5094 channelIndex = index % numChannels;
5095 setChannel();
5096 }
5097
5098 public void incChannel() {
5099 channelIndex = (channelIndex + 1) % numChannels;
5100 setChannel();
5101 }
5102
5103 public void decChannel() {
5104 channelIndex = (channelIndex + numChannels - 1) % numChannels;
5105 setChannel();
5106 }
5107
5108 public void setStrip(int index) {
5109 stripIndex = index % Cube.STRIPS_PER_CUBE;
5110 }
5111
5112 public void incStrip() {
5113 stripIndex = (stripIndex + 1) % Cube.STRIPS_PER_CUBE;
5114 }
5115
5116 public void decStrip() {
5117 stripIndex = (stripIndex + Cube.STRIPS_PER_CUBE - 1) % Cube.STRIPS_PER_CUBE;
5118 }
5119
5120 public void keyPressed(UIMapping uiMapping) {
5121 switch (keyCode) {
5122 case UP: if (mappingMode == MAPPING_MODE_CHANNEL) incChannel(); else incCube(); break;
5123 case DOWN: if (mappingMode == MAPPING_MODE_CHANNEL) decChannel(); else decCube(); break;
5124 case LEFT: decStrip(); break;
5125 case RIGHT: incStrip(); break;
5126 }
5127 switch (key) {
5128 case 'r': channelModeRed = !channelModeRed; break;
5129 case 'g': channelModeGreen = !channelModeGreen; break;
5130 case 'b': channelModeBlue = !channelModeBlue; break;
5131 }
5132 uiMapping.setChannelID(channelIndex+1);
5133 uiMapping.setCubeID(cubeIndex+1);
5134 uiMapping.setStripID(stripIndex+1);
5135 uiMapping.redraw();
5136 }
5137
5138 }
5139 /**
5140 * Not very flushed out, but kind of fun nonetheless.
5141 */
5142 class TimSpheres extends SCPattern {
5143 private BasicParameter hueParameter = new BasicParameter("RAD", 1.0f);
5144 private final SawLFO lfo = new SawLFO(0, 1, 10000);
5145 private final SinLFO sinLfo = new SinLFO(0, 1, 4000);
5146 private final float centerX, centerY, centerZ;
5147
5148 class Sphere {
5149 float x, y, z;
5150 float radius;
5151 float hue;
5152 }
5153
5154 private final Sphere[] spheres;
5155
5156 public TimSpheres(GLucose glucose) {
5157 super(glucose);
5158 addParameter(hueParameter);
5159 addModulator(lfo).trigger();
5160 addModulator(sinLfo).trigger();
5161 centerX = (model.xMax + model.xMin) / 2;
5162 centerY = (model.yMax + model.yMin) / 2;
5163 centerZ = (model.zMax + model.zMin) / 2;
5164
5165 spheres = new Sphere[2];
5166
5167 spheres[0] = new Sphere();
5168 spheres[0].x = model.xMin;
5169 spheres[0].y = centerY;
5170 spheres[0].z = centerZ;
5171 spheres[0].hue = 0;
5172 spheres[0].radius = 50;
5173
5174 spheres[1] = new Sphere();
5175 spheres[1].x = model.xMax;
5176 spheres[1].y = centerY;
5177 spheres[1].z = centerZ;
5178 spheres[1].hue = 0.33f;
5179 spheres[1].radius = 50;
5180 }
5181
5182 public void run(double deltaMs) {
5183 // Access the core master hue via this method call
5184 float hv = hueParameter.getValuef();
5185 float lfoValue = lfo.getValuef();
5186 float sinLfoValue = sinLfo.getValuef();
5187
5188 spheres[0].x = model.xMin + sinLfoValue * model.xMax;
5189 spheres[1].x = model.xMax - sinLfoValue * model.xMax;
5190
5191 spheres[0].radius = 100 * hueParameter.getValuef();
5192 spheres[1].radius = 100 * hueParameter.getValuef();
5193
5194 for (Point p : model.points) {
5195 float value = 0;
5196
5197 int c = lx.hsb(0, 0, 0);
5198 for (Sphere s : spheres) {
5199 float d = sqrt(pow(p.x - s.x, 2) + pow(p.y - s.y, 2) + pow(p.z - s.z, 2));
5200 float r = (s.radius); // * (sinLfoValue + 0.5));
5201 value = max(0, 1 - max(0, d - r) / 10);
5202
5203 c = blendColor(c, lx.hsb(((s.hue + lfoValue) % 1) * 360, 100, min(1, value) * 100), ADD);
5204 }
5205
5206 colors[p.index] = c;
5207 }
5208 }
5209 }
5210
5211 class Vector2 {
5212 float x, y;
5213
5214 Vector2() {
5215 this(0, 0);
5216 }
5217
5218 Vector2(float x, float y) {
5219 this.x = x;
5220 this.y = y;
5221 }
5222
5223 public float distanceTo(float x, float y) {
5224 return sqrt(pow(x - this.x, 2) + pow(y - this.y, 2));
5225 }
5226
5227 public float distanceTo(Vector2 v) {
5228 return distanceTo(v.x, v.y);
5229 }
5230
5231 public Vector2 plus(float x, float y) {
5232 return new Vector2(this.x + x, this.y + y);
5233 }
5234
5235 public Vector2 plus(Vector2 v) {
5236 return plus(v.x, v.y);
5237 }
5238
5239 public Vector2 minus(Vector2 v) {
5240 return plus(-1 * v.x, -1 * v.y);
5241 }
5242 }
5243
5244 class Vector3 {
5245 float x, y, z;
5246
5247 Vector3() {
5248 this(0, 0, 0);
5249 }
5250
5251 Vector3(float x, float y, float z) {
5252 this.x = x;
5253 this.y = y;
5254 this.z = z;
5255 }
5256
5257 public float distanceTo(float x, float y, float z) {
5258 return sqrt(pow(x - this.x, 2) + pow(y - this.y, 2) + pow(z - this.z, 2));
5259 }
5260
5261 public float distanceTo(Vector3 v) {
5262 return distanceTo(v.x, v.y, v.z);
5263 }
5264
5265 public float distanceTo(Point p) {
5266 return distanceTo(p.x, p.y, p.z);
5267 }
5268
5269 public void add(Vector3 other, float multiplier) {
5270 this.add(other.x * multiplier, other.y * multiplier, other.z * multiplier);
5271 }
5272
5273 public void add(float x, float y, float z) {
5274 this.x += x;
5275 this.y += y;
5276 this.z += z;
5277 }
5278
5279 public void divide(float factor) {
5280 this.x /= factor;
5281 this.y /= factor;
5282 this.z /= factor;
5283 }
5284 }
5285
5286 class Rotation {
5287 private float a, b, c, d, e, f, g, h, i;
5288
5289 Rotation(float yaw, float pitch, float roll) {
5290 float cosYaw = cos(yaw);
5291 float sinYaw = sin(yaw);
5292 float cosPitch = cos(pitch);
5293 float sinPitch = sin(pitch);
5294 float cosRoll = cos(roll);
5295 float sinRoll = sin(roll);
5296
5297 a = cosYaw * cosPitch;
5298 b = cosYaw * sinPitch * sinRoll - sinYaw * cosRoll;
5299 c = cosYaw * sinPitch * cosRoll + sinYaw * sinRoll;
5300 d = sinYaw * cosPitch;
5301 e = sinYaw * sinPitch * sinRoll + cosYaw * cosRoll;
5302 f = sinYaw * sinPitch * cosRoll - cosYaw * sinRoll;
5303 g = -1 * sinPitch;
5304 h = cosPitch * sinRoll;
5305 i = cosPitch * cosRoll;
5306 }
5307
5308 public Vector3 rotated(Vector3 v) {
5309 return new Vector3(
5310 rotatedX(v),
5311 rotatedY(v),
5312 rotatedZ(v));
5313
5314 }
5315
5316 public float rotatedX(Vector3 v) {
5317 return a * v.x + b * v.y + c * v.z;
5318 }
5319
5320 public float rotatedY(Vector3 v) {
5321 return d * v.x + e * v.y + f * v.z;
5322 }
5323
5324 public float rotatedZ(Vector3 v) {
5325 return g * v.x + h * v.y + i * v.z;
5326 }
5327 }
5328
5329 /**
5330 * Very literal rain effect. Not that great as-is but some tweaking could make it nice.
5331 * A couple ideas:
5332 * - changing hue and direction of "rain" could make a nice fire effect
5333 * - knobs to change frequency and size of rain drops
5334 * - sync somehow to tempo but maybe less frequently than every beat?
5335 */
5336 class TimRaindrops extends SCPattern {
5337 public Vector3 randomVector3() {
5338 return new Vector3(
5339 random(model.xMax - model.xMin) + model.xMin,
5340 random(model.yMax - model.yMin) + model.yMin,
5341 random(model.zMax - model.zMin) + model.zMin);
5342 }
5343
5344 class Raindrop {
5345 Vector3 p;
5346 Vector3 v;
5347 float radius;
5348 float hue;
5349
5350 Raindrop() {
5351 this.radius = 30;
5352 this.p = new Vector3(
5353 random(model.xMax - model.xMin) + model.xMin,
5354 model.yMax + this.radius,
5355 random(model.zMax - model.zMin) + model.zMin);
5356 float velMagnitude = 120;
5357 this.v = new Vector3(
5358 0,
5359 -3 * model.yMax,
5360 0);
5361 this.hue = random(40) + 200;
5362 }
5363
5364 // returns TRUE when this should die
5365 public boolean age(double ms) {
5366 p.add(v, (float) (ms / 1000.0f));
5367 return this.p.y < (0 - this.radius);
5368 }
5369 }
5370
5371 private float leftoverMs = 0;
5372 private float msPerRaindrop = 40;
5373 private List<Raindrop> raindrops;
5374
5375 public TimRaindrops(GLucose glucose) {
5376 super(glucose);
5377 raindrops = new LinkedList<Raindrop>();
5378 }
5379
5380 public void run(double deltaMs) {
5381 leftoverMs += deltaMs;
5382 while (leftoverMs > msPerRaindrop) {
5383 leftoverMs -= msPerRaindrop;
5384 raindrops.add(new Raindrop());
5385 }
5386
5387 for (Point p : model.points) {
5388 int c =
5389 blendColor(
5390 lx.hsb(210, 20, (float)Math.max(0, 1 - Math.pow((model.yMax - p.y) / 10, 2)) * 50),
5391 lx.hsb(220, 60, (float)Math.max(0, 1 - Math.pow((p.y - model.yMin) / 10, 2)) * 100),
5392 ADD);
5393 for (Raindrop raindrop : raindrops) {
5394 if (p.x >= (raindrop.p.x - raindrop.radius) && p.x <= (raindrop.p.x + raindrop.radius) &&
5395 p.y >= (raindrop.p.y - raindrop.radius) && p.y <= (raindrop.p.y + raindrop.radius)) {
5396 float d = raindrop.p.distanceTo(p) / raindrop.radius;
5397 // float value = (float)Math.max(0, 1 - Math.pow(Math.min(0, d - raindrop.radius) / 5, 2));
5398 if (d < 1) {
5399 c = blendColor(c, lx.hsb(raindrop.hue, 80, (float)Math.pow(1 - d, 0.01f) * 100), ADD);
5400 }
5401 }
5402 }
5403 colors[p.index] = c;
5404 }
5405
5406 Iterator<Raindrop> i = raindrops.iterator();
5407 while (i.hasNext()) {
5408 Raindrop raindrop = i.next();
5409 boolean dead = raindrop.age(deltaMs);
5410 if (dead) {
5411 i.remove();
5412 }
5413 }
5414 }
5415 }
5416
5417
5418 class TimCubes extends SCPattern {
5419 private BasicParameter rateParameter = new BasicParameter("RATE", 0.125f);
5420 private BasicParameter attackParameter = new BasicParameter("ATTK", 0.5f);
5421 private BasicParameter decayParameter = new BasicParameter("DECAY", 0.5f);
5422 private BasicParameter hueParameter = new BasicParameter("HUE", 0.5f);
5423 private BasicParameter hueVarianceParameter = new BasicParameter("H.V.", 0.25f);
5424 private BasicParameter saturationParameter = new BasicParameter("SAT", 0.5f);
5425
5426 class CubeFlash {
5427 Cube c;
5428 float value;
5429 float hue;
5430 boolean hasPeaked;
5431
5432 CubeFlash() {
5433 c = model.cubes.get(floor(random(model.cubes.size())));
5434 hue = random(1);
5435 boolean infiniteAttack = (attackParameter.getValuef() > 0.999f);
5436 hasPeaked = infiniteAttack;
5437 value = (infiniteAttack ? 1 : 0);
5438 }
5439
5440 // returns TRUE if this should die
5441 public boolean age(double ms) {
5442 if (!hasPeaked) {
5443 value = value + (float) (ms / 1000.0f * ((attackParameter.getValuef() + 0.01f) * 5));
5444 if (value >= 1.0f) {
5445 value = 1.0f;
5446 hasPeaked = true;
5447 }
5448 return false;
5449 } else {
5450 value = value - (float) (ms / 1000.0f * ((decayParameter.getValuef() + 0.01f) * 10));
5451 return value <= 0;
5452 }
5453 }
5454 }
5455
5456 private float leftoverMs = 0;
5457 private List<CubeFlash> flashes;
5458
5459 public TimCubes(GLucose glucose) {
5460 super(glucose);
5461 addParameter(rateParameter);
5462 addParameter(attackParameter);
5463 addParameter(decayParameter);
5464 addParameter(hueParameter);
5465 addParameter(hueVarianceParameter);
5466 addParameter(saturationParameter);
5467 flashes = new LinkedList<CubeFlash>();
5468 }
5469
5470 public void run(double deltaMs) {
5471 leftoverMs += deltaMs;
5472 float msPerFlash = 1000 / ((rateParameter.getValuef() + .01f) * 100);
5473 while (leftoverMs > msPerFlash) {
5474 leftoverMs -= msPerFlash;
5475 flashes.add(new CubeFlash());
5476 }
5477
5478 for (Point p : model.points) {
5479 colors[p.index] = 0;
5480 }
5481
5482 for (CubeFlash flash : flashes) {
5483 float hue = (hueParameter.getValuef() + (hueVarianceParameter.getValuef() * flash.hue)) % 1.0f;
5484 int c = lx.hsb(hue * 360, saturationParameter.getValuef() * 100, (flash.value) * 100);
5485 for (Point p : flash.c.points) {
5486 colors[p.index] = c;
5487 }
5488 }
5489
5490 Iterator<CubeFlash> i = flashes.iterator();
5491 while (i.hasNext()) {
5492 CubeFlash flash = i.next();
5493 boolean dead = flash.age(deltaMs);
5494 if (dead) {
5495 i.remove();
5496 }
5497 }
5498 }
5499 }
5500
5501 /**
5502 * This one is the best but you need to play with all the knobs. It's synced to
5503 * the tempo, with the WSpd knob letting you pick 4 discrete multipliers for
5504 * the tempo.
5505 *
5506 * Basically it's just 3 planes all rotating to the beat, but also rotated relative
5507 * to one another. The intersection of the planes and the cubes over time makes
5508 * for a nice abstract effect.
5509 */
5510 class TimPlanes extends SCPattern {
5511 private BasicParameter wobbleParameter = new BasicParameter("Wob", 0.166f);
5512 private BasicParameter wobbleSpreadParameter = new BasicParameter("WSpr", 0.25f);
5513 private BasicParameter wobbleSpeedParameter = new BasicParameter("WSpd", 0.375f);
5514 private BasicParameter wobbleOffsetParameter = new BasicParameter("WOff", 0);
5515 private BasicParameter derezParameter = new BasicParameter("Drez", 0.5f);
5516 private BasicParameter thicknessParameter = new BasicParameter("Thick", 0.4f);
5517 private BasicParameter ySpreadParameter = new BasicParameter("ySpr", 0.2f);
5518 private BasicParameter hueParameter = new BasicParameter("Hue", 0.75f);
5519 private BasicParameter hueSpreadParameter = new BasicParameter("HSpr", 0.68f);
5520
5521 final float centerX, centerY, centerZ;
5522 float phase;
5523
5524 class Plane {
5525 Vector3 center;
5526 Rotation rotation;
5527 float hue;
5528
5529 Plane(Vector3 center, Rotation rotation, float hue) {
5530 this.center = center;
5531 this.rotation = rotation;
5532 this.hue = hue;
5533 }
5534 }
5535
5536 TimPlanes(GLucose glucose) {
5537 super(glucose);
5538 centerX = (model.xMin + model.xMax) / 2;
5539 centerY = (model.yMin + model.yMax) / 2;
5540 centerZ = (model.zMin + model.zMax) / 2;
5541 phase = 0;
5542 addParameter(wobbleParameter);
5543 addParameter(wobbleSpreadParameter);
5544 addParameter(wobbleSpeedParameter);
5545 // addParameter(wobbleOffsetParameter);
5546 addParameter(derezParameter);
5547 addParameter(thicknessParameter);
5548 addParameter(ySpreadParameter);
5549 addParameter(hueParameter);
5550 addParameter(hueSpreadParameter);
5551 }
5552
5553 int beat = 0;
5554 float prevRamp = 0;
5555 float[] wobbleSpeeds = { 1.0f/8, 1.0f/4, 1.0f/2, 1.0f };
5556
5557 public void run(double deltaMs) {
5558 float ramp = (float)lx.tempo.ramp();
5559 if (ramp < prevRamp) {
5560 beat = (beat + 1) % 32;
5561 }
5562 prevRamp = ramp;
5563
5564 float wobbleSpeed = wobbleSpeeds[floor(wobbleSpeedParameter.getValuef() * wobbleSpeeds.length * 0.9999f)];
5565
5566 phase = (((beat + ramp) * wobbleSpeed + wobbleOffsetParameter.getValuef()) % 1) * 2 * PI;
5567
5568 float ySpread = ySpreadParameter.getValuef() * 50;
5569 float wobble = wobbleParameter.getValuef() * PI;
5570 float wobbleSpread = wobbleSpreadParameter.getValuef() * PI;
5571 float hue = hueParameter.getValuef() * 360;
5572 float hueSpread = (hueSpreadParameter.getValuef() - 0.5f) * 360;
5573
5574 float saturation = 10 + 60.0f * pow(ramp, 0.25f);
5575
5576 float derez = derezParameter.getValuef();
5577
5578 Plane[] planes = {
5579 new Plane(
5580 new Vector3(centerX, centerY + ySpread, centerZ),
5581 new Rotation(wobble - wobbleSpread, phase, 0),
5582 (hue + 360 - hueSpread) % 360),
5583 new Plane(
5584 new Vector3(centerX, centerY, centerZ),
5585 new Rotation(wobble, phase, 0),
5586 hue),
5587 new Plane(
5588 new Vector3(centerX, centerY - ySpread, centerZ),
5589 new Rotation(wobble + wobbleSpread, phase, 0),
5590 (hue + 360 + hueSpread) % 360)
5591 };
5592
5593 float thickness = (thicknessParameter.getValuef() * 25 + 1);
5594
5595 Vector3 normalizedPoint = new Vector3();
5596
5597 for (Point p : model.points) {
5598 if (random(1.0f) < derez) {
5599 continue;
5600 }
5601
5602 int c = 0;
5603
5604 for (Plane plane : planes) {
5605 normalizedPoint.x = p.x - plane.center.x;
5606 normalizedPoint.y = p.y - plane.center.y;
5607 normalizedPoint.z = p.z - plane.center.z;
5608
5609 float v = plane.rotation.rotatedY(normalizedPoint);
5610 float d = abs(v);
5611
5612 final int planeColor;
5613 if (d <= thickness) {
5614 planeColor = lx.hsb(plane.hue, saturation, 100);
5615 } else if (d <= thickness * 2) {
5616 float value = 1 - ((d - thickness) / thickness);
5617 planeColor = lx.hsb(plane.hue, saturation, value * 100);
5618 } else {
5619 planeColor = 0;
5620 }
5621
5622 if (planeColor != 0) {
5623 if (c == 0) {
5624 c = planeColor;
5625 } else {
5626 c = blendColor(c, planeColor, ADD);
5627 }
5628 }
5629 }
5630
5631 colors[p.index] = c;
5632 }
5633 }
5634 }
5635
5636 /**
5637 * Two spinning wheels, basically XORed together, with a color palette that should
5638 * be pretty easy to switch around. Timed to the beat; also introduces "clickiness"
5639 * which makes the movement non-linear throughout a given beat, giving it a nice
5640 * dance feel. I'm not 100% sure that it's actually going to look like it's _on_
5641 * the beat, but that should be easy enough to adjust.
5642 *
5643 * It's particularly nice to turn down the clickiness and turn up derez during
5644 * slow/beatless parts of the music and then revert them at the drop :) But maybe
5645 * I shouldn't be listening to so much shitty dubstep while making these...
5646 */
5647 class TimPinwheels extends SCPattern {
5648 private BasicParameter horizSpreadParameter = new BasicParameter("HSpr", 0.75f);
5649 private BasicParameter vertSpreadParameter = new BasicParameter("VSpr", 0.5f);
5650 private BasicParameter vertOffsetParameter = new BasicParameter("VOff", 1.0f);
5651 private BasicParameter zSlopeParameter = new BasicParameter("ZSlp", 0.6f);
5652 private BasicParameter sharpnessParameter = new BasicParameter("Shrp", 0.25f);
5653 private BasicParameter derezParameter = new BasicParameter("Drez", 0.25f);
5654 private BasicParameter clickinessParameter = new BasicParameter("Clic", 0.5f);
5655 private BasicParameter hueParameter = new BasicParameter("Hue", 0.667f);
5656 private BasicParameter hueSpreadParameter = new BasicParameter("HSpd", 0.667f);
5657
5658 float phase = 0;
5659 private final int NUM_BLADES = 12;
5660
5661 class Pinwheel {
5662 Vector2 center;
5663 int numBlades;
5664 float realPhase;
5665 float phase;
5666 float speed;
5667
5668 Pinwheel(float xCenter, float yCenter, int numBlades, float speed) {
5669 this.center = new Vector2(xCenter, yCenter);
5670 this.numBlades = numBlades;
5671 this.speed = speed;
5672 }
5673
5674 public void age(float numBeats) {
5675 int numSteps = numBlades;
5676
5677 realPhase = (realPhase + numBeats / numSteps) % 2.0f;
5678
5679 float phaseStep = floor(realPhase * numSteps);
5680 float phaseRamp = (realPhase * numSteps) % 1.0f;
5681 phase = (phaseStep + pow(phaseRamp, (clickinessParameter.getValuef() * 10) + 1)) / (numSteps * 2);
5682 // phase = (phase + deltaMs / 1000.0 * speed) % 1.0;
5683 }
5684
5685 public boolean isOnBlade(float x, float y) {
5686 x = x - center.x;
5687 y = y - center.y;
5688
5689 float normalizedAngle = (atan2(x, y) / (2 * PI) + 1 + phase) % 1;
5690 float v = (normalizedAngle * 4 * numBlades);
5691 int blade_num = floor((v + 2) / 4);
5692 return (blade_num % 2) == 0;
5693 }
5694 }
5695
5696 private final List<Pinwheel> pinwheels;
5697 private final float[] values;
5698
5699 TimPinwheels(GLucose glucose) {
5700 super(glucose);
5701
5702 addParameter(horizSpreadParameter);
5703 // addParameter(vertSpreadParameter);
5704 addParameter(vertOffsetParameter);
5705 addParameter(zSlopeParameter);
5706 addParameter(sharpnessParameter);
5707 addParameter(derezParameter);
5708 addParameter(clickinessParameter);
5709 addParameter(hueParameter);
5710 addParameter(hueSpreadParameter);
5711
5712 pinwheels = new ArrayList();
5713 pinwheels.add(new Pinwheel(0, 0, NUM_BLADES, 0.1f));
5714 pinwheels.add(new Pinwheel(0, 0, NUM_BLADES, -0.1f));
5715
5716 this.updateHorizSpread();
5717 this.updateVertPositions();
5718
5719 values = new float[model.points.size()];
5720 }
5721
5722 public void onParameterChanged(LXParameter parameter) {
5723 if (parameter == horizSpreadParameter) {
5724 updateHorizSpread();
5725 } else if (parameter == vertSpreadParameter || parameter == vertOffsetParameter) {
5726 updateVertPositions();
5727 }
5728 }
5729
5730 private void updateHorizSpread() {
5731 float xDist = model.xMax - model.xMin;
5732 float xCenter = (model.xMin + model.xMax) / 2;
5733
5734 float spread = horizSpreadParameter.getValuef() - 0.5f;
5735 pinwheels.get(0).center.x = xCenter - xDist * spread;
5736 pinwheels.get(1).center.x = xCenter + xDist * spread;
5737 }
5738
5739 private void updateVertPositions() {
5740 float yDist = model.yMax - model.yMin;
5741 float yCenter = model.yMin + yDist * vertOffsetParameter.getValuef();
5742
5743 float spread = vertSpreadParameter.getValuef() - 0.5f;
5744 pinwheels.get(0).center.y = yCenter - yDist * spread;
5745 pinwheels.get(1).center.y = yCenter + yDist * spread;
5746 }
5747
5748 private float prevRamp = 0;
5749
5750 public void run(double deltaMs) {
5751 float ramp = lx.tempo.rampf();
5752 float numBeats = (1 + ramp - prevRamp) % 1;
5753 prevRamp = ramp;
5754
5755 float hue = hueParameter.getValuef() * 360;
5756 // 0 -> -180
5757 // 0.5 -> 0
5758 // 1 -> 180
5759 float hueSpread = (hueSpreadParameter.getValuef() - 0.5f) * 360;
5760
5761 float fadeAmount = (float) (deltaMs / 1000.0f) * pow(sharpnessParameter.getValuef() * 10, 1);
5762
5763 for (Pinwheel pw : pinwheels) {
5764 pw.age(numBeats);
5765 }
5766
5767 float derez = derezParameter.getValuef();
5768
5769 float zSlope = (zSlopeParameter.getValuef() - 0.5f) * 2;
5770
5771 int i = -1;
5772 for (Point p : model.points) {
5773 ++i;
5774
5775 int value = 0;
5776 for (Pinwheel pw : pinwheels) {
5777 value += (pw.isOnBlade(p.x, p.y - p.z * zSlope) ? 1 : 0);
5778 }
5779 if (value == 1) {
5780 values[i] = 1;
5781 // colors[p.index] = lx.hsb(120, 0, 100);
5782 } else {
5783 values[i] = max(0, values[i] - fadeAmount);
5784 //color c = colors[p.index];
5785 //colors[p.index] = lx.hsb(max(0, lx.h(c) - 10), min(100, lx.s(c) + 10), lx.b(c) - 5 );
5786 }
5787
5788 if (random(1.0f) >= derez) {
5789 float v = values[i];
5790 colors[p.index] = lx.hsb((360 + hue + pow(v, 2) * hueSpread) % 360, 30 + pow(1 - v, 0.25f) * 60, v * 100);
5791 }
5792 }
5793 }
5794 }
5795
5796 /**
5797 * This tries to figure out neighboring pixels from one cube to another to
5798 * let you have a bunch of moving points tracing all over the structure.
5799 * Adds a couple seconds of startup time to do the calculation, and in the
5800 * end just comes out looking a lot like a screensaver. Probably not worth
5801 * it but there may be useful code here.
5802 */
5803 class TimTrace extends SCPattern {
5804 private Map<Point, List<Point>> pointToNeighbors;
5805 private Map<Point, Strip> pointToStrip;
5806 // private final Map<Strip, List<Strip>> stripToNearbyStrips;
5807
5808 int extraMs;
5809
5810 class MovingPoint {
5811 Point currentPoint;
5812 float hue;
5813 private Strip currentStrip;
5814 private int currentStripIndex;
5815 private int direction; // +1 or -1
5816
5817 MovingPoint(Point p) {
5818 this.setPointOnNewStrip(p);
5819 hue = random(360);
5820 }
5821
5822 private void setPointOnNewStrip(Point p) {
5823 this.currentPoint = p;
5824 this.currentStrip = pointToStrip.get(p);
5825 for (int i = 0; i < this.currentStrip.points.size(); ++i) {
5826 if (this.currentStrip.points.get(i) == p) {
5827 this.currentStripIndex = i;
5828 break;
5829 }
5830 }
5831 if (this.currentStripIndex == 0) {
5832 // we are at the beginning of the strip; go forwards
5833 this.direction = 1;
5834 } else if (this.currentStripIndex == this.currentStrip.points.size()) {
5835 // we are at the end of the strip; go backwards
5836 this.direction = -1;
5837 } else {
5838 // we are in the middle of a strip; randomly go one way or another
5839 this.direction = ((random(1.0f) < 0.5f) ? -1 : 1);
5840 }
5841 }
5842
5843 public void step() {
5844 List<Point> neighborsOnOtherStrips = pointToNeighbors.get(this.currentPoint);
5845
5846 Point nextPointOnCurrentStrip = null;
5847 this.currentStripIndex += this.direction;
5848 if (this.currentStripIndex >= 0 && this.currentStripIndex < this.currentStrip.points.size()) {
5849 nextPointOnCurrentStrip = this.currentStrip.points.get(this.currentStripIndex);
5850 }
5851
5852 // pick which option to take; if we can keep going on the current strip then
5853 // add that as another option
5854 int option = floor(random(neighborsOnOtherStrips.size() + (nextPointOnCurrentStrip == null ? 0 : 100)));
5855
5856 if (option < neighborsOnOtherStrips.size()) {
5857 this.setPointOnNewStrip(neighborsOnOtherStrips.get(option));
5858 } else {
5859 this.currentPoint = nextPointOnCurrentStrip;
5860 }
5861 }
5862 }
5863
5864 List<MovingPoint> movingPoints;
5865
5866 TimTrace(GLucose glucose) {
5867 super(glucose);
5868
5869 extraMs = 0;
5870
5871 pointToNeighbors = this.buildPointToNeighborsMap();
5872 pointToStrip = this.buildPointToStripMap();
5873
5874 int numMovingPoints = 1000;
5875 movingPoints = new ArrayList();
5876 for (int i = 0; i < numMovingPoints; ++i) {
5877 movingPoints.add(new MovingPoint(model.points.get(floor(random(model.points.size())))));
5878 }
5879
5880 }
5881
5882 private Map<Strip, List<Strip>> buildStripToNearbyStripsMap() {
5883 Map<Strip, Vector3> stripToCenter = new HashMap();
5884 for (Strip s : model.strips) {
5885 Vector3 v = new Vector3();
5886 for (Point p : s.points) {
5887 v.add(p.x, p.y, p.z);
5888 }
5889 v.divide(s.points.size());
5890 stripToCenter.put(s, v);
5891 }
5892
5893 Map<Strip, List<Strip>> stripToNeighbors = new HashMap();
5894 for (Strip s : model.strips) {
5895 List<Strip> neighbors = new ArrayList();
5896 Vector3 sCenter = stripToCenter.get(s);
5897 for (Strip potentialNeighbor : model.strips) {
5898 if (s != potentialNeighbor) {
5899 float distance = sCenter.distanceTo(stripToCenter.get(potentialNeighbor));
5900 if (distance < 25) {
5901 neighbors.add(potentialNeighbor);
5902 }
5903 }
5904 }
5905 stripToNeighbors.put(s, neighbors);
5906 }
5907
5908 return stripToNeighbors;
5909 }
5910
5911 private Map<Point, List<Point>> buildPointToNeighborsMap() {
5912 Map<Point, List<Point>> m = new HashMap();
5913 Map<Strip, List<Strip>> stripToNearbyStrips = this.buildStripToNearbyStripsMap();
5914
5915 for (Strip s : model.strips) {
5916 List<Strip> nearbyStrips = stripToNearbyStrips.get(s);
5917
5918 for (Point p : s.points) {
5919 Vector3 v = new Vector3(p.x, p.y, p.z);
5920
5921 List<Point> neighbors = new ArrayList();
5922
5923 for (Strip nearbyStrip : nearbyStrips) {
5924 Point closestPoint = null;
5925 float closestPointDistance = 100000;
5926
5927 for (Point nsp : nearbyStrip.points) {
5928 float distance = v.distanceTo(nsp.x, nsp.y, nsp.z);
5929 if (closestPoint == null || distance < closestPointDistance) {
5930 closestPoint = nsp;
5931 closestPointDistance = distance;
5932 }
5933 }
5934
5935 if (closestPointDistance < 15) {
5936 neighbors.add(closestPoint);
5937 }
5938 }
5939
5940 m.put(p, neighbors);
5941 }
5942 }
5943
5944 return m;
5945 }
5946
5947 private Map<Point, Strip> buildPointToStripMap() {
5948 Map<Point, Strip> m = new HashMap();
5949 for (Strip s : model.strips) {
5950 for (Point p : s.points) {
5951 m.put(p, s);
5952 }
5953 }
5954 return m;
5955 }
5956
5957 public void run(double deltaMs) {
5958 for (Point p : model.points) {
5959 int c = colors[p.index];
5960 colors[p.index] = lx.hsb(lx.h(c), lx.s(c), lx.b(c) - 3);
5961 }
5962
5963 for (MovingPoint mp : movingPoints) {
5964 mp.step();
5965 colors[mp.currentPoint.index] = blendColor(colors[mp.currentPoint.index], lx.hsb(mp.hue, 10, 100), ADD);
5966 }
5967 }
5968 }
5969 class GlitchPlasma extends SCPattern {
5970 private int pos = 0;
5971 private float satu = 100;
5972 private float speed = 1;
5973 private float glitch = 0;
5974 BasicParameter saturationParameter = new BasicParameter("SATU", 1.0f);
5975 BasicParameter speedParameter = new BasicParameter("SPEED", 0.1f);
5976 BasicParameter glitchParameter = new BasicParameter("GLITCH", 0.0f);
5977
5978 public GlitchPlasma(GLucose glucose) {
5979 super(glucose);
5980 addParameter(saturationParameter);
5981 addParameter(speedParameter);
5982 addParameter(glitchParameter);
5983 }
5984 public void onParameterChanged(LXParameter parameter) {
5985 if (parameter == saturationParameter) {
5986 satu = 100*parameter.getValuef();
5987 } else if (parameter == speedParameter) {
5988 speed = 8*parameter.getValuef();
5989 } else if (parameter == glitchParameter) {
5990 glitch = parameter.getValuef();
5991 }
5992 }
5993
5994 public void run(double deltaMs) {
5995 for (Point p : model.points) {
5996 float hv = sin(dist(p.x + pos, p.y, 128.0f, 128.0f) / 8.0f)
5997 + sin(dist(p.x, p.y, 64.0f, 64.0f) / 8.0f)
5998 + sin(dist(p.x, p.y + pos / 7, 192.0f, 64.0f) / 7.0f)
5999 + sin(dist(p.x, p.z + pos, 192.0f, 100.0f) / 8.0f);
6000 float bv = 100;
6001 colors[p.index] = lx.hsb((hv+2)*50, satu, bv);
6002 }
6003 if (random(1.0f)<glitch/20) {
6004 pos=pos-PApplet.parseInt(random(10,30));
6005 }
6006 pos+=speed;
6007 if (pos >= MAX_INT-1) pos=0;
6008 }
6009 }
6010
6011 // This is very much a work in progress. Trying to get a flame effect.
6012 class FireEffect extends SCPattern {
6013 private float[][] intensity;
6014 private float hotspot;
6015 private float decay = 0.3f;
6016 private int xm;
6017 private int ym;
6018 BasicParameter decayParameter = new BasicParameter("DECAY", 0.3f);
6019
6020 public FireEffect(GLucose glucose) {
6021 super(glucose);
6022 xm = PApplet.parseInt(model.xMax);
6023 ym = PApplet.parseInt(model.yMax);
6024
6025 intensity = new float[xm][ym];
6026 addParameter(decayParameter);
6027 }
6028 public void onParameterChanged(LXParameter parameter) {
6029 if (parameter == decayParameter) {
6030 decay = parameter.getValuef();
6031 }
6032 }
6033 private int flameColor(float level) {
6034 if (level<=0) return lx.hsb(0,0,0);
6035 float br=min(100,sqrt(level)*15);
6036 return lx.hsb(level/1.7f,100,br);
6037 }
6038 public void run(double deltaMs) {
6039 for (int x=10;x<xm-10;x++) {
6040 if (x%50>45 || x%50<5) {
6041 intensity[x][ym-1] = random(30,100);
6042 } else {
6043 intensity[x][ym-1] = random(0,50);
6044 }
6045 }
6046 for (int x=1;x<xm-1;x++) {
6047 for (int y=0;y<ym-1;y++) {
6048 intensity[x][y] = (intensity[x-1][y+1]+intensity[x][y+1]+intensity[x+1][y+1])/3-decay;
6049 }
6050 }
6051
6052 for (Point p : model.points) {
6053 int x = max(0,(PApplet.parseInt(p.x)+PApplet.parseInt(p.z))%xm);
6054 int y = constrain(ym-PApplet.parseInt(p.y),0,ym-1);
6055 colors[p.index] = flameColor(intensity[x][y]);
6056 }
6057 }
6058 }
6059
6060 class StripBounce extends SCPattern {
6061 private final int numOsc = 30;
6062 SinLFO[] fX = new SinLFO[numOsc]; //new SinLFO(0, model.xMax, 5000);
6063 SinLFO[] fY = new SinLFO[numOsc]; //new SinLFO(0, model.yMax, 4000);
6064 SinLFO[] fZ = new SinLFO[numOsc]; //new SinLFO(0, model.yMax, 3000);
6065 SinLFO[] sat = new SinLFO[numOsc];
6066 float[] colorOffset = new float[numOsc];
6067
6068 public StripBounce(GLucose glucose) {
6069 super(glucose);
6070 for (int i=0;i<numOsc;i++) {
6071 fX[i] = new SinLFO(0, model.xMax, random(2000,20000));
6072 fY[i] = new SinLFO(0, model.yMax, random(2000,20000));
6073 fZ[i] = new SinLFO(0, model.zMax, random(2000,20000));
6074 sat[i] = new SinLFO(60, 100, random(2000,50000));
6075 addModulator(fX[i]).trigger();
6076 addModulator(fY[i]).trigger();
6077 addModulator(fZ[i]).trigger();
6078 colorOffset[i]=random(0,256);
6079 }
6080 }
6081
6082 public void run(double deltaMs) {
6083 float[] bright = new float[model.points.size()];
6084 for (Strip strip : model.strips) {
6085 for (int i=0;i<numOsc;i++) {
6086 float avgdist=0.0f;
6087 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());
6088 boolean on = avgdist<30;
6089 float hv = (lx.getBaseHuef()+colorOffset[i])%360;
6090 float br = max(0,100-avgdist*4);
6091 for (Point p : strip.points) {
6092 if (on && br>bright[p.index]) {
6093 colors[p.index] = lx.hsb(hv,sat[i].getValuef(),br);
6094 bright[p.index] = br;
6095 }
6096 }
6097 }
6098 }
6099 }
6100 }
6101
6102 class SoundRain extends SCPattern {
6103
6104 private FFT fft = null;
6105 private LinearEnvelope[] bandVals = null;
6106 private float[] lightVals = null;
6107 private int avgSize;
6108 private float gain = 25;
6109 SawLFO pos = new SawLFO(0, 9, 8000);
6110 SinLFO col1 = new SinLFO(0, model.xMax, 5000);
6111 BasicParameter gainParameter = new BasicParameter("GAIN", 0.5f);
6112
6113 public SoundRain(GLucose glucose) {
6114 super(glucose);
6115 addModulator(pos).trigger();
6116 addModulator(col1).trigger();
6117 addParameter(gainParameter);
6118 }
6119
6120 public void onParameterChanged(LXParameter parameter) {
6121 if (parameter == gainParameter) {
6122 gain = 50*parameter.getValuef();
6123 }
6124 }
6125 protected void onActive() {
6126 if (this.fft == null) {
6127 this.fft = new FFT(lx.audioInput().bufferSize(), lx.audioInput().sampleRate());
6128 this.fft.window(FFT.HAMMING);
6129 this.fft.logAverages(40, 1);
6130 this.avgSize = this.fft.avgSize();
6131 this.bandVals = new LinearEnvelope[this.avgSize];
6132 for (int i = 0; i < this.bandVals.length; ++i) {
6133 this.addModulator(this.bandVals[i] = (new LinearEnvelope(0, 0, 700+i*4))).trigger();
6134 }
6135 lightVals = new float[avgSize];
6136 }
6137 }
6138
6139 public void run(double deltaMs) {
6140 this.fft.forward(this.lx.audioInput().mix);
6141 for (int i = 0; i < avgSize; ++i) {
6142 float value = this.fft.getAvg(i);
6143 this.bandVals[i].setEndVal(value,40).trigger();
6144 float lv = min(value*gain,100);
6145 if (lv>lightVals[i]) {
6146 lightVals[i]=min(lightVals[i]+15,lv,100);
6147 } else {
6148 lightVals[i]=max(lv,lightVals[i]-5,0);
6149 }
6150 }
6151 for (Cube c : model.cubes) {
6152 for (int j=0; j<c.strips.size(); j++) {
6153 Strip s = c.strips.get(j);
6154 if (j%4!=0 && j%4!=2) {
6155 for (Point p : s.points) {
6156 int seq = PApplet.parseInt(p.y*avgSize/model.yMax+pos.getValuef()+sin(p.x+p.z)*2)%avgSize;
6157 seq=min(abs(seq-(avgSize/2)),avgSize-1);
6158 colors[p.index] = lx.hsb(200,max(0,100-abs(p.x-col1.getValuef())/2),lightVals[seq]);
6159 }
6160 }
6161 }
6162 }
6163 }
6164 }
6165
6166 class FaceSync extends SCPattern {
6167 SinLFO xosc = new SinLFO(-10, 10, 3000);
6168 SinLFO zosc = new SinLFO(-10, 10, 3000);
6169 SinLFO col1 = new SinLFO(0, model.xMax, 5000);
6170 SinLFO col2 = new SinLFO(0, model.xMax, 4000);
6171
6172 public FaceSync(GLucose glucose) {
6173 super(glucose);
6174 addModulator(xosc).trigger();
6175 addModulator(zosc).trigger();
6176 zosc.setValue(0);
6177 addModulator(col1).trigger();
6178 addModulator(col2).trigger();
6179 col2.setValue(model.xMax);
6180 }
6181
6182 public void run(double deltaMs) {
6183 int i=0;
6184 for (Strip s : model.strips) {
6185 i++;
6186 for (Point p : s.points) {
6187 float dx, dz;
6188 if (i%32 < 16) {
6189 dx = p.x - (s.cx+xosc.getValuef());
6190 dz = p.z - (s.cz+zosc.getValuef());
6191 } else {
6192 dx = p.x - (s.cx+zosc.getValuef());
6193 dz = p.z - (s.cz+xosc.getValuef());
6194 }
6195 //println(dx);
6196 float a1=max(0,100-abs(p.x-col1.getValuef()));
6197 float a2=max(0,100-abs(p.x-col2.getValuef()));
6198 float sat = max(a1,a2);
6199 float h = (359*a1+200*a2) / (a1+a2);
6200 colors[p.index] = lx.hsb(h,sat,100-abs(dx*5)-abs(dz*5));
6201 }
6202 }
6203 }
6204 }
6205
6206 class SoundSpikes extends SCPattern {
6207 private FFT fft = null;
6208 private LinearEnvelope[] bandVals = null;
6209 private float[] lightVals = null;
6210 private int avgSize;
6211 private float gain = 25;
6212 BasicParameter gainParameter = new BasicParameter("GAIN", 0.5f);
6213 SawLFO pos = new SawLFO(0, model.xMax, 8000);
6214
6215 public SoundSpikes(GLucose glucose) {
6216 super(glucose);
6217 addParameter(gainParameter);
6218 addModulator(pos).trigger();
6219 }
6220
6221 public void onParameterChanged(LXParameter parameter) {
6222 if (parameter == gainParameter) {
6223 gain = 50*parameter.getValuef();
6224 }
6225 }
6226 protected void onActive() {
6227 if (this.fft == null) {
6228 this.fft = new FFT(lx.audioInput().bufferSize(), lx.audioInput().sampleRate());
6229 this.fft.window(FFT.HAMMING);
6230 this.fft.logAverages(40, 1);
6231 this.avgSize = this.fft.avgSize();
6232 this.bandVals = new LinearEnvelope[this.avgSize];
6233 for (int i = 0; i < this.bandVals.length; ++i) {
6234 this.addModulator(this.bandVals[i] = (new LinearEnvelope(0, 0, 700+i*4))).trigger();
6235 }
6236 lightVals = new float[avgSize];
6237 }
6238 }
6239
6240 public void run(double deltaMs) {
6241 this.fft.forward(this.lx.audioInput().mix);
6242 for (int i = 0; i < avgSize; ++i) {
6243 float value = this.fft.getAvg(i);
6244 this.bandVals[i].setEndVal(value,40).trigger();
6245 float lv = min(value*gain,model.yMax+10);
6246 if (lv>lightVals[i]) {
6247 lightVals[i]=min(lightVals[i]+30,lv,model.yMax+10);
6248 } else {
6249 lightVals[i]=max(lv,lightVals[i]-10,0);
6250 }
6251 }
6252 int i = 0;
6253 for (Cube c : model.cubes) {
6254 for (int j=0; j<c.strips.size(); j++) {
6255 Strip s = c.strips.get(j);
6256 if (j%4!=0 && j%4!=2) {
6257 for (Point p : s.points) {
6258 float dis = (abs(p.x-model.xMax/2)+pos.getValuef())%model.xMax/2;
6259 int seq = PApplet.parseInt((dis*avgSize*2)/model.xMax);
6260 if (seq>avgSize) seq=avgSize-seq;
6261 seq=constrain(seq,0,avgSize-1);
6262 float br=max(0, lightVals[seq]-p.y);
6263 colors[p.index] = lx.hsb((dis*avgSize*65)/model.xMax,90,br);
6264 }
6265 }
6266 }
6267 }
6268 }
6269 }
6270
6271 static public void main(String[] passedArgs) {
6272 String[] appletArgs = new String[] { "SugarCubes" };
6273 if (passedArgs != null) {
6274 PApplet.main(concat(appletArgs, passedArgs));
6275 } else {
6276 PApplet.main(appletArgs);
6277 }
6278 }
6279 }