diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fbb77ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Ant +/build/ + +# pack.sh +/releases/ + +# mashine +*.mashine + +# sublime +*.sublime-* + diff --git a/README.md b/README.md index ac360a7..0f50b2d 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,42 @@ # WHAT ? -MaShine is a software to do live light show using live audio analysis, prerecorded animations (sequences of frames), filters (function, algorithm written (inside or outside the software, using javascript maybe, I hope.)), midi input, Open Lighting Architecture for DMX/ArtNet output. +MaShine is a software to do live light show using live audio analysis, prerecorded animations (sequences of frames), filters (function, algorithms, effects, ...), MIDI/DMX input, Open Lighting Architecture for DMX/ArtNet output. -You can check the draft version here : https://github.com/procsynth/ShineProcessing +![mashine](https://cloud.githubusercontent.com/assets/321345/14266488/eed3cbbc-fac8-11e5-8db3-842b1792f12e.png) -# HOW +# HOW TO USE +Download the [latest build](https://github.com/procsynth/MaShine/blob/devel/dist/mashine.jar?raw=true). You may want to install [OLA](https://www.openlighting.org/ola/) to output real DMX, but you can still use MaShine without it. -Require Processing 3, Java 8 and ant to build. Run `ant` in the root folder to build and run, or `java -jar dist/mashine.jar` to run. -Midi device only tested on Linux. Otherwise it should be crossplateform. +Midi devices only tested on Linux. Otherwise it should be crossplateform (OLA is only available on Linux and OSX). -## What the hell are you doing ? +TODO : user manual. - - 10:25 10 fev 2016 : implementing focusable - - 15:12 10 fev 2016 : implemented focusable, nice mouse related elements methods - - 15:13 10 fev 2016 : what could I do now ? - - 20:00 10 fev 2016 : little pack script. - - 02:00 11 fev 2016 : added all the colors \o/ - - 08:30 11 fev 2016 : TODO : ola, port, devices, universes, addresses, -images- frames, device features (color, range, fixed) - - 00:34 13 fev 2016 : DONE : midi inputs (generic (?) but particularly Behringer CMD DC-1 and Korg NanoKontrol2) (LOT of inputs !) - - 00:35 13 fev 2016 : TODO : scrollable content (LOT of inputs !) - - 16:47 14 fev 2016 : better mouse event/element focus behavior, began frame and sequence stuff (devices, features, frame, sequence, visualizer) a lot to do - - 16:49 14 fev 2016 : TODO : UI for creating scene/frames, UI for typing - - 23:21 14 fev 2016 : better device drawing, added menu bar, worked out how frame can work, better focus, commencing UI device editor (2 buttons yeah!) - - 16:37 18 fev 2016 : basic user text input, should add regEx validation, maxlength check. - - 16:43 22 fev 2016 : git versionning ! Main UI elements are drawn on separate PGraphics canvas, and it works (soon : scrollable) +# HOW TO BUILD - # WHICH LICENSE ? +Require Java 8 and ant to build. Run `ant` in the root folder to build and run. + +# TODO + +- [x] Inputs basics +- [x] Outputs basics +- [x] Patch +- [x] Animation +- [x] Saves +- [x] Input binding +- [x] Filters mechanism +- [x] Device groups in filters +- [x] DMX input +- [x] FFT ranges +- [x] More device features +- [x] More filters +- [x] MIDI outputs (get rid of themidibus ?) +- [ ] protobuf saves +- [ ] grid ui (see [Blocks](https://github.com/procsynth/Blocks)) +- [ ] integrated Artnet node, get rid of OLA ? +- [ ] __A complete demo set__ +- [ ] User manual + +# WHICH LICENSE ? This software is licensed under the GNU/LGPL v3 license, a copy is provided in the [LICENSES][licenses] file. @@ -36,4 +47,4 @@ The [Processing](https://processing.org) export libraries under the GNU/LGPL v3 [The OLA client](https://www.openlighting.org/ola/) with Google's protobuf software under a custom license which can be found in the [LICENSES][licenses] file. The Roboto Mono font under the Apache 2.0 license,a copy is provided in the [LICENSES][licenses] file. -[licenses]: https://github.com/procsynth/MaShine/blob/master/LICENSES "Licenses file" \ No newline at end of file +[licenses]: https://github.com/procsynth/MaShine/blob/master/LICENSES "Licenses file" diff --git a/build.xml b/build.xml index 2fc3539..e660815 100644 --- a/build.xml +++ b/build.xml @@ -1,7 +1,5 @@ - @@ -9,16 +7,12 @@ - - - - @@ -26,8 +20,8 @@ - - + + @@ -40,7 +34,7 @@ - @@ -48,6 +42,8 @@ + + diff --git a/RobotoMono-Light-11.vlw b/data/RobotoMono-Light-11.vlw similarity index 100% rename from RobotoMono-Light-11.vlw rename to data/RobotoMono-Light-11.vlw diff --git a/data/mashine.png b/data/mashine.png new file mode 100644 index 0000000..dd565f6 Binary files /dev/null and b/data/mashine.png differ diff --git a/dist/mashine.jar b/dist/mashine.jar index 8da0c7a..7494067 100644 Binary files a/dist/mashine.jar and b/dist/mashine.jar differ diff --git a/lib/ola/protobuf-java-2.6.1.jar b/lib/ola/protobuf-java-2.6.1.jar new file mode 100644 index 0000000..0fcd808 Binary files /dev/null and b/lib/ola/protobuf-java-2.6.1.jar differ diff --git a/lib/ola/protobuf-java-3.0.0-beta-2.jar b/lib/ola/protobuf-java-3.0.0-beta-2.jar deleted file mode 100644 index f1995f4..0000000 Binary files a/lib/ola/protobuf-java-3.0.0-beta-2.jar and /dev/null differ diff --git a/lib/processing3/core.jar b/lib/processing3/core.jar new file mode 100644 index 0000000..7fc8f99 Binary files /dev/null and b/lib/processing3/core.jar differ diff --git a/lib/processing3/export.txt b/lib/processing3/export.txt new file mode 100644 index 0000000..88d0bc2 --- /dev/null +++ b/lib/processing3/export.txt @@ -0,0 +1,11 @@ +# If you want to support more platforms, visit jogamp.org to get the +# natives libraries for the platform in question (i.e. Solaris). + +name = OpenGL + +application.macosx=core.jar,jogl-all.jar,gluegen-rt.jar,jogl-all-natives-macosx-universal.jar,gluegen-rt-natives-macosx-universal.jar +application.windows32=core.jar,jogl-all.jar,gluegen-rt.jar,jogl-all-natives-windows-i586.jar,gluegen-rt-natives-windows-i586.jar +application.windows64=core.jar,jogl-all.jar,gluegen-rt.jar,jogl-all-natives-windows-amd64.jar,gluegen-rt-natives-windows-amd64.jar +application.linux32=core.jar,jogl-all.jar,gluegen-rt.jar,jogl-all-natives-linux-i586.jar,gluegen-rt-natives-linux-i586.jar +application.linux64=core.jar,jogl-all.jar,gluegen-rt.jar,jogl-all-natives-linux-amd64.jar,gluegen-rt-natives-linux-amd64.jar +application.linux-armv6hf=core.jar,jogl-all.jar,gluegen-rt.jar,jogl-all-natives-linux-armv6hf.jar,gluegen-rt-natives-linux-armv6hf.jar \ No newline at end of file diff --git a/lib/processing3/gluegen-rt-natives-linux-amd64.jar b/lib/processing3/gluegen-rt-natives-linux-amd64.jar new file mode 100644 index 0000000..a2466f4 Binary files /dev/null and b/lib/processing3/gluegen-rt-natives-linux-amd64.jar differ diff --git a/lib/processing3/gluegen-rt-natives-linux-armv6hf.jar b/lib/processing3/gluegen-rt-natives-linux-armv6hf.jar new file mode 100644 index 0000000..5ef0673 Binary files /dev/null and b/lib/processing3/gluegen-rt-natives-linux-armv6hf.jar differ diff --git a/lib/processing3/gluegen-rt-natives-linux-i586.jar b/lib/processing3/gluegen-rt-natives-linux-i586.jar new file mode 100644 index 0000000..914a259 Binary files /dev/null and b/lib/processing3/gluegen-rt-natives-linux-i586.jar differ diff --git a/lib/processing3/gluegen-rt-natives-macosx-universal.jar b/lib/processing3/gluegen-rt-natives-macosx-universal.jar new file mode 100644 index 0000000..15df5e8 Binary files /dev/null and b/lib/processing3/gluegen-rt-natives-macosx-universal.jar differ diff --git a/lib/processing3/gluegen-rt-natives-windows-amd64.jar b/lib/processing3/gluegen-rt-natives-windows-amd64.jar new file mode 100644 index 0000000..517fb84 Binary files /dev/null and b/lib/processing3/gluegen-rt-natives-windows-amd64.jar differ diff --git a/lib/processing3/gluegen-rt-natives-windows-i586.jar b/lib/processing3/gluegen-rt-natives-windows-i586.jar new file mode 100644 index 0000000..1c393b7 Binary files /dev/null and b/lib/processing3/gluegen-rt-natives-windows-i586.jar differ diff --git a/lib/processing3/gluegen-rt.jar b/lib/processing3/gluegen-rt.jar new file mode 100644 index 0000000..742fdb2 Binary files /dev/null and b/lib/processing3/gluegen-rt.jar differ diff --git a/lib/processing3/jogl-all-natives-linux-amd64.jar b/lib/processing3/jogl-all-natives-linux-amd64.jar new file mode 100644 index 0000000..e57b8c7 Binary files /dev/null and b/lib/processing3/jogl-all-natives-linux-amd64.jar differ diff --git a/lib/processing3/jogl-all-natives-linux-armv6hf.jar b/lib/processing3/jogl-all-natives-linux-armv6hf.jar new file mode 100644 index 0000000..5aea1b2 Binary files /dev/null and b/lib/processing3/jogl-all-natives-linux-armv6hf.jar differ diff --git a/lib/processing3/jogl-all-natives-linux-i586.jar b/lib/processing3/jogl-all-natives-linux-i586.jar new file mode 100644 index 0000000..88a27ce Binary files /dev/null and b/lib/processing3/jogl-all-natives-linux-i586.jar differ diff --git a/lib/processing3/jogl-all-natives-macosx-universal.jar b/lib/processing3/jogl-all-natives-macosx-universal.jar new file mode 100644 index 0000000..c65ea99 Binary files /dev/null and b/lib/processing3/jogl-all-natives-macosx-universal.jar differ diff --git a/lib/processing3/jogl-all-natives-windows-amd64.jar b/lib/processing3/jogl-all-natives-windows-amd64.jar new file mode 100644 index 0000000..9577bf1 Binary files /dev/null and b/lib/processing3/jogl-all-natives-windows-amd64.jar differ diff --git a/lib/processing3/jogl-all-natives-windows-i586.jar b/lib/processing3/jogl-all-natives-windows-i586.jar new file mode 100644 index 0000000..4439f1d Binary files /dev/null and b/lib/processing3/jogl-all-natives-windows-i586.jar differ diff --git a/lib/processing3/jogl-all.jar b/lib/processing3/jogl-all.jar new file mode 100644 index 0000000..e2c7b44 Binary files /dev/null and b/lib/processing3/jogl-all.jar differ diff --git a/pack.sh b/pack.sh deleted file mode 100755 index 5da99db..0000000 --- a/pack.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P) -cd $dir -echo `pwd` -mkdir MaShine releases -cp dist/mashine.jar MaShine -cp -R lib/ MaShine -cp *.vlw MaShine -cp README.md MaShine -tar czvf releases/MaShine_`date +%Y-%m-%d_%H-%M-%S`.tar.gz MaShine -rm -rf MaShine -echo Done \ No newline at end of file diff --git a/src/mashine/Bank.java b/src/mashine/Bank.java new file mode 100644 index 0000000..6bbf69e --- /dev/null +++ b/src/mashine/Bank.java @@ -0,0 +1,579 @@ +/** + * Collection of sequences and filters + * + * @author procsynth - Antoine Pintout + * @since 19-03-2016` + */ +package mashine; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Collection; +import java.io.Serializable; + +import mashine.engine.Filter; +import mashine.scene.Sequence; +import mashine.scene.Frame; +import mashine.scene.Device; +import mashine.scene.DeviceGroup; +import mashine.scene.features.*; +import mashine.ui.Colors; +import mashine.ui.FlatColor; + +import processing.core.PConstants; + +public class Bank implements Serializable{ + + private ArrayList sequences; + private HashMap filters; + private ArrayList colors; + + public Bank(){ + sequences = new ArrayList(); + filters = new HashMap(); + colors = new ArrayList(); + + sequences.add(new Sequence("unamed sequence")); + + for(int i = 0; i < 154; i++){ + colors.add(new FlatColor(0xFF, 0x00, 0x00) + .withHue((i % 14)/(float)14.0) + .withBrightness((float) Math.floor((167-i)/14)/11) + .withAlpha(0)); + } + + colors.add(Colors.WHITE.withAlpha(255)); + for(int i = 0; i < 5; i++){ + colors.add(Colors.BLACK.withAlpha(0)); + } + + filters.put("dimmer", new Filter("dimmer", new Filter.Robot(){ + public void setup(Filter filter){ + filter.declare("value", Filter.RANGE); + } + + public Frame f(Filter filter, Frame frame){ + + for(EditableFeature f : frame.getFeatures().values()){ + if(f instanceof ColorFeature){ + ColorFeature c = (ColorFeature) f; + c.link(c.getLinkedColor().dim((float)filter.getRange("value"))); + } + } + return frame; + } + })); + + filters.put("blackout", new Filter("blackout", new Filter.Robot(){ + public Frame f(Filter filter, Frame frame){ + Map weights = filter.getGroup().getDevices(); + for(Device d : weights.keySet()){ + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature){ + ColorFeature c = (ColorFeature) f; + c.link(new FlatColor(0)); + frame.addFeature(d, c); + } + } + } + return frame; + } + })); + + filters.put("whiteout", new Filter("whiteout", new Filter.Robot(){ + public Frame f(Filter filter, Frame frame){ + Map weights = filter.getGroup().getDevices(); + for(Device d : weights.keySet()){ + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature){ + ColorFeature c = (ColorFeature) f; + c.link(new FlatColor(255)); + frame.addFeature(d, c); + } + } + } + return frame; + } + })); + + filters.put("shine", new Filter("shine", new Filter.Robot(){ + public Frame f(Filter filter, Frame frame){ + Map weights = filter.getGroup().getDevices(); + for(Device d : weights.keySet()){ + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature){ + if(Math.random() > 0.95){ + ColorFeature c = (ColorFeature) f; + c.link(new FlatColor(255)); + frame.addFeature(d, c); + } + } + } + } + return frame; + } + })); + + filters.put("strobe", new Filter("strobe", new Filter.Robot(){ + public void setup(Filter filter){ + filter.declare("on", Filter.RANGE); + filter.declare("off", Filter.RANGE); + } + public Frame f(Filter filter, Frame frame){ + long on = Math.round(1+ 1000.0*filter.getRange("on")); + long off = Math.round(1+ 1000.0*filter.getRange("off")); + if(MaShine.m.millis() % (on+off) >= off){ + Map weights = filter.getGroup().getDevices(); + for(Device d : weights.keySet()){ + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature){ + ColorFeature c = (ColorFeature) f; + c.link(new FlatColor(255)); + frame.addFeature(d, c); + } + } + } + } + return frame; + } + })); + + filters.put("direct_control", new Filter("direct_control", new Filter.Robot(){ + public void setup(Filter filter){ + filter.declare("x", Filter.RANGE); + filter.declare("x_", Filter.RANGE); + filter.declare("y", Filter.RANGE); + filter.declare("y_", Filter.RANGE); + } + + public Frame f(Filter filter, Frame frame){ + Map weights = filter.getGroup().getDevices(); + for(Device d : weights.keySet()){ + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof Coords){ + Coords cf = new Coords(); + boolean symetry = weights.get(d) % 2 == 1; + + double pan = (symetry ? 255 : 0) + (symetry ? -1 : 1)*filter.getRange("x")*255; + double pan_ = (symetry ? 255 : 0) + (symetry ? -1 : 1)*filter.getRange("x_")*255; + + cf.setField("x", (int) Math.round(pan)); + cf.setField("x_", (int) Math.round(pan_)); + cf.setField("y", (int) Math.round(filter.getRange("y")*255)); + cf.setField("y_", (int) Math.round(filter.getRange("y_")*255)); + frame.addFeature(d, cf); + } + } + } + return frame; + } + })); + + filters.put("hue_abs", new Filter("hue_abs", new Filter.Robot(){ + public void setup(Filter filter){ + filter.declare("newhue", Filter.RANGE); + } + public Frame f(Filter filter, Frame frame){ + Map weights = filter.getGroup().getDevices(); + for(Device d : weights.keySet()){ + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature && frame.isIn(d,f)){ + ColorFeature c = (ColorFeature) frame.getFeature(d, f); + c.link(c.getLinkedColor().withHue((float)filter.getRange("newhue"))); + } + } + } + return frame; + } + })); + + filters.put("hue_rot", new Filter("hue_rot", new Filter.Robot(){ + public void setup(Filter filter){ + filter.declare("rotation", Filter.RANGE); + } + public Frame f(Filter filter, Frame frame){ + Map weights = filter.getGroup().getDevices(); + for(Device d : weights.keySet()){ + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature && frame.isIn(d,f)){ + ColorFeature c = (ColorFeature) frame.getFeature(d, f); + c.link(c.getLinkedColor().withRotatedHue((float)filter.getRange("rotation") * 2.0f)); + } + } + } + return frame; + } + })); + + filters.put("drop_all", new Filter("drop_all", new Filter.Robot(){ + public Frame f(Filter filter, Frame frame){ + return new Frame(); + } + })); + + filters.put("fft_graph", new Filter("fft_graph", new Filter.Robot(){ + public Frame f(Filter filter, Frame frame){ + Map weights = filter.getGroup().getDevices(); + for(Device d : weights.keySet()){ + int w = weights.get(d); + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature){ + String fftName = String.format("%2s", w).replace(' ', '0'); + ColorFeature c = (ColorFeature) f; + FlatColor fc = new FlatColor(100, 0, 255, 0); + fc.setBrightness((float)MaShine.inputs.getRange("minim.fft."+fftName)); + c.link(fc); + frame.addFeature(d, c); + + } + } + } + return frame; + } + })); + + filters.put("wave_circle", new Filter("wave_circle", new Filter.Robot(){ + public void setup(Filter filter){ + filter.declare("size", Filter.RANGE); + filter.declare("speed", Filter.RANGE); + filter.declare("speedMult", Filter.RANGE); + filter.declare("offset", Filter.LONG); + filter.setLong("offset", 0L); + } + public Frame f(Filter filter, Frame frame){ + Map weights = filter.getGroup().getDevices(); + int min = -1; + int max = 0; + for(int v : weights.values()){ + if(min == -1 || v < min) min = v; + else if(v > max) max = v; + } + int length = Math.max(1, (max - min) + 1); + float size = (float)filter.getRange("size")*length/2f; + long offset = filter.getLong("offset"); + offset += filter.getRange("speed")*9000f*filter.getRange("speedMult")*10f; + filter.setLong("offset", offset); + + float scaledOffset = offset/10000.0f; + + + for(Device d : weights.keySet()){ + int w = weights.get(d); + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature && frame.isIn(d,f)){ + ColorFeature c = (ColorFeature) frame.getFeature(d, f); + c.link(c.getLinkedColor().dim(waveFunction(scaledOffset, w, size, length))); + } + } + } + + return frame; + } + + public float waveFunction(float offset, int index, float size, int length){ + offset = offset % length; + if(length < offset+ 2*size && index < (offset + 2*size) - length){ + offset = (offset+ 2*size) - length; + return (float)Math.cos(PConstants.PI*(-index + offset)/size - PConstants.PI)/2f+0.5f; + }else if(offset < index && index < offset + 2*size){ + return (float)Math.cos(PConstants.PI*(-index + offset)/size - PConstants.PI)/2f+0.5f; + } + return 0f; + } + })); + + filters.put("wave_bounce", new Filter("wave_bounce", new Filter.Robot(){ + public void setup(Filter filter){ + filter.declare("size", Filter.RANGE); + filter.declare("speed", Filter.RANGE); + filter.declare("speedMult", Filter.RANGE); + filter.declare("offset", Filter.LONG); + filter.setLong("offset", 0L); + } + public Frame f(Filter filter, Frame frame){ + Map weights = filter.getGroup().getDevices(); + int min = -1; + int max = 0; + for(int v : weights.values()){ + if(min == -1 || v < min) min = v; + else if(v > max) max = v; + } + int length = Math.max(1, (max - min) + 1); + float size = (float)filter.getRange("size")*length/2f; + long offset = filter.getLong("offset"); + offset += filter.getRange("speed")*9000f*filter.getRange("speedMult")*10f; + filter.setLong("offset", offset); + + float scaledOffset = offset/10000.0f; + + + for(Device d : weights.keySet()){ + int w = weights.get(d); + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature && frame.isIn(d,f)){ + ColorFeature c = (ColorFeature) frame.getFeature(d, f); + c.link(c.getLinkedColor().dim(waveFunction(scaledOffset, w, size, length))); + } + } + } + + return frame; + } + + public float waveFunction(float offset, int index, float size, int length){ + length --; + offset = Math.abs((offset % (2*length)) -(length)); + if(offset - size <= index && index < offset + size){ + return (float)Math.cos(PConstants.PI*(-index + (offset - size))/size - PConstants.PI)/2f+0.5f; + } + return 0f; + } + })); + + filters.put("waves", new Filter("waves", new Filter.Robot(){ + public void setup(Filter filter){ + filter.declare("size", Filter.RANGE); + filter.declare("speed", Filter.RANGE); + filter.declare("min", Filter.RANGE); + filter.declare("max", Filter.RANGE); + filter.declare("speedMult", Filter.RANGE); + filter.declare("direction", Filter.STATE); + filter.declare("oneWave", Filter.STATE); + filter.declare("offset", Filter.LONG); + filter.setLong("offset", 0L); + } + public Frame f(Filter filter, Frame frame){ + Map weights = filter.getGroup().getDevices(); + int min = -1; + int max = 0; + for(int v : weights.values()){ + if(min == -1 || v < min) min = v; + else if(v > max) max = v; + } + int length = Math.max(1, (max - min) + 1); + float size = (float)filter.getRange("size")*length/2f; + long offset = filter.getLong("offset"); + float minDim = Math.min((float)filter.getRange("min"), (float)filter.getRange("max")); + float rangeDim = Math.max((float)filter.getRange("min"), (float)filter.getRange("max")) - minDim; + //MaShine.println(filter.getRange("min") + "\t" + filter.getRange("max")+ "\t" + rangeDim); + boolean cut = filter.getState("oneWave"); + + offset += (filter.getState("direction") ? 1 : -1) *filter.getRange("speed")*9000f*filter.getRange("speedMult")*10f; + filter.setLong("offset", offset); + + float scaledOffset = offset/10000.0f; + + + for(Device d : weights.keySet()){ + int w = weights.get(d); + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature && frame.isIn(d,f)){ + ColorFeature c = (ColorFeature) frame.getFeature(d, f); + float value = minDim + rangeDim * waveFunction(scaledOffset, w, size, length, cut); + //MaShine.println(value); + c.link(c.getLinkedColor().dim(value)); + } + } + } + + return frame; + } + + public float waveFunction(float offset, int index, float size, int length, boolean cut){ + if(cut) + offset = offset % length; + if(cut && length < offset+ 2*size && index < (offset + 2*size) - length){ + offset = (offset+ 2*size) - length; + return (float)Math.cos(PConstants.PI*(-index + offset)/size - PConstants.PI)/2f+0.5f; + } + if(!cut || (cut && offset < index && index < offset + 2*size)){ + return (float)Math.cos(PConstants.PI*(-index + offset)/size - PConstants.PI)/2f+0.5f; + } + return 0f; + } + })); + + filters.put("group_dimmer", new Filter("group_dimmer", new Filter.Robot(){ + public void setup(Filter filter){ + filter.declare("value", Filter.RANGE); + } + + public Frame f(Filter filter, Frame frame){ + Map weights = filter.getGroup().getDevices(); + + for(Device d : weights.keySet()){ + int w = weights.get(d); + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature && frame.isIn(d,f)){ + ColorFeature c = (ColorFeature) frame.getFeature(d, f); + c.link(c.getLinkedColor().dim((float)filter.getRange("value"))); + } + } + } + return frame; + } + })); + + filters.put("shine_adv", new Filter("shine_adv", new Filter.Robot(){ + public void setup(Filter filter){ + filter.declare("probabilty", Filter.RANGE); + } + public Frame f(Filter filter, Frame frame){ + float probabilty = (float)filter.getRange("probabilty"); + Map weights = filter.getGroup().getDevices(); + for(Device d : weights.keySet()){ + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature){ + if(Math.random() < probabilty){ + ColorFeature c = (ColorFeature) f; + c.link(new FlatColor(255)); + frame.addFeature(d, c); + } + } + } + } + return frame; + } + })); + filters.put("easing_basic", new Filter("easing_basic", new Filter.Robot(){ + public void setup(Filter filter){ + filter.declare("rate", Filter.RANGE); + filter.declare("currentFrame", Filter.FRAME); + } + public Frame f(Filter filter, Frame frame){ + Frame currentFrame = filter.getFrame("currentFrame"); + Frame targetFrame = new Frame(frame); + Frame returnFrame = new Frame(); + double rate = filter.getRange("rate"); + rate = Math.max(0.002, rate*rate); + + Map targetFeatures = targetFrame.getFeatures(); + + for(String targetId : targetFeatures.keySet()){ + + EditableFeature currentFeature = currentFrame.getFeature(targetId); + EditableFeature targetFeature = (EditableFeature)Feature.cloneFeature(targetFeatures.get(targetId)); + // if target feature is not in current, just put it in. + if(currentFeature != null){ + for(String targetFieldId : targetFeature.getFields().keySet()){ + int currentFieldValue = currentFeature.getField(targetFieldId); + int targetFieldValue = targetFeature.getField(targetFieldId); + if(currentFieldValue != targetFieldValue){ + int newValue = 0; + double change = (targetFieldValue - currentFieldValue) * rate; + if(change < 0) newValue = currentFieldValue + (int)Math.round(Math.min(-1, change)); + if(change > 0) newValue = currentFieldValue + (int)Math.round(Math.max(+1, change)); + currentFeature.setField(targetFieldId,newValue); + } + } + + returnFrame.addFeature(targetId, currentFeature); + }else{ + returnFrame.addFeature(targetId, targetFeature); + } + } + + filter.setFrame("currentFrame", returnFrame); + return returnFrame; + } + })); + + filters.put("ERY", new Filter("ERY", new Filter.Robot(){ + public Frame f(Filter filter, Frame frame){ + Map weights = filter.getGroup().getDevices(); + for(Device d : weights.keySet()){ + List feats = d.getFeatures(); + for(Feature f : feats){ + int w = weights.get(d); + if(f instanceof Coords){ + Coords cf = new Coords(); + + cf.setField("x", (int) Math.ceil(MaShine.inputs.getRange("udp.ERY."+ w +".pan"))); + cf.setField("x_", (int) Math.ceil(MaShine.inputs.getRange("udp.ERY."+ w +".panf"))); + cf.setField("y", (int) Math.ceil(MaShine.inputs.getRange("udp.ERY."+ w +".tilt"))); + cf.setField("y_", (int) Math.ceil(MaShine.inputs.getRange("udp.ERY."+ w +".tiltf"))); + frame.addFeature(d, cf); + }else if(f instanceof Zoom){ + Zoom cf = new Zoom(); + + cf.setField("z", (int) Math.ceil(MaShine.inputs.getRange("udp.ERY."+ w +".zoom"))); + cf.setField("z_", (int)Math.ceil(MaShine.inputs.getRange("udp.ERY."+ w +".zoomf"))); + frame.addFeature(d, cf); + } + } + } + return frame; + } + })); + + filters.put("shine_dim", new Filter("shine_dim", new Filter.Robot(){ + public void setup(Filter filter){ + filter.declare("probabilty", Filter.RANGE); + } + public Frame f(Filter filter, Frame frame){ + float probabilty = (float)filter.getRange("probabilty"); + Map weights = filter.getGroup().getDevices(); + for(Device d : weights.keySet()){ + List feats = d.getFeatures(); + for(Feature f : feats){ + if(f instanceof ColorFeature){ + if(Math.random() < probabilty){ + ColorFeature c = (ColorFeature) frame.getFeature(d, f); + c.link(c.getLinkedColor().dim(0f)); + } + } + } + } + return frame; + } + })); + } + + + public void addSequence(Sequence seq){sequences.add(seq);} + public void deleteSequence(Sequence seq){sequences.remove(seq);} + public ArrayList getSequences(){return sequences;} + public Sequence getSequence(int index){return sequences.get(index);} + public int getSequencesSize(){return sequences.size();} + + public ArrayList getColors(){return colors;} + + public Filter getFilter(String f){return filters.get(f);} + public HashMap getFilters(){return filters;} + + public static class SaveObject implements Serializable{ + public ArrayList sequences; + public ArrayList colors; + + public SaveObject(ArrayList sequences, ArrayList colors){ + this.sequences = sequences; + this.colors = colors; + } + } + + public Object save(){ + return new SaveObject(sequences, colors); + } + + public void restore(Object restoredObject){ + SaveObject s = (SaveObject) restoredObject; + sequences = s.sequences; + colors = s.colors; + MaShine.ui.setSelectedSequence(sequences.get(0)); + } +} \ No newline at end of file diff --git a/src/mashine/Engine.java b/src/mashine/Engine.java index 1aea5f1..50f98dd 100644 --- a/src/mashine/Engine.java +++ b/src/mashine/Engine.java @@ -7,15 +7,97 @@ package mashine; +import java.util.ArrayList; +import java.util.HashMap; + +import mashine.engine.Track; +import mashine.engine.Filter; +import mashine.scene.Frame; + public class Engine{ - public MaShine M; + private ArrayList tracks; + private ArrayList filters; + + private int filterIndex; + + public Engine(){ + tracks = new ArrayList(); + filters = new ArrayList(); - public Engine(MaShine m){ + tracks.add(new Track("A")); + tracks.add(new Track("B")); + tracks.add(new Track("C")); + tracks.add(new Track("D")); + tracks.add(new Track("E")); + tracks.add(new Track("F")); + + String dimmerName = addFilter("dimmer"); + if(dimmerName != null){ + MaShine.inputs.state(dimmerName+".enabled", "_true"); + MaShine.inputs.range(dimmerName+".value", "_100"); + } + addFilter("blackout"); + addFilter("whiteout"); } public void tick(){ + Frame frame = new Frame(); + + for(int i = 0; i < tracks.size(); i ++){ + frame = Frame.mix(frame, tracks.get(i).getFrame()); + } + + for(Filter f : filters){ + if(f.isEnabled()){ + frame = f.filter(frame); + } + } + + MaShine.ui.setDisplayedFrame(frame); + MaShine.outputs.setFrame(frame); + } + + public ArrayList getTracks(){return tracks;} + public ArrayList getFilters(){return filters;} + + public String addFilter(String type){ + if(MaShine.bank.getFilter(type) != null){ + filterIndex ++; + String name = "mixer.filter."+hex(filterIndex); + Filter f = new Filter(name, MaShine.bank.getFilter(type)); + filters.add(f); + return name+"."+type; + } + return null; + } + + public static String hex(int n) { + return String.format("%2s", Integer.toHexString(n)).replace(' ', '0'); + } + + public Object save(){ + HashMap saveObject = new HashMap(); + saveObject.put("tracks", tracks); + saveObject.put("filters", filters); + return saveObject; + } + public void restore(Object restoredObject){ + HashMap r = (HashMap) restoredObject; + filters = (ArrayList) r.get("filters"); + tracks = (ArrayList) r.get("tracks"); + for(Track t : tracks){ + t.registerActions(); + for(Filter f : t.getFilters()){ + f.redeclare(); + f.registerActions(); + } + } + for(Filter f : filters){ + f.redeclare(); + f.registerActions(); + } } } \ No newline at end of file diff --git a/src/mashine/Inputs.java b/src/mashine/Inputs.java index 818b183..b5e8ca2 100644 --- a/src/mashine/Inputs.java +++ b/src/mashine/Inputs.java @@ -5,54 +5,89 @@ * @since 13-02-2016` */ - package mashine; +package mashine; -import mashine.*; -import mashine.inputs.*; -import java.util.HashMap; +import java.util.ArrayList; +import java.util.HashMap; import java.util.Set; -import processing.event.*; + +import mashine.inputs.*; +import processing.event.KeyEvent; +import processing.event.MouseEvent; public class Inputs{ private HashMap inputs; - private MaShine M; + private ArrayList learnable; private KeyboardInputs keyboard; private MouseInputs mouse; private String lastTyped = ""; + private String lastState; + private String lastRange; private HashMap stateInputs; - private HashMap rangeInputs; + private HashMap rangeInputs; private HashMap actions; + private ArrayList ranges; + private ArrayList states; + private HashMap actionLinks; + private HashMap rangeLinks; + private HashMap stateLinks; - public Inputs(MaShine m){ - M = m; + public Inputs(){ + rangeInputs = new HashMap(); stateInputs = new HashMap(); - rangeInputs = new HashMap(); actions = new HashMap(); + ranges = new ArrayList (); + states = new ArrayList (); + actionLinks = new HashMap(); + rangeLinks = new HashMap(); + stateLinks = new HashMap(); - keyboard = new KeyboardInputs(M); - mouse = new MouseInputs(M); + keyboard = new KeyboardInputs(); + mouse = new MouseInputs(); inputs = new HashMap(){{ put("keyboard", keyboard); put("mouse", mouse); - put("minim", new MinimAnalysis(M)); - put("midi", new MidiInputs(M)); + put("minim", new MinimAnalysis()); + put("midi", new MidiInputs()); + put("ola", new OlaInput()); + put("c", new InputConstants()); + put("clock", new Clock()); + put("udp", new UDPInput()); }}; + learnable = new ArrayList(); + learnable.add(keyboard); + learnable.add((Learnable)inputs.get("midi")); + } + public void passKeyEvent(KeyEvent e){keyboard.keyEvent(e);} + public void passMouseEvent(MouseEvent e){mouse.mouseEvent(e);} + public void poll(){ + lastRange = null; + lastState = null; lastTyped = keyboard.lastTyped; + for(Learnable l : learnable){ + if(null != l.getLastState()){ + lastState = l.getLastState(); + } + if(null != l.getLastRange()){ + lastRange = l.getLastRange(); + } + } + for(String key : inputs.keySet()){ inputs.get(key).tick(); stateInputs.putAll(inputs.get(key).pollStates()); @@ -60,81 +95,84 @@ public void poll(){ inputs.get(key).clear(); } - for(String key : actionLinks.keySet()){ - String s = actionLinks.get(key); - if(stateInputs.containsKey(s)){ - if(stateInputs.get(s)){ - actions.get(key).x(); + for(String destination : actions.keySet()){ + String source = actionLinks.get(destination); + if(stateInputs.containsKey(source)){ + if(stateInputs.get(source)){ + actions.get(destination).x(); } } } } + public Set getStateInputSet(){return stateInputs.keySet();} + public Set getRangeInputSet(){return rangeInputs.keySet();} + + public boolean hasRange(String inputName){ return rangeInputs.containsKey(inputName);} + public boolean hasState(String inputName){ return stateInputs.containsKey(inputName);} + + public void setRange(String inputName, double range){ rangeInputs.put(inputName, range);} + public void setState(String inputName, Boolean state){ stateInputs.put(inputName, state);} + public boolean getState(String inputName){ if(stateInputs.containsKey(inputName)){ return stateInputs.get(inputName); + }else if(stateLinks.containsKey(inputName)){ + return getState(stateLinks.get(inputName)); }else{ return false; } } - public boolean hasState(String inputName){ - return stateInputs.containsKey(inputName); - } - - public void setState(String inputName, Boolean state){ - stateInputs.put(inputName, state); - } - - public float getRange(String inputName){ + public double getRange(String inputName){ if(rangeInputs.containsKey(inputName)){ return rangeInputs.get(inputName); + }else if(rangeLinks.containsKey(inputName)){ + return getRange(rangeLinks.get(inputName)); }else{ - return (float)0.0; + return 0.0; } } - public boolean hasRange(String inputName){ - return rangeInputs.containsKey(inputName); - } - - public void setRange(String inputName, float range){ - rangeInputs.put(inputName, range); - } - - public void passKeyEvent(KeyEvent e){ - keyboard.keyEvent(e); - } - - public void passMouseEvent(MouseEvent e){ - mouse.mouseEvent(e); - } - - public void register(String actionName, Do action){ - actions.put(actionName, action); - } - - public void link(String actionName, String stateInputName){ - actionLinks.put(actionName, stateInputName); - } - - public void unlink(String actionName){ - actionLinks.remove(actionName); - } - - public Set getActionSet(){ - return actions.keySet(); - } - - public Set getStateSet(){ - return stateInputs.keySet(); - } - public Set getRangeSet(){ - return rangeInputs.keySet(); - } - - public String getLastKey(){ - return lastTyped; + public void registerAction(String destination, Do action){ if(!actions.containsKey(destination)) actions.put(destination, action);} + public void registerState(String destination){ if(!states.contains(destination)) states.add(destination);} + public void registerRange(String destination){ if(!ranges.contains(destination)) ranges.add(destination);} + + public void link(String destination, String source){actionLinks.put(destination, source);} + public void range(String destination, String source){rangeLinks.put(destination, source);} + public void state(String destination, String source){stateLinks.put(destination, source);} + + public void unlink(String destination){ actionLinks.remove(destination);} + public void unrange(String destination){ rangeLinks.remove(destination);} + public void unstate(String destination){ stateLinks.remove(destination);} + + public HashMap getActionLinks(){ return actionLinks;} + public HashMap getRangeLinks(){ return rangeLinks;} + public HashMap getStateLinks(){ return stateLinks;} + + public Set getActionSet(){ return actions.keySet();} + public ArrayList getRangeSet(){ return ranges;} + public ArrayList getStateSet(){ return states;} + + public Do getAction(String actionName){return actions.get(actionName);} + + public String getLastKey(){ return lastTyped;} + public String getLastState(){ return lastState;} + public String getLastRange(){ return lastRange;} + + public Object save(){ + HashMap saveObject = new HashMap(); + saveObject.put("actions", actionLinks); + saveObject.put("states", stateLinks); + saveObject.put("ranges", rangeLinks); + return saveObject; + } + + public void restore(Object restoredObject){ + HashMap r = (HashMap) restoredObject; + actionLinks = (HashMap) r.get("actions"); + stateLinks = (HashMap) r.get("states"); + rangeLinks = (HashMap) r.get("ranges"); } } diff --git a/src/mashine/MaShine.java b/src/mashine/MaShine.java index 912a5cb..ee6f456 100644 --- a/src/mashine/MaShine.java +++ b/src/mashine/MaShine.java @@ -7,52 +7,80 @@ package mashine; -import processing.core.*; -import processing.event.*; -import mashine.ui.FlatColor; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.HashMap; + +import processing.core.PApplet; +import processing.event.KeyEvent; +import processing.event.MouseEvent; public class MaShine extends PApplet{ + public static String VERSION = "0.1"; + private static final String[] MAIN_WINDOW = new String[] { "mashine.MaShine" }; + private String lastSavedTo = ""; + private String lastBackupFile = ""; - public Inputs inputs; - public Outputs outputs; - public Scene scene; - public UI ui; + public static Inputs inputs; + public static Outputs outputs; + public static Engine engine; + public static Scene scene; + public static Bank bank; + public static UI ui; + public static MaShine m; public static void main(String[] args) { PApplet.main(MAIN_WINDOW); } public void settings() { - size(1200, 700, PApplet.P3D); + size(1920, 1080, PApplet.P3D); noSmooth(); } public void setup() { - frameRate(60); - //surface.setResizable(true); + println("MaShine "+ VERSION + " / procsynth"); + m = this; + frameRate(50); + surface.setResizable(true); - inputs = new Inputs(this); - outputs = new Outputs(this); - scene = new Scene(this); - ui = new UI(this); + outputs = new Outputs(); + inputs = new Inputs(); + bank = new Bank(); + scene = new Scene(); + engine = new Engine(); + ui = new UI(); - inputs.register("mashine.test", new Do(){public void x(){println("TEST");}}); - inputs.link("mashine.test", "keyboard.32"); + inputs.registerAction("mashine.test", new Do(){public void x(){println("TEST");}}); + inputs.registerAction("mashine.save_as", new Do(){public void x(){save();}}); + inputs.registerAction("mashine.open", new Do(){public void x(){restore();}}); + inputs.registerAction("mashine.save", new Do(){public void x(){save(lastSavedTo);}}); + inputs.registerAction("mashine.restore", new Do(){public void x(){restore(lastBackupFile);}}); + inputs.link("mashine.save", "keyboard.97.press"); + inputs.link("mashine.open", "keyboard.98.press"); + inputs.link("mashine.restore", "keyboard.99.press"); } public void draw() { background(55, 71, 79); inputs.poll(); - outputs.push(); + engine.tick(); ui.draw(); + outputs.push(); } public void keyPressed(KeyEvent e){ + if (key == ESC) {key = 0;} // prevent window from closing inputs.passKeyEvent(e); } + public void keyReleased(KeyEvent e){ inputs.passKeyEvent(e); } @@ -66,4 +94,94 @@ public void mousePressed(MouseEvent e) { public void mouseReleased(MouseEvent e) { inputs.passMouseEvent(e); } + + public void save(){ + println("Requesting path to save to."); + selectOutput("Select file to save to.", "save"); + } + + public void save(File file){ + if(file != null) + save(file.getAbsolutePath()); + } + + public void save(String path){ + + if(path.equals("")){ + save(); + return; + } + + if(!path.equals(lastBackupFile)){ + lastSavedTo = path; + ui.status.set("file", path.split("/")[path.split("/").length -1]); + } + + HashMap saveObject = new HashMap(); + saveObject.put("scene", scene.save()); + saveObject.put("bank", bank.save()); + saveObject.put("inputs", inputs.save()); + saveObject.put("engine", engine.save()); + saveObject.put("ui", ui.save()); + + try{ + FileOutputStream fileOut = new FileOutputStream(path); + ObjectOutputStream out = new ObjectOutputStream(fileOut); + out.writeObject(saveObject); + out.close(); + fileOut.close(); + println("Serialized data is saved in "+ path); + }catch(IOException i){ + i.printStackTrace(); + } + + } + + public void restore(){ + println("Requesting path to open from."); + selectInput("Select file to open.", "restore"); + } + + public void restore(File file){ + if(file != null){ + restore(file.getAbsolutePath()); + } + } + + public void restore(String path){ + lastBackupFile = "mashine_"+timestamp()+".backup.mashine"; + save(lastBackupFile); + try + { + FileInputStream fileIn = new FileInputStream(path); + ObjectInputStream in = new ObjectInputStream(fileIn); + HashMap restoredObject = (HashMap) in.readObject(); + in.close(); + fileIn.close(); + + scene.restore(restoredObject.get("scene")); + bank.restore(restoredObject.get("bank")); + inputs.restore(restoredObject.get("inputs")); + engine.restore(restoredObject.get("engine")); + ui.restore(restoredObject.get("ui")); + + println("Restored from "+ path); + ui.status.set("file", path.split("/")[path.split("/").length -1]); + + }catch(IOException i) + { + i.printStackTrace(); + return; + }catch(ClassNotFoundException c) + { + System.out.println("Some class not found"); + c.printStackTrace(); + ui.status.set("file", "failed to load " + path.split("/")[path.split("/").length -1]); + return; + } + } + + private String timestamp(){ + return year()+"-"+month()+"-"+day()+"_"+hour()+"-"+minute()+"-"+second(); + } } diff --git a/src/mashine/Outputs.java b/src/mashine/Outputs.java index c08e540..f5abd1f 100644 --- a/src/mashine/Outputs.java +++ b/src/mashine/Outputs.java @@ -4,30 +4,40 @@ * @author procsynth - Antoine Pintout * @since 13-02-2016` */ - - package mashine; - import java.util.HashMap; - import mashine.outputs.*; +package mashine; - public class Outputs{ +import java.util.HashMap; - public MaShine M; +import mashine.outputs.*; +import mashine.scene.Frame; - private HashMap outputs; +public class Outputs{ - public Outputs(MaShine m){ - M = m; + private HashMap outputs; + private Frame frame; + public Ola ola; - outputs = new HashMap(); + public Outputs(){ + outputs = new HashMap(); + ola = new Ola(); + outputs.put("OLA", ola); + //outputs.put("UDP", new UDP()); + } - outputs.put("OLA", new Ola(M)); - } + public void push(){ + if(null != frame){ + for(Output o : outputs.values()){ + o.push(frame); + } + frame = null; + } + } - public void push(){ - for(String o : outputs.keySet()){ - outputs.get(o).push(); - } - } + public void setFrame(Frame frame){ + if (null == this.frame) { + this.frame = frame; + } + } - } \ No newline at end of file +} \ No newline at end of file diff --git a/src/mashine/Scene.java b/src/mashine/Scene.java index 7792de9..17d6edb 100644 --- a/src/mashine/Scene.java +++ b/src/mashine/Scene.java @@ -7,40 +7,97 @@ package mashine; +import java.util.ArrayList; +import java.util.HashMap; +import java.io.Serializable; + +import mashine.scene.Device; +import mashine.scene.DeviceGroup; import mashine.scene.Frame; -import mashine.scene.*; import mashine.scene.features.*; -import java.util.HashMap; public class Scene{ - private MaShine M; - private HashMap devices; // for the frames - private HashMap groups; // for the filters + private ArrayList devices; // for the frames + private ArrayList groups; // for the filters private Frame frameZero; - public Scene(MaShine m){ + public static HashMap> FEATURES; + + static{ + FEATURES = new HashMap>(); + FEATURES.put(Tradi.class.getName(), Tradi.class); + FEATURES.put(RGBW.class.getName(), RGBW.class); + FEATURES.put(RGB.class.getName(), RGB.class); + FEATURES.put(Coords.class.getName(), Coords.class); + FEATURES.put(Zoom.class.getName(), Zoom.class); + } - M = m; + public Scene(){ - devices = new HashMap(); - Device testDevice = new Device("testDevice", 1, 42, 10, 10, 200, 50); - Device testDevice2 = new Device("testDevice2", 6, 42, 10, 65, 200, 60); - testDevice.addFeature(new RGBW(255)); + groups = new ArrayList(); + + devices = new ArrayList(); + Device testDevice = new Device("RGB", 1, 0, 10, 10, 200, 50); + Device testDevice2 = new Device("RGBW", 5, 0, 10, 65, 200, 50); + testDevice.addFeature(new RGB(255)); + testDevice.addFeature(new SingleField("dimmer", 255)); testDevice.addFeature(new FixedField("strobe", 255)); testDevice2.addFeature(new RGBW(255)); + testDevice2.addFeature(new SingleField("dimmer", 255)); testDevice2.addFeature(new FixedField("strobe", 255)); - testDevice2.addFeature(new FixedField("dimmer", 255)); - devices.put(testDevice.getIdentifier(), testDevice); - devices.put(testDevice2.getIdentifier(), testDevice2); + devices.add(testDevice); + devices.add(testDevice2); + frameZero = new Frame(devices); } - public HashMap getDevices(){ - return new HashMap(devices); + public ArrayList getDevices(){ + return new ArrayList(devices); + } + public void addDevice(Device d){ + devices.add(d); + } + + public void removeDevice(Device d){ + devices.remove(d); } public Frame getDefaultFrame(){ return new Frame(frameZero); } + + public void renameDevice(Device device, String newName){ + device.setName(newName); + } + + public void addGroup(DeviceGroup grp){groups.add(grp);} + public void deleteGroup(DeviceGroup grp){groups.remove(grp);} + public ArrayList getGroups(){return new ArrayList(groups);} + public DeviceGroup getGroup(int index){return groups.get(index);} + public int getGroupsSize(){return groups.size();} + + public static class SaveObject implements Serializable{ + public ArrayList devices; + public ArrayList groups; + + public SaveObject( + ArrayList devices, + ArrayList groups + ){ + this.devices = devices; + this.groups = groups; + } + } + + public Object save(){ + return new SaveObject(devices, groups); + } + + public void restore(Object restoredObject){ + SaveObject s = (SaveObject) restoredObject; + devices = s.devices; + groups = s.groups; + MaShine.ui.reloadElements(); + } } \ No newline at end of file diff --git a/src/mashine/UI.java b/src/mashine/UI.java index 1f685ff..9c06f42 100644 --- a/src/mashine/UI.java +++ b/src/mashine/UI.java @@ -7,23 +7,52 @@ package mashine; -import mashine.*; -import mashine.ui.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedList; + import mashine.scene.Device; +import mashine.scene.DeviceGroup; import mashine.scene.Frame; +import mashine.scene.Sequence; +import mashine.ui.FlatColor; +import mashine.ui.Focusable; +import mashine.ui.Status; +import mashine.ui.boxes.ColorPalette; +import mashine.ui.boxes.DataViewer; +import mashine.ui.boxes.DeviceEditor; +import mashine.ui.boxes.DeviceSelector; +import mashine.ui.boxes.EngineView; +import mashine.ui.boxes.EventViewer; +import mashine.ui.boxes.Linker; +import mashine.ui.boxes.Menu; +import mashine.ui.boxes.SceneView; +import mashine.ui.boxes.SequenceEditor; +import mashine.ui.boxes.SequenceSelector; +import mashine.ui.boxes.FilterSelector; +import mashine.ui.boxes.ControlSurface; + import processing.core.PFont; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Comparator; + +import java.io.Serializable; public class UI{ - private Status status; private Menu menu; - private SceneVisualizer sceneVisualizer; private HashMap uiElements; private LinkedList openedUiElements; - private MaShine M; + private ArrayList toBeOpenedUiElements; + private Frame displayFrame; + public SceneView sceneView; + public EngineView engineView; + public SequenceSelector sequenceSelector; + public DeviceSelector deviceSelector; + public ColorPalette colorPalette; + public ControlSurface controlSurface; + public Linker linker; + public Status status; public PFont TEXTFONT; public PFont TITLEFONT; public int TEXTSIZE; @@ -33,9 +62,9 @@ public static final class SortByFocus implements Comparator { public int compare(Focusable a, Focusable b) { boolean mouseInA = a.mouseIn(); boolean mouseInB = b.mouseIn(); - if(a.hasFocus() && a.M.mousePressed) + if(a.hasFocus() && MaShine.m.mousePressed) return 1; // b get drawn first, a on top - else if (b.hasFocus() && a.M.mousePressed) + else if (b.hasFocus() && MaShine.m.mousePressed) return -1; // b on top else if(mouseInA == mouseInB) @@ -48,30 +77,45 @@ else if (b.hasFocus() && a.M.mousePressed) } } - public UI(MaShine m){ - M = m; - M.colorMode(MaShine.RGB); + public UI(){ + MaShine.m.colorMode(MaShine.RGB); - TEXTFONT = M.loadFont("RobotoMono-Light-11.vlw"); + TEXTFONT = MaShine.m.loadFont("data/RobotoMono-Light-11.vlw"); TEXTSIZE = 11; - M.textFont(TEXTFONT); - M.textSize(TEXTSIZE); + MaShine.m.textFont(TEXTFONT); + MaShine.m.textSize(TEXTSIZE); - status = new Status(M); - menu = new Menu(M); - sceneVisualizer = new SceneVisualizer(M); + status = new Status(); + menu = new Menu(); + sceneView = new SceneView(); + engineView = new EngineView(); + sequenceSelector = new SequenceSelector(); + deviceSelector = new DeviceSelector(); + colorPalette = new ColorPalette(); + linker = new Linker(); + controlSurface = new ControlSurface(); uiElements = new HashMap(); - uiElements.put("EventViewer", new EventViewer(M)); - uiElements.put("DataViewer", new DataViewer(M)); - uiElements.put("DeviceEditor", new DeviceEditor(M)); + uiElements.put("EventViewer", new EventViewer()); + uiElements.put("DataViewer", new DataViewer()); + uiElements.put("DeviceEditor", new DeviceEditor()); + uiElements.put("DeviceSelector", deviceSelector); + uiElements.put("SequenceSelector", sequenceSelector); + uiElements.put("ColorPalette", colorPalette); + uiElements.put("SequenceEditor", new SequenceEditor()); + uiElements.put("Linker", linker); + uiElements.put("FilterSelector", new FilterSelector()); + uiElements.put("ControlSurface", controlSurface); openedUiElements = new LinkedList(); - open("EventViewer");open("DataViewer");open("DeviceEditor"); + toBeOpenedUiElements = new ArrayList(); + + displayFrame = new Frame(); + + open("ControlSurface"); } public void close(String uiElementName){ - M.println("Close el from name"); if(uiElements.containsKey(uiElementName)){ Focusable el = uiElements.get(uiElementName); openedUiElements.remove(el); @@ -80,7 +124,6 @@ public void close(String uiElementName){ } public void close(Focusable el){ - M.println("Close el from object"); if(uiElements.containsValue(el)){ openedUiElements.remove(el); el.defocus(); @@ -90,20 +133,30 @@ public void close(Focusable el){ public void open(String uiElementName){ if(uiElements.containsKey(uiElementName)){ Focusable el = uiElements.get(uiElementName); - openedUiElements.add(el); - //el.focus(); + if(!openedUiElements.contains(el)){ + toBeOpenedUiElements.add(el); + } } } public void draw(){ //M.strokeWeight((float)0.5); //M.strokeJoin(M.MITER); + menu.draw(); - //sceneVisualizer.setFrame(M.scene.getDefaultFrame()); - //sceneVisualizer.setFrame(new Frame()); + if(null == displayFrame){ + displayFrame = new Frame(); + } + sceneView.setFrame(displayFrame); + displayFrame = null; + sceneView.draw(); + engineView.draw(); - - sceneVisualizer.draw(); + for(Focusable f : toBeOpenedUiElements){ + openedUiElements.add(f); + } + + toBeOpenedUiElements.clear(); openedUiElements.sort(new SortByFocus()); @@ -113,17 +166,51 @@ public void draw(){ el.defocus(); } - if(!openedUiElements.isEmpty()) - if(openedUiElements.getLast().mouseIn()) + sceneView.defocus(); + + if(!openedUiElements.isEmpty()){ + if(openedUiElements.getLast().mouseIn()){ openedUiElements.getLast().focus(); - else - sceneVisualizer.focus(); + }else if(sceneView.mouseIn()){ + sceneView.focus(); + } + }else if(sceneView.mouseIn()){ + sceneView.focus(); + } status.draw(); } - public HashMap getSelectedDevices(){ - return sceneVisualizer.getSelectedDevices(); + + public ArrayList getSelectedDevices(){return sceneView.getSelectedDevices();} + public void setSelectedDevices(ArrayList newSelection){sceneView.setSelectedDevices(newSelection);} + public void clearSelectedDevices(){sceneView.clearSelectedDevices();} + public void reloadElements(){sceneView.reloadElements();} + + public Sequence getSelectedSequence(){return sequenceSelector.getSelectedSequence();} + public void setSelectedSequence(Sequence s){sequenceSelector.setSelectedSequence(s);} + + public FlatColor getSelectedColor(){return colorPalette.getSelectedColor();} + public void setSelectedColor(FlatColor c){colorPalette.setSelectedColor(c);} + + public DeviceGroup getSelectedGroup(){return deviceSelector.getSelectedGroup();} + public void setSelectedGroup(DeviceGroup g){deviceSelector.setSelectedGroup(g);} + + public void setDisplayedFrame(Frame frame){if(null == displayFrame)displayFrame = frame;} + + public static class SaveObject implements Serializable{ + public List sliderValues; + + public SaveObject(List sliderValues){this.sliderValues = sliderValues;} + } + + public Object save(){ + return new SaveObject(controlSurface.getValues()); + } + + public void restore(Object restoredObject){ + SaveObject s = (SaveObject) restoredObject; + controlSurface.setValues(s.sliderValues); } } diff --git a/src/mashine/engine/Filter.java b/src/mashine/engine/Filter.java index cc113cb..fa533e3 100644 --- a/src/mashine/engine/Filter.java +++ b/src/mashine/engine/Filter.java @@ -7,11 +7,136 @@ package mashine.engine; -import mashine.*; +import mashine.MaShine; +import mashine.Do; +import mashine.scene.Frame; +import mashine.scene.DeviceGroup; -public class Filter{ +import java.io.Serializable; +import java.lang.Math; +import java.util.ArrayList; +import java.util.HashMap; - public Filter(){ +public class Filter implements Serializable{ + private static final long serialVersionUID = 0xF1750001L; + + public static final short RANGE = 0; + public static final short STATE = 1; + public static final short FRAME = 3; + public static final short LONG = 4; + + private HashMap parameters; + private DeviceGroup group; + private HashMap frames; + private HashMap longs; + + private Robot robot; + + private String name; + private String type; + private Boolean enabled; + + public static abstract class Robot implements Serializable{ + public void setup(Filter filter){}; + public abstract Frame f(Filter filter, Frame frame); + } + + public Filter(String name, Robot robot){ + this.name = this.type = name; + this.robot = robot; + this.enabled = false; + } + + public Filter(String parent, Filter f){ + this.type = f.getType(); + this.name = parent + "." + f.getName(); + this.robot = f.getRobot(); + parameters = new HashMap(); + frames = new HashMap(); + longs = new HashMap(); + robot.setup(this); + this.enabled = false; + + registerActions(); + } + + public Frame filter(Frame f){ + return robot.f(this, f); + } + public void declare(String param, short type){ + if(type == RANGE){ + MaShine.inputs.registerRange(this.name+"."+param); + }else if(type == STATE){ + MaShine.inputs.registerState(this.name+"."+param); + }else if(type == FRAME){ + frames.put(param, new Frame()); + }else if(type == LONG){ + longs.put(param, 0L); + } + parameters.put(param, type); + } + + public void redeclare(){ + for(String param : parameters.keySet()){ + int type = parameters.get(param); + if(type == RANGE){ + MaShine.inputs.registerRange(this.name+"."+param); + }else if(type == STATE){ + MaShine.inputs.registerState(this.name+"."+param); + } + } } + + public void registerActions(){ + MaShine.inputs.registerState(this.name+".enabled"); + MaShine.inputs.registerAction(this.name+".toggle", new Do(){public void x(){toggle();}}); + MaShine.inputs.registerAction(this.name+".enable", new Do(){public void x(){enable();}}); + MaShine.inputs.registerAction(this.name+".disable", new Do(){public void x(){disable();}}); + } + + public double getRange(String param){ + if(parameters.containsKey(param) && parameters.get(param) == RANGE){ + return MaShine.inputs.getRange(this.name + "." + param); + } + return 0.0; + } + + public boolean getState(String param){ + if(parameters.containsKey(param) && parameters.get(param) == STATE){ + return MaShine.inputs.getState(this.name + "." + param); + } + return false; + } + + public long getLong(String param){ + if(parameters.containsKey(param) && parameters.get(param) == LONG && null != longs.get(param)){ + return longs.get(param); + } + return 0L; + } + + public Frame getFrame(String param){ + if(parameters.containsKey(param) && parameters.get(param) == FRAME && null != frames.get(param)){ + return frames.get(param); + } + return new Frame(); + } + + public DeviceGroup getGroup(){return (null != group ? group : new DeviceGroup(""));} + public void setGroup(DeviceGroup g){group = g;} + + public String getName(){return name;} + public String getType(){return type;} + public Robot getRobot(){return robot;} + public HashMap getParameters(){return parameters;} + public boolean isEnabled(){return enabled || MaShine.inputs.getState(this.name+".enabled");} + public void disable(){enabled = false;} + public void enable(){enabled = true;} + public void toggle(){enabled = !enabled;} + + public void setLong(String param, Long value){longs.put(param, value);} + public void setFrame(String param, Frame value){frames.put(param, value);} + + } \ No newline at end of file diff --git a/src/mashine/engine/Mixer.java b/src/mashine/engine/Mixer.java deleted file mode 100644 index 2d487e4..0000000 --- a/src/mashine/engine/Mixer.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Instance to generate a frame from multiple tracks - * - * @author procsynth - Antoine Pintout - * @since 13-02-2016` - */ - -package mashine.engine; - -import mashine.*; - -public class Mixer{ - - public Mixer(){ - - } -} \ No newline at end of file diff --git a/src/mashine/engine/Sequencer.java b/src/mashine/engine/Sequencer.java index ae573b6..c442696 100644 --- a/src/mashine/engine/Sequencer.java +++ b/src/mashine/engine/Sequencer.java @@ -7,11 +7,131 @@ package mashine.engine; -import mashine.*; +import java.io.Serializable; -public class Sequencer{ +import mashine.Do; +import mashine.MaShine; +import mashine.scene.Frame; +import mashine.scene.Sequence; - public Sequencer(){ +public class Sequencer implements Serializable{ + private static final long serialVersionUID = 0x53050001L; + + private String name; + private Sequence sequence; + private boolean tweaking = false; + private boolean manual = false; + private boolean loop = true; + private int clip; + private int offset; + private int index; + + public Sequencer(String name, Sequence sequence){ + this.name = name; + this.sequence = sequence; + this.clip = 0; + this.offset = 0; + this.index = 0; + + MaShine.inputs.link("sequencer."+name+".forward.auto", "minim.beat.interpolated"); + registerActions(); + } + + public void registerActions(){ + MaShine.inputs.registerAction("sequencer."+name+".manual.start", new Do(){public void x(){manual = true;}}); + MaShine.inputs.registerAction("sequencer."+name+".manual.end", new Do(){public void x(){manual = false;}}); + + MaShine.inputs.registerAction("sequencer."+name+".reset", new Do(){public void x(){setIndex(clip);}}); + MaShine.inputs.registerAction("sequencer."+name+".loop.toggle", new Do(){public void x(){loop = !loop;}}); + MaShine.inputs.registerAction("sequencer."+name+".loop.on", new Do(){public void x(){loop = true;}}); + MaShine.inputs.registerAction("sequencer."+name+".loop.off", new Do(){public void x(){loop = false;}}); + + MaShine.inputs.registerAction("sequencer."+name+".clip.less.tweak", new Do(){public void x(){if(tweaking)setClip(clip +1);}}); + MaShine.inputs.registerAction("sequencer."+name+".clip.more.tweak", new Do(){public void x(){if(tweaking)setClip(clip -1);}}); + MaShine.inputs.registerAction("sequencer."+name+".offset.less.tweak", new Do(){public void x(){if(tweaking)setOffset(offset +1);}}); + MaShine.inputs.registerAction("sequencer."+name+".offset.more.tweak", new Do(){public void x(){if(tweaking)setOffset(offset -1);}}); + + MaShine.inputs.registerAction("sequencer."+name+".forward.manual", new Do(){public void x(){if(manual)setIndex(index +1);}}); + MaShine.inputs.registerAction("sequencer."+name+".backward.manual", new Do(){public void x(){if(manual)setIndex(index -1);}}); + + MaShine.inputs.registerAction("sequencer."+name+".forward.auto", new Do(){public void x(){if(!manual)setIndex(index +1);}}); + MaShine.inputs.registerAction("sequencer."+name+".backward.auto", new Do(){public void x(){if(!manual)setIndex(index -1);}}); } + + public void setClip(int v){ + if(v == 0){ + clip = 0; + }else{ + clip = Math.max(1, Math.min(v, sequence.getSize() - offset)); + } + }; + public void setOffset(int v){ + offset = Math.max(0, Math.min(v, sequence.getSize() - 2)); + setClip(clip); + }; + public void setIndex(int v){ + setOffset(offset); + if(clip == 0){ + if(v < offset){ + v = sequence.getSize() - 1; + } + if(v >= sequence.getSize()){ + if(loop) + v = offset; + else + v = sequence.getSize() - 1; + } + }else{ + if(v < offset){ + v = offset + clip - 1; + } + + if(v >= offset + clip ){ + if(loop) + v = offset; + else + v = (offset + clip) - 1; + } + } + index = v; + }; + + public Frame getFrame(){ + return sequence.getFrame(index); + } + + public Sequence getSequence(){ + return sequence; + } + public void setSequence(Sequence sequence){ + this.sequence = sequence; + setIndex(0); + } + + public String getName(){ + return name; + } + + public int getClip(){return clip;} + public int getOffset(){return offset;} + public int getIndex(){return index;} + + public void startTweak(){ + MaShine.ui.open("SequenceSelector"); + tweaking = true; + } + public void endTweak(){ + MaShine.ui.close("SequenceSelector"); + tweaking = false; + } + + public boolean isTweaked(){ + return tweaking; + } + + public boolean isManual(){ + return manual; + } + } \ No newline at end of file diff --git a/src/mashine/engine/Track.java b/src/mashine/engine/Track.java index efc9c17..a083e21 100644 --- a/src/mashine/engine/Track.java +++ b/src/mashine/engine/Track.java @@ -7,12 +7,72 @@ package mashine.engine; -import mashine.*; +import java.io.Serializable; +import java.util.ArrayList; -public class Track{ +import mashine.MaShine; +import mashine.Do; +import mashine.scene.Frame; +public class Track implements Serializable{ - public Track(){ + private static final long serialVersionUID = 0x1A0F0001L; + private String name; + public Sequencer sequencer; + private ArrayList filters; + private int filterIndex; + + private boolean tweaked = false; + + public Track(String name){ + this.name = name; + sequencer = new Sequencer(name, MaShine.bank.getSequence(0)); + filters = new ArrayList(); + + String filterName = addFilter("dimmer"); + if(filterName != null){ + MaShine.inputs.state(filterName+".enabled", "_true"); + MaShine.inputs.range(filterName+".value", "_100"); + } + registerActions(); + } + + public void registerActions(){ + MaShine.inputs.registerAction("track."+name+".tweak.start", new Do(){public void x(){startTweak();}}); + MaShine.inputs.registerAction("track."+name+".tweak.end", new Do(){public void x(){endTweak();}}); + sequencer.registerActions(); + } + + public Frame getFrame(){ + Frame frame = new Frame(sequencer.getFrame()); + for(Filter f : filters){ + if(f.isEnabled()){ + frame = f.filter(frame); + } + } + return frame; + } + + public String addFilter(String type){ + if(MaShine.bank.getFilter(type) != null){ + filterIndex ++; + String name = "track."+this.name+".filter."+hex(filterIndex); + Filter f = new Filter(name, MaShine.bank.getFilter(type)); + filters.add(f); + return name+"."+type; + } + return null; + } + + public ArrayList getFilters(){return filters;} + public String getName(){return name;} + + public boolean isTweaked(){return tweaked;} + public void startTweak(){sequencer.startTweak(); tweaked = true;} + public void endTweak(){sequencer.endTweak();tweaked = false;} + + public static String hex(int n) { + return String.format("%2s", Integer.toHexString(n)).replace(' ', '0'); } } \ No newline at end of file diff --git a/src/mashine/inputs/AutoCorrelationPitchDetector.java b/src/mashine/inputs/AutoCorrelationPitchDetector.java new file mode 100644 index 0000000..9537ad5 --- /dev/null +++ b/src/mashine/inputs/AutoCorrelationPitchDetector.java @@ -0,0 +1,105 @@ +/* + * Pitch dectection by Autocorrelation + * + * Freely adapted from the works of + * L. Anton-Canalis (info@luisanton.es) + * https://github.com/Notnasiul/R2D2-Processing-Pitch + */ + +package mashine.inputs; + +import java.util.Arrays; + +import ddf.minim.AudioBuffer; + +public class AutoCorrelationPitchDetector { + + private float sampleRate; + private double lastPeriod; + private double currentFrequency; + private long t; + + private final double FMIN = 20; + private final double FMAX = 440; + + private int minShift; + private int maxShift; + + private double []freqBuffer = new double[10]; + private double []sortedFreqBuffer = new double[10]; + private int freqBufferIndex = 0; + + + AutoCorrelationPitchDetector (float sampleRate) { + this.sampleRate = sampleRate; + double tmin = 1.0 / FMAX; + double tmax = 1.0 / FMIN; + minShift = (int)Math.round(tmin * sampleRate ); + maxShift = (int)Math.round(tmax * sampleRate ); + lastPeriod = maxShift; + t = 0; + } + + private void setFrequency(double f) { + if(f < FMAX){ + freqBuffer[freqBufferIndex] = f; + freqBufferIndex++; + freqBufferIndex = freqBufferIndex % freqBuffer.length; + sortedFreqBuffer = freqBuffer.clone(); + Arrays.sort(sortedFreqBuffer); + currentFrequency = sortedFreqBuffer[5]; + currentFrequency = f; + } + } + + public double getFrequency() { + return sortedFreqBuffer[7]; + } + public double[] getFrequencies() { + return sortedFreqBuffer; + } + + void forward(AudioBuffer buffer) { + float[] audio = buffer.toArray(); + + t++; + int buffer_index = 0; + + double max_sum = 0; + int period = 0; + for (int shift = minShift; shift < maxShift; shift++) + { + // Assigh higher weights to lower frequencies + // and even higher to periods that are closer to the last period (quick temporal coherence hack) + double mod = (shift - minShift) / (maxShift - minShift); + mod *= 1.0 - 1.0 / (1.0 + Math.abs(shift - lastPeriod)); + + // Compare samples with shifted samples using autocorrelation + double dif = 0; + for (int i = shift; i < audio.length; i++) + dif += audio[i] * audio[i - shift]; + + // Apply weight + dif *= 1.0 + mod; + + if (dif > max_sum) + { + max_sum = dif; + period = shift; + } + } + + if (period != 0){ + lastPeriod = period; + double freq = 1.0f / period; + freq *= sampleRate; + setFrequency(freq); + buffer_index += period + minShift; + } + else { + lastPeriod = (maxShift + minShift) / 2; + setFrequency(0); + buffer_index += minShift; + } + } +}; diff --git a/src/mashine/inputs/Clock.java b/src/mashine/inputs/Clock.java new file mode 100644 index 0000000..a7f2a21 --- /dev/null +++ b/src/mashine/inputs/Clock.java @@ -0,0 +1,71 @@ +/** + * Clock input + * + * @author procsynth - Antoine Pintout + * @since 13-02-2016` + */ + +package mashine.inputs; + +import java.util.ArrayList; +import mashine.MaShine; + +public class Clock extends InputSource { + + private ArrayList rates; + private ArrayList str; + private ArrayList next; + + private static final long MINUTE = 60000L; + private static final long SECOND = 1000L; + + private boolean init = false; + + public Clock () { + super(); + next = new ArrayList(); + str = new ArrayList(); + rates = new ArrayList(); + + rates.add(MINUTE/120); + rates.add(SECOND/35); + rates.add(SECOND/25); + rates.add(SECOND/10); + rates.add(SECOND/1); + + for(int i = 0; i < rates.size(); i ++){ + str.add(rates.get(i)+""); + next.add(0L); + } + + str.add("variable"); + next.add(0L); + + } + + public void tick(){ + if(!init){ init = true; MaShine.inputs.registerRange("clock.variable.adjust"); } + long now = MaShine.m.millis(); + int i = 0; + for(Long n : next){ + if(n <= now){ + long rate; + if(str.get(i).equals("variable")){ + rate = Math.round(SECOND * MaShine.inputs.getRange("clock.variable.adjust")); + //MaShine.println(rate); + }else{ + rate = rates.get(i); + } + states.put("clock."+str.get(i), true); + next.set(i, now+rate); + } + i++; + } + } + + public void clear(){ + for(String key : states.keySet()){ + states.put(key, false); + } + } +} \ No newline at end of file diff --git a/src/mashine/inputs/InputConstants.java b/src/mashine/inputs/InputConstants.java new file mode 100644 index 0000000..2bce7e0 --- /dev/null +++ b/src/mashine/inputs/InputConstants.java @@ -0,0 +1,24 @@ +/** + * Mother class for all inputs + * + * @author procsynth - Antoine Pintout + * @since 13-02-2016` + */ + +package mashine.inputs; + +import java.util.HashMap; + +public class InputConstants extends InputSource { + + public InputConstants () { + super(); + states.put("_false", false); + states.put("_true", true); + ranges.put("_100",1.0); + ranges.put("_75",0.75); + ranges.put("_50",0.5); + ranges.put("_25",0.25); + ranges.put("_0",0.0); + } +} \ No newline at end of file diff --git a/src/mashine/inputs/InputSource.java b/src/mashine/inputs/InputSource.java index cf106ea..0606381 100644 --- a/src/mashine/inputs/InputSource.java +++ b/src/mashine/inputs/InputSource.java @@ -7,19 +7,16 @@ package mashine.inputs; -import mashine.*; -import java.util.HashMap; +import java.util.HashMap; public class InputSource { - protected MaShine M; protected HashMap states; - protected HashMap ranges; + protected HashMap ranges; - public InputSource (MaShine m) { - M = m; + public InputSource () { states = new HashMap(); - ranges = new HashMap(); + ranges = new HashMap(); } public void tick(){} @@ -29,7 +26,7 @@ public HashMap pollStates() { return states; } - public HashMap pollRanges() { + public HashMap pollRanges() { return ranges; } diff --git a/src/mashine/inputs/KeyboardInputs.java b/src/mashine/inputs/KeyboardInputs.java index e5fe658..c00d3ed 100644 --- a/src/mashine/inputs/KeyboardInputs.java +++ b/src/mashine/inputs/KeyboardInputs.java @@ -7,28 +7,28 @@ package mashine.inputs; -import mashine.*; -import processing.event.*; -import java.util.HashMap; +import mashine.MaShine; +import processing.event.KeyEvent; -public class KeyboardInputs extends InputSource{ +public class KeyboardInputs extends InputSource implements Learnable{ public String lastTyped; + private String lastState; - - public KeyboardInputs (MaShine m) { - super(m); + public KeyboardInputs () { + super(); } public void keyEvent(KeyEvent e){ if(e.getAction() == KeyEvent.PRESS){ states.put("keyboard."+e.getKeyCode()+".press" , true); states.put("keyboard."+e.getKeyCode()+".hold" , true); - lastTyped = "" + M.key; - M.println(M.key + " -> " + e.getKeyCode()); + lastTyped = "" + MaShine.m.key; + lastState = "keyboard."+e.getKeyCode()+".press"; }else{ states.put("keyboard."+e.getKeyCode()+".release" , true); states.put("keyboard."+e.getKeyCode()+".hold" , false); + lastState = "keyboard."+e.getKeyCode()+".release"; } } @@ -39,6 +39,10 @@ public void clear() { } lastTyped = ""; + lastState = null; } + public String getLastState(){return lastState;} + public String getLastRange(){return null;} + } \ No newline at end of file diff --git a/src/mashine/inputs/Learnable.java b/src/mashine/inputs/Learnable.java new file mode 100644 index 0000000..0156071 --- /dev/null +++ b/src/mashine/inputs/Learnable.java @@ -0,0 +1,13 @@ +/** + * Interface for learnable events + * + * @author procsynth - Antoine Pintout + * @since 01-04-2016` + */ + +package mashine.inputs; + +public interface Learnable{ + public String getLastState(); + public String getLastRange(); +} \ No newline at end of file diff --git a/src/mashine/inputs/MidiInputs.java b/src/mashine/inputs/MidiInputs.java index 84e2743..6749c1f 100644 --- a/src/mashine/inputs/MidiInputs.java +++ b/src/mashine/inputs/MidiInputs.java @@ -7,28 +7,37 @@ package mashine.inputs; -import mashine.*; +import java.util.HashMap; +import java.util.Map; + +import javax.sound.midi.MidiMessage; + import mashine.inputs.midi_devices.*; -import java.util.HashMap; -import themidibus.*; -import javax.sound.midi.MidiMessage; +import mashine.MaShine; +import mashine.Do; +import themidibus.MidiBus; -public class MidiInputs extends InputSource{ +public class MidiInputs extends InputSource implements Learnable{ private HashMap buses; + private HashMap deviceByBuses; private MidiDevice[] devicesTypes = {new KorgNanoKontrol2(), new BehringerDC1(), new GenericMidiDevice()}; - private MidiBus testBus; + + private String lastState; + private String lastRange; + private boolean init = false; - public MidiInputs (MaShine m) { - super(m); + public MidiInputs () { + super(); buses = new HashMap(); + deviceByBuses = new HashMap(); rescanDevices(); } public void midiMessage(MidiMessage message, long timestamp, String busName){ int command = (int)(message.getMessage()[0] & 0xF0); - int channel = (int)(message.getMessage()[0] & 0x0F); + //int channel = (int)(message.getMessage()[0] & 0x0F); int keyNumber = (int)(message.getMessage()[1] & 0xFF); int value = (int)(message.getMessage()[2] & 0xFF); @@ -36,26 +45,53 @@ public void midiMessage(MidiMessage message, long timestamp, String busName){ if(busName.contains(devicesTypes[i].getDeviceName())){ String name = "midi."+ busName +"."+ devicesTypes[i].getInputName(command, keyNumber, value); Boolean state = devicesTypes[i].getState(command, keyNumber, value); - Float range = devicesTypes[i].getRange(command, keyNumber, value); + Double range = devicesTypes[i].getRange(command, keyNumber, value); - if(state != null) + if(state != null){ states.put(name + (state ? ".on" : ".off"), true); - if(range != null) - ranges.put(name, range); + lastState = name + (state ? ".on" : ".off"); + } + if(range != null){ + ranges.put(name, range/127); + lastRange = name; + } break; } } } public void tick(){ - // rescan midi devices here - //M.inputs.setState("internal.midi.status", buses.size() != 0); + if(!init){ + init = true; + MaShine.inputs.registerAction("mashine.inputs.reload_midi", new Do(){public void x(){rescanDevices(); registerOutputs();}}); + registerOutputs(); + } + + for(MidiBus bus : deviceByBuses.keySet()){ + MidiDevice device = deviceByBuses.get(bus); + Map outputs = device.getOutputs(); + for(int keyNumber: outputs.keySet()){ + String output = outputs.get(keyNumber); + bus.sendControllerChange(0, keyNumber, MaShine.inputs.getState("midi."+ bus.getBusName() +"."+ output) ? 127 : 0); + } + } } public void clear(){ for(String s : states.keySet()){ states.put(s, false); } + lastRange = null; + lastState = null; + } + + private void registerOutputs(){ + for(MidiBus bus : deviceByBuses.keySet()){ + MidiDevice device = deviceByBuses.get(bus); + for(String output : device.getOutputs().values()){ + MaShine.inputs.registerState("midi."+ bus.getBusName() +"."+ output); + } + } } private void rescanDevices(){ @@ -71,12 +107,26 @@ private void rescanDevices(){ for(int o = 0; o < outputNames.length; o++){ if(outputNames[o].indexOf(hwAddress) != -1){ name = name.substring(0, name.indexOf(" ")); - M.println("inputs.midi.registeredDevice: "+ name); - buses.put(name, new MidiBus(this, inputNames[i], outputNames[o], name)); + MidiBus bus = new MidiBus(this, inputNames[i], outputNames[o], name); + buses.put(name, bus); + for(int t = 0; t < devicesTypes.length; t++){ + if(name.contains(devicesTypes[t].getDeviceName())){ + deviceByBuses.put(bus, devicesTypes[i]); + MaShine.println(name +" ol"); + } + } } } } } } + public String getLastRange(){ + + return lastRange; + } + public String getLastState(){ + return lastState; + } + } \ No newline at end of file diff --git a/src/mashine/inputs/MinimAnalysis.java b/src/mashine/inputs/MinimAnalysis.java index f21bb3d..1e74ff2 100644 --- a/src/mashine/inputs/MinimAnalysis.java +++ b/src/mashine/inputs/MinimAnalysis.java @@ -7,46 +7,48 @@ package mashine.inputs; -import mashine.*; -import ddf.minim.*; -import ddf.minim.analysis.*; import java.util.ArrayList; +import mashine.MaShine; +import ddf.minim.AudioInput; +import ddf.minim.Minim; +import ddf.minim.analysis.BeatDetect; +import ddf.minim.analysis.FFT; + public class MinimAnalysis extends InputSource{ - private final int MINIM_BD_SENSITIVITY = 300; + private final int MINIM_BD_SENSITIVITY = 200; private final int MEAN_BEAT_SAMPLE = 4; - private final float MINUTE = (float)60000.0; + private final double MINUTE = 60000.0; private Minim minim; private BeatDetect beatSE; private BeatDetect beatFE; private AudioInput in; private FFT fft; + private AutoCorrelationPitchDetector acpd; - private String beatInputName = "minim.onset"; private long lastBeat = 0; private int lastBeatDistance = 0; private int meanBeatDistance = 0; private long lastInterpolatedBeat = 0; private long lastInterpolatedMeanBeat = 0; - private ArrayList beatDistances; + private boolean init = false; - public MinimAnalysis (MaShine m) { + public MinimAnalysis () { - super(m); + super(); - minim = new Minim(M); + minim = new Minim(MaShine.m); initMinim(); - - beatDistances = new ArrayList(); - beatDistances.add(500); // hydrate with a single 120BPM time } private void initMinim(){ + // FFT in = minim.getLineIn(); fft = new FFT(in.bufferSize(), in.sampleRate()); + fft.logAverages( 21, 3 ); beatSE = new BeatDetect(); beatFE = new BeatDetect(); @@ -55,15 +57,51 @@ private void initMinim(){ beatSE.detectMode(BeatDetect.SOUND_ENERGY); beatFE.detectMode(BeatDetect.FREQ_ENERGY); + // Auto Correlation Pitch Detect + acpd = new AutoCorrelationPitchDetector(in.sampleRate()); + } public void tick() { + if(!init){ + MaShine.inputs.registerState("minim.beat"); + MaShine.inputs.state("minim.beat", "minim.onset"); + MaShine.inputs.registerState("minim.do_interpolate"); + MaShine.inputs.state("minim.do_interpolate", "_true"); + init = true; + } + // audio analysis beatSE.detect(in.mix); beatFE.detect(in.mix); fft.forward(in.mix); - float rms = in.mix.level(); + acpd.forward(in.mix); + + float maxBandValue = 0; + int maxBand = 0; + + for(int i = 0; i < fft.avgSize(); i++){ + float bandValue = fft.getAvg(i); + ranges.put("minim.fft."+ + String.format("%2s", i).replace(' ', '0'), + (double) bandValue/64.0); + if(bandValue > maxBandValue){ + maxBand = i; + maxBandValue = bandValue; + } + } + double[] pitchs = acpd.getFrequencies(); + for(int i = 0; i < pitchs.length; i++){ + ranges.put("minim.acpd." + + String.format("%2s", i).replace(' ', '0'), + pitchs[i]); + } + + ranges.put("minim.pitch", acpd.getFrequency()); + ranges.put("minim.fft.maxband", (double) maxBand); + + double rms = in.mix.level(); states.put("minim.onset" , beatSE.isOnset()); states.put("minim.kick" , beatFE.isKick()); states.put("minim.snare", beatFE.isSnare()); @@ -73,15 +111,17 @@ public void tick() { // interpolated beat detect boolean isBeat = false; - long now = M.millis(); + long now = MaShine.m.millis(); // get user selected beat input (key, pad, ext source, minim beatdetect, ...) + String beatInputName = MaShine.inputs.getStateLinks().get("minim.beat"); + if(states.containsKey(beatInputName)){ // instant isBeat = states.get(beatInputName); }else{ // 16.6ms in one frame, 16.6ms of delay... - isBeat = M.inputs.getState(beatInputName); + isBeat = MaShine.inputs.getState(beatInputName); } states.put("minim.beat", isBeat); @@ -89,45 +129,19 @@ public void tick() { // last measured beat update if(isBeat){ lastBeatDistance = (int) (now - lastBeat); - beatDistances.add(lastBeatDistance); lastBeat = now; lastInterpolatedBeat = now; lastInterpolatedMeanBeat = now; - ranges.put("minim.bpm.last", ((float)MINUTE/lastBeatDistance)); - - if(beatDistances.size() > MEAN_BEAT_SAMPLE) - beatDistances.remove(0); - - ranges.put("minim.bpm.samplesize", (float)beatDistances.size()); - - meanBeatDistance = 0; - - for(long bd : beatDistances){ - meanBeatDistance += bd; - } - - meanBeatDistance /= beatDistances.size(); - - - ranges.put("minim.bpm.mean", ((float)MINUTE/meanBeatDistance)); + ranges.put("minim.bpm.last", ((double)MINUTE/lastBeatDistance)); } // interpolated beat - if(now == lastInterpolatedBeat || now > lastInterpolatedBeat + lastBeatDistance){ + if(now == lastInterpolatedBeat || MaShine.inputs.getState("minim.do_interpolate") && now > lastInterpolatedBeat + lastBeatDistance){ lastInterpolatedBeat = now; states.put("minim.beat.interpolated", true); }else{ states.put("minim.beat.interpolated", false); } - - // interpolated mean beat - if(now == lastInterpolatedMeanBeat || now > lastInterpolatedMeanBeat + meanBeatDistance){ - lastInterpolatedMeanBeat = now; - states.put("minim.beat.mean", true); - }else{ - states.put("minim.beat.mean", false); - } - } public FFT getFFT(){ diff --git a/src/mashine/inputs/MouseInputs.java b/src/mashine/inputs/MouseInputs.java index c804bc1..03ccebf 100644 --- a/src/mashine/inputs/MouseInputs.java +++ b/src/mashine/inputs/MouseInputs.java @@ -7,45 +7,62 @@ package mashine.inputs; -import mashine.*; -import processing.event.*; +import mashine.MaShine; +import processing.event.MouseEvent; public class MouseInputs extends InputSource{ - public MouseInputs (MaShine m) { - super(m); + private int lastClickX = 0; + private int lastClickY = 0; + + public MouseInputs () { + super(); } public void mouseEvent(MouseEvent e){ String button = "?"; - if(e.getButton() == M.LEFT){ + if(e.getButton() == MaShine.LEFT){ button = "left"; - }else if(e.getButton() == M.RIGHT){ + }else if(e.getButton() == MaShine.RIGHT){ button = "right"; - }else if(e.getButton() == M.CENTER){ + }else if(e.getButton() == MaShine.CENTER){ button = "center"; } - if(e.getAction() == e.RELEASE){ + if(e.getAction() == MouseEvent.RELEASE){ states.put("mouse."+ button + ".release", true); states.put("mouse."+ button + ".hold", false); - }else if(e.getAction() == e.PRESS){ + states.put("mouse."+ button + ".drag", false); + }else if(e.getAction() == MouseEvent.PRESS){ states.put("mouse."+ button + ".press", true); states.put("mouse."+ button + ".hold", true); - }else if(e.getAction() == e.WHEEL){ + lastClickY = e.getY(); + lastClickX = e.getX(); + }else if(e.getAction() == MouseEvent.WHEEL){ if(e.getCount() > 0){ states.put("mouse.wheel.encoder.on", true); }else{ states.put("mouse.wheel.encoder.off", true); } } + + } public void clear() { for(String s : states.keySet()){ - if(!s.contains("hold")) + if(!s.contains("hold") && !s.contains("drag")) states.put(s, false); } } + public void tick(){ + if(MaShine.inputs.getState("mouse.left.hold") && (lastClickX != MaShine.m.mouseX || lastClickY != MaShine.m.mouseY)){ + states.put("mouse.left.drag", true); + // lastClickY = e.getY(); + // lastClickX = e.getY(); + }else if (!MaShine.inputs.getState("mouse.left.hold")) { + states.put("mouse.left.drag", false); + } + } } \ No newline at end of file diff --git a/src/mashine/inputs/OlaInput.java b/src/mashine/inputs/OlaInput.java new file mode 100644 index 0000000..56944c2 --- /dev/null +++ b/src/mashine/inputs/OlaInput.java @@ -0,0 +1,56 @@ +/** + * Handle keyboard inputs + * + * @author procsynth - Antoine Pintout + * @since 13-02-2016` + */ + +package mashine.inputs; + +import mashine.MaShine; +import java.lang.Thread; +import java.lang.Runnable; + +public class OlaInput extends InputSource implements Runnable{ + + private Thread T; + private boolean started = false; + + public OlaInput () { + super(); + + T = new Thread(this); + } + + public void tick() { + if(!started){ + T.start(); + started = true; + }else{ + synchronized (this) { + this.notify(); + } + } + } + + public void run() { + while(true){ + short[] dmxData; + synchronized (MaShine.outputs.ola) { + dmxData = MaShine.outputs.ola.getDmx(); + } + if(dmxData != null){ + synchronized (ranges) { + for(int i = 0; i < Math.min(8, dmxData.length); i++){ + ranges.put("ola.in."+(i+1), (double) dmxData[i]/255.0); + } + } + } + try { + synchronized (this) { + this.wait(); + } + } catch (InterruptedException e) {e.printStackTrace();} + } + } +} \ No newline at end of file diff --git a/src/mashine/inputs/UDPInput.java b/src/mashine/inputs/UDPInput.java new file mode 100644 index 0000000..dbea912 --- /dev/null +++ b/src/mashine/inputs/UDPInput.java @@ -0,0 +1,96 @@ +/** + * Inputs from Internet + * + * @author procsynth - Antoine Pintout + * @since 13-02-2016` + */ + +package mashine.inputs; + +import java.io.*; +import java.net.*; +import java.util.*; + +import mashine.MaShine; + +public class UDPInput extends InputSource { + + public UDPInput() { + super(); + try{ + new ReceiveThread().start(); + MaShine.println("LISTENING ON UDP 227.16.20.1:1703"); + }catch (IOException e) { + e.printStackTrace(); + } + } + + + public class ReceiveThread extends Thread { + + protected InetAddress group; + protected MulticastSocket socket = null; + + public ReceiveThread() throws IOException { + this("UDPInput"); + } + + public ReceiveThread(String name) throws IOException { + super(name); + socket = new MulticastSocket(1703); + group = InetAddress.getByName("227.16.20.1"); + socket.joinGroup(group); + } + + public void run() { + DatagramPacket packet; + Boolean running = true; + while (running) { + try{ + byte[] buf = new byte[4096]; + packet = new DatagramPacket(buf, buf.length); + socket.receive(packet); + String received = new String(packet.getData()); + Map newStates = parseStates(received); + Map newRanges = parseRanges(received); + synchronized(states){states.putAll(newStates);} + synchronized(ranges){ranges.putAll(newRanges);} + }catch(IOException e){ + running = false; + e.printStackTrace(); + + try{ + socket.leaveGroup(group); + }catch(Exception ignore){} + } + } + socket.close(); + } + } + + public Map parseStates(String data){ + String[] d = data.split("\n"); + Map m = new HashMap(); + for(String s : d){ + String[] l = s.split(":"); + if(l.length == 3 && l[0].equals("s")){ + m.put("udp."+ l[1], l[2].equals("1")); + MaShine.println("udp."+ l[1]); + } + } + return m; + } + public Map parseRanges(String data){ + String[] d = data.split("\n"); + Map m = new HashMap(); + for(String s : d){ + try{ + String[] l = s.split(":"); + if(l.length == 3 && l[0].equals("r")){ + m.put("udp."+ l[1], new Double(l[2])); + } + }catch (Exception ignore) {} + } + return m; + } +} \ No newline at end of file diff --git a/src/mashine/inputs/midi_devices/BehringerDC1.java b/src/mashine/inputs/midi_devices/BehringerDC1.java index 6fc42a7..494affc 100644 --- a/src/mashine/inputs/midi_devices/BehringerDC1.java +++ b/src/mashine/inputs/midi_devices/BehringerDC1.java @@ -7,9 +7,9 @@ package mashine.inputs.midi_devices; -import java.util.Map; -import java.util.HashMap; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; public class BehringerDC1 extends MidiDevice{ @@ -18,7 +18,7 @@ public BehringerDC1(){ deviceName = "DC1"; - Map pad = new HashMap(); + Map pad = new HashMap(); pad.put(32, "bigbutton.pad"); pad.put(0, "top.1"); @@ -57,7 +57,7 @@ public BehringerDC1(){ pad.put(51, "pad.16"); PAD = Collections.unmodifiableMap(pad); - Map encoder = new HashMap(); + Map encoder = new HashMap(); encoder.put(32, "bigbutton.encoder"); encoder.put(16+0, "encoder.1"); diff --git a/src/mashine/inputs/midi_devices/GenericMidiDevice.java b/src/mashine/inputs/midi_devices/GenericMidiDevice.java index b7d4942..ad3d657 100644 --- a/src/mashine/inputs/midi_devices/GenericMidiDevice.java +++ b/src/mashine/inputs/midi_devices/GenericMidiDevice.java @@ -7,7 +7,6 @@ package mashine.inputs.midi_devices; -import mashine.*; public class GenericMidiDevice extends MidiDevice{ diff --git a/src/mashine/inputs/midi_devices/KorgNanoKontrol2.java b/src/mashine/inputs/midi_devices/KorgNanoKontrol2.java index 39eb476..515d15c 100644 --- a/src/mashine/inputs/midi_devices/KorgNanoKontrol2.java +++ b/src/mashine/inputs/midi_devices/KorgNanoKontrol2.java @@ -7,9 +7,9 @@ package mashine.inputs.midi_devices; -import java.util.Map; -import java.util.HashMap; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; public class KorgNanoKontrol2 extends MidiDevice{ @@ -18,7 +18,7 @@ public KorgNanoKontrol2(){ deviceName = "nanoKONTROL2"; - Map pad = new HashMap(); + Map pad = new HashMap(); pad.put(58, "track.previous"); pad.put(59, "track.next"); pad.put(46, "cycle"); @@ -56,7 +56,9 @@ public KorgNanoKontrol2(){ pad.put(64+7, "track8.rec"); PAD = Collections.unmodifiableMap(pad); - Map range = new HashMap(); + OUTPUTS = PAD; + + Map range = new HashMap(); range.put(16+0, "track1.knob"); range.put(0 +0, "track1.slider"); range.put(16+1, "track2.knob"); diff --git a/src/mashine/inputs/midi_devices/MidiDevice.java b/src/mashine/inputs/midi_devices/MidiDevice.java index 30fb14e..614939e 100644 --- a/src/mashine/inputs/midi_devices/MidiDevice.java +++ b/src/mashine/inputs/midi_devices/MidiDevice.java @@ -7,28 +7,33 @@ package mashine.inputs.midi_devices; -import java.util.Map; import java.util.HashMap; -import java.util.Collections; +import java.util.Map; public class MidiDevice{ protected Map PAD; protected Map RANGE; protected Map ENCODER; + protected Map OUTPUTS; protected String deviceName; public MidiDevice(){ deviceName = ""; - PAD = new HashMap(); - RANGE = new HashMap(); - ENCODER = new HashMap(); + PAD = new HashMap(); + RANGE = new HashMap(); + ENCODER = new HashMap(); + OUTPUTS = new HashMap(); } public String getDeviceName(){ return deviceName; } + public Map getOutputs(){ + return OUTPUTS; + } + public String getInputName(int command, int keyNumber,int value){ if(PAD.containsKey(keyNumber)){ return PAD.get(keyNumber); @@ -58,11 +63,11 @@ else if (command == 0xB0) } // returns an int between 0 and 127, null if unchanged/unapplicable - public Float getRange(int command, int keyNumber, int value){ + public Double getRange(int command, int keyNumber, int value){ if(PAD.containsKey(keyNumber) || ENCODER.containsKey(keyNumber)) return null; else if(command == 0xB0) - return (float) value; + return (double) value; else return null; } diff --git a/src/mashine/outputs/Ola.java b/src/mashine/outputs/Ola.java index e418d8a..91d490b 100644 --- a/src/mashine/outputs/Ola.java +++ b/src/mashine/outputs/Ola.java @@ -7,25 +7,90 @@ package mashine.outputs; -import mashine.*; +import java.util.HashMap; +import java.util.List; + +import mashine.MaShine; +import mashine.Do; +import mashine.scene.Device; +import mashine.scene.Frame; +import mashine.scene.features.EditableFeature; +import mashine.scene.features.Feature; import ola.OlaClient; +import ola.proto.Ola.UniverseInfo; +import ola.proto.Ola.DmxData; public class Ola extends Output{ private OlaClient ola; + private Integer inputUniverse; + private boolean init = false; - public Ola(MaShine m){ - - super(m); - connectToServer(); - + public Ola(){ + //connectToServer(); } - public void push(){ + public void push(Frame frame){ if(ola != null){ + HashMap dmxData = new HashMap(); + for(int u : ports.keySet()) + dmxData.put(u, new short[512]); + + List devices = MaShine.scene.getDevices(); + + // build arrays + + for(Device d : devices){ + int u = d.getUniverse(); + + if(dmxData.containsKey(u)){ + int i = d.getStartAddress(); + + for(Feature f : d.getFeatures()){ + // look in the frame + if(f instanceof EditableFeature){ + Feature ff = frame.getFeature(d, f); + if(null == ff){ + for(int j = 0; j < f.getFootprint(); j++){ + dmxData.get(u)[i-1] = 0; + i++; + } + }else{ + for(short v : ff.toArray()){ + dmxData.get(u)[i-1] = v; + i++; + } + } + // look in the device + }else{ + for(short v : f.toArray()){ + dmxData.get(u)[i-1] = v; + i++; + } + } + } + } + } + + // send array to OLA + try{ + for(Integer u : dmxData.keySet()){ + ola.streamDmx(u, dmxData.get(u)); + } + }catch(Exception e){ + ola = null; + MaShine.inputs.setState("internal.ola.status", false); + MaShine.ui.status.set("OLA", "disconnected"); + } + + //testReceive(3); }else{ - if(M.frameCount % 120 == 0) + if(!init){ + init = true; + MaShine.inputs.registerAction("mashine.outputs.reload_universes", new Do(){public void x(){connectToServer();}}); + } + if(MaShine.m.frameCount % 120 == 0) connectToServer(); } } @@ -33,10 +98,36 @@ public void push(){ private void connectToServer(){ try{ ola = new OlaClient(); - M.inputs.setState("internal.ola.status", true); + MaShine.inputs.setState("internal.ola.status", true); + loadUniverses(); }catch (Exception e) { ola = null; - M.inputs.setState("internal.ola.status", false); + MaShine.inputs.setState("internal.ola.status", false); + MaShine.ui.status.set("OLA", "disconnected"); + } + } + + private void loadUniverses(){ + ports = new HashMap(); + for(int i = 0; i < 64; i ++){ + try{ + List r = ola.getUniverseInfo(i).getUniverseList(); + for(UniverseInfo u : r){ + if(u.getOutputPortCount()>0) + ports.put(u.getUniverse(), u.getName()); + if(u.getInputPortCount()>0) + inputUniverse = u.getUniverse(); + } + }catch (Exception e) {} + } + MaShine.ui.status.set("OLA", ports.size()+ " universes"); + } + + public short[] getDmx(){ + if(inputUniverse == null){ + return null; } + DmxData r = ola.getDmx(inputUniverse); + return ola.convertFromUnsigned(r.getData()); } } \ No newline at end of file diff --git a/src/mashine/outputs/Output.java b/src/mashine/outputs/Output.java index c243090..ad731a3 100644 --- a/src/mashine/outputs/Output.java +++ b/src/mashine/outputs/Output.java @@ -7,18 +7,20 @@ package mashine.outputs; -import mashine.*; +import java.util.HashMap; -public class Output{ - - protected MaShine M; +import mashine.scene.Frame; - public Output(MaShine m){ +public class Output{ - M = m; + protected HashMap ports; + public Output(){ } - public void push(){ + public void push(Frame frame){} + + public HashMap getPorts(){ + return ports; } } \ No newline at end of file diff --git a/src/mashine/outputs/UDP.java b/src/mashine/outputs/UDP.java new file mode 100644 index 0000000..90e6903 --- /dev/null +++ b/src/mashine/outputs/UDP.java @@ -0,0 +1,43 @@ +/** + * Output to Internet + * + * @author procsynth - Antoine Pintout + * @since 13-02-2016` + */ + +package mashine.outputs; + +import java.io.*; +import java.net.*; +import java.util.*; + +import mashine.MaShine; +import mashine.scene.Frame; + +public class UDP extends Output { + + private InetAddress address; + private DatagramSocket socket; + + public UDP() { + try{ + socket = new DatagramSocket() ; + address = InetAddress.getByName("224.0.0.1"); + }catch (Exception e) {e.printStackTrace();} + } + + public void push(Frame frame){ + if(socket != null){ + try{ + byte[] buf = new byte[4096]; + String dString = ""; + for(String s : MaShine.inputs.getStateSet()){ + dString += "S:"+ s +":"+ (MaShine.inputs.getState(s) ? "1" : "0") +"\n"; + } + buf = dString.getBytes(); + DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 1703); + socket.send(packet); + }catch (Exception e) {e.printStackTrace();} + } + } +} diff --git a/src/mashine/scene/Device.java b/src/mashine/scene/Device.java index fb1d50c..c4bbd7f 100644 --- a/src/mashine/scene/Device.java +++ b/src/mashine/scene/Device.java @@ -7,24 +7,30 @@ package mashine.scene; -import mashine.*; -import mashine.scene.features.*; +import java.io.Serializable; import java.util.ArrayList; +import java.util.Iterator; +import java.util.UUID; -public class Device{ +import mashine.scene.features.Feature; + +public class Device implements Serializable{ + + private static final long serialVersionUID = 0xDE410001L; private int startAddress; private int universe; - private int footprint; + private int footprint = 0; private int x; private int y; private int w; private int h; - private String identifier; + private String id; + private String name; private ArrayList features; - public Device(String identifier, int startAddress, int universe, int x, int y, int width, int height){ - this.identifier = identifier; + public Device(String name, int startAddress, int universe, int x, int y, int width, int height){ + this.name = name; this.startAddress = startAddress; this.universe = universe; footprint = 0; @@ -32,12 +38,68 @@ public Device(String identifier, int startAddress, int universe, int x, int y, i this.y = y; this.w = width; this.h = height; + this.id = UUID.randomUUID().toString(); features = new ArrayList(); } - public void addFeature(Feature f){ - features.add(f); - footprint += f.getFootprint(); + public Device(Device d, String name){ + this.name = name; + this.startAddress = d.getStartAddress(); + this.universe = d.getUniverse(); + features = d.getFeatures(); + for(Feature f : features){ + footprint += f.getFootprint(); + } + this.x = d.getX(); + this.y = d.getY() + 5 + d.getHeight();; + this.w = d.getWidth(); + this.h = d.getHeight(); + + this.id = UUID.randomUUID().toString(); + } + + public void addFeature(Feature feature){ + for(Feature f : features){ + if(f.getType() == feature.getType()) + return; + } + features.add(feature); + footprint += feature.getFootprint(); + } + + public void removeFeature(String featureType){ + Iterator fi = features.iterator(); + while(fi.hasNext()){ + Feature f = fi.next(); + if(f.getType().equals(featureType)){ + features.remove(f); + break; + } + } + } + public void updateFeature(String featureField, int value){ + String[] ff = featureField.split("\\."); + Iterator fi = features.iterator(); + while(fi.hasNext()){ + Feature f = fi.next(); + if(f.getType().equals(ff[0])){ + f.setField(ff[1], value); + break; + } + } + } + + public Integer getFeatureField(String featureField){ + String[] ff = featureField.split("\\."); + Iterator fi = features.iterator(); + while(fi.hasNext()){ + Feature f = fi.next(); + if(f.getType().equals(ff[0])){ + return f.getField(ff[1]); + } + } + + return null; } public ArrayList getFeatures(){ @@ -49,9 +111,51 @@ public ArrayList getFeatures(){ public int getWidth(){return w;} public int getHeight(){return h;} - public String getIdentifier(){return identifier;} + public String getIdentifier(){return id;} + public String getName(){return name;} public int getStartAddress(){return startAddress;} + public int getFootprint() {return footprint;} public int getUniverse(){return universe;} + public void setName(String name){this.name = name;} + public void setStartAddress(int address){this.startAddress = address;} + public void setUniverse(int universe){this.universe = universe;} + + public void moveUp(){y --; y = Math.max(0, y);} + public void moveDown(){y ++;} + public void moveLeft(){x --; x = Math.max(0, x);} + public void moveRight(){x ++;} + + public void setX(int v){x = v;} + public void setY(int v){y = v;} + public void setWidth(int v){w = v;} + public void setHeight(int v){h = v;} + + public static ArrayList commonFeatures(ArrayList devices){ + + ArrayList commonFeatures = new ArrayList(); + if(!devices.isEmpty()){ + commonFeatures = devices.get(0).getFeatures(); + for(Device d : devices){ + ArrayList deviceFeatures = (d).getFeatures(); + + Iterator cfi = commonFeatures.iterator(); + while(cfi.hasNext()){ + Feature cf = cfi.next(); + boolean found = false; + for(Feature df : deviceFeatures){ + if(df.getType() == cf.getType()) + found = true; + } + + if(!found){ + cfi.remove(); + } + } + } + } + + return commonFeatures; + } } \ No newline at end of file diff --git a/src/mashine/scene/DeviceGroup.java b/src/mashine/scene/DeviceGroup.java index db7846a..ef55517 100644 --- a/src/mashine/scene/DeviceGroup.java +++ b/src/mashine/scene/DeviceGroup.java @@ -7,17 +7,104 @@ package mashine.scene; -import mashine.*; -import mashine.scene.features.*; +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; import java.util.HashMap; +import java.util.List; +import java.lang.Comparable; +import java.util.Comparator; +import java.util.stream.Stream; -public class DeviceGroup{ +public class DeviceGroup implements Serializable{ - private HashMap devices; - private String identifier; + private static final long serialVersionUID = 0xDED00001L; - public DeviceGroup(String identifier){ - this.identifier = identifier; - devices = new HashMap(); + private LinkedHashMap devices; + private String name; + + public DeviceGroup(String name){ + this.name = name; + devices = new LinkedHashMap(); + } + + public DeviceGroup(String name, DeviceGroup grp){ + this.name = name; + this.devices = new LinkedHashMap(grp.getDevices()); + } + + public DeviceGroup(String name, List devices){ + this(name); + + for(Device d : devices){ + this.devices.put(d, d.getStartAddress()); + } + } + + public LinkedHashMap getDevices() { + return devices; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void putDevice(Device d){ + this.devices.put(d, d.getStartAddress()); + } + public void removeDevice(Device d){ + this.devices.remove(d); + } + + public boolean isIn(Device d){ + return devices.containsKey(d); + } + public boolean isEmpty(){ + return devices.isEmpty(); + } + + public Device getFirst(){ + if(isEmpty()){ + return null; + } + return (Device) devices.keySet().toArray()[0]; + } + + public Integer getWeight(Device d){ + return devices.get(d); + } + + public Integer setWeight(Device d, Integer w){ + return devices.put(d, w); } + + public void reverseWeights(){ + int min = -1; + int max = 0; + for(int v : devices.values()){ + if(min == -1 || v < min) min = v; + if(v > max) max = v; + } + int range = Math.max(1, (max - min) + 1 ); + for(Device d : devices.keySet()){ + devices.put(d, min + range - devices.get(d)); + } + } + + public static > Map sortByValue( Map map ){ + + Map result = new LinkedHashMap<>(); + Stream > st = map.entrySet().stream(); + + st.sorted(Comparator.comparing(e -> e.getValue())) + .forEachOrdered(e ->result.put(e.getKey(),e.getValue())); + + return result; + } + } \ No newline at end of file diff --git a/src/mashine/scene/Frame.java b/src/mashine/scene/Frame.java index 38feb43..dd3a869 100644 --- a/src/mashine/scene/Frame.java +++ b/src/mashine/scene/Frame.java @@ -7,25 +7,28 @@ package mashine.scene; -import mashine.*; -import mashine.scene.*; -import mashine.scene.features.*; -import java.util.HashMap; +import java.io.Serializable; import java.util.ArrayList; +import java.util.HashMap; + +import mashine.scene.features.EditableFeature; +import mashine.scene.features.Feature; -public class Frame{ +public class Frame implements Serializable{ + + private static final long serialVersionUID = 0xF44E0001L; private HashMap features; - public Frame(HashMap devices){ + public Frame(ArrayList devices){ features = new HashMap(); - for(String deviceName : devices.keySet()){ - ArrayList devFeatures = devices.get(deviceName).getFeatures(); + for(Device d : devices){ + ArrayList devFeatures = d.getFeatures(); for(Feature f : devFeatures){ if(f instanceof EditableFeature){ - features.put(deviceName +"."+ f.getType(), (EditableFeature) Feature.cloneFeature(f)); + features.put(d.getIdentifier() +"."+ f.getType(), (EditableFeature) Feature.cloneFeature(f)); } } } @@ -50,4 +53,51 @@ public Frame(){ public HashMap getFeatures(){ return features; } + + public EditableFeature getFeature(Device d, Feature f){ + if(features.containsKey(d.getIdentifier()+"."+f.getType())){ + return features.get(d.getIdentifier()+"."+f.getType()); + }else{ + return null; + } + } + public EditableFeature getFeature(String featureString){ + if(features.containsKey(featureString)){ + return features.get(featureString); + }else{ + return null; + } + } + + public void addFeature(Device d, EditableFeature f){ + features.put(d.getIdentifier()+"."+f.getType(), (EditableFeature) Feature.cloneFeature(f)); + } + + public void addFeature(String featureString, EditableFeature f){ + features.put(featureString, (EditableFeature) Feature.cloneFeature(f)); + } + public void removeFeature(Device d, EditableFeature f){ + features.remove(d.getIdentifier()+"."+f.getType()); + } + + public boolean isIn(Device d, Feature f){ + return features.containsKey(d.getIdentifier()+"."+f.getType()); + } + + public void updateFeature(Device d, String featureType, String fieldName, int value){ + if(features.containsKey(d.getIdentifier()+"."+featureType)) + features.get(d.getIdentifier()+"."+featureType).setField(fieldName, value); + } + + public static Frame mix(Frame top, Frame base){ + Frame mix = new Frame(base); + + HashMap topFeatures = top.getFeatures(); + + for(String fs : topFeatures.keySet()){ + mix.addFeature(fs, topFeatures.get(fs)); + } + + return mix; + } } \ No newline at end of file diff --git a/src/mashine/scene/Sequence.java b/src/mashine/scene/Sequence.java index 23bd21e..a5c4c41 100644 --- a/src/mashine/scene/Sequence.java +++ b/src/mashine/scene/Sequence.java @@ -7,22 +7,34 @@ package mashine.scene; -import mashine.*; +import java.io.Serializable; import java.util.ArrayList; +import java.util.UUID; -public class Sequence{ +public class Sequence implements Serializable{ + + private static final long serialVersionUID = 0x5E0E0001L; private ArrayList frames; + private String name; + private String id; - public Sequence(){ + public Sequence(String name){ frames = new ArrayList(); + frames.add(new Frame()); + this.name = name; + this.id = UUID.randomUUID().toString(); } - public Sequence(Frame f){ + public Sequence(String name, Frame f){ frames = new ArrayList(); frames.add(f); + this.name = name; + this.id = UUID.randomUUID().toString(); } public Frame getFrame(int index){ + if(index >= frames.size()) + index = frames.size() -1; return frames.get(index); } @@ -34,7 +46,27 @@ public void addFrame(Frame f){ frames.add(f); } + public void deleteFrame(int index){ + if(index < frames.size() && index >= 0){ + frames.remove(index); + } + if(frames.size() == 0){ + addFrame(new Frame()); + } + } + public void setFrame(int i, Frame f){ frames.set(i, f); } + + public String getName(){ + return name; + } + public void setName(String name){ + this.name = name; + } + + public String getIdentifier(){ + return id; + } } \ No newline at end of file diff --git a/src/mashine/scene/features/ColorFeature.java b/src/mashine/scene/features/ColorFeature.java index 45293a5..7475888 100644 --- a/src/mashine/scene/features/ColorFeature.java +++ b/src/mashine/scene/features/ColorFeature.java @@ -1,10 +1,15 @@ package mashine.scene.features; -import mashine.scene.*; -import mashine.ui.*; +import java.util.LinkedHashMap; + +import mashine.ui.FlatColor; public abstract class ColorFeature extends EditableFeature { + private static final long serialVersionUID = 0xC010F301L; + + protected FlatColor linkedColor = null; + public ColorFeature(String type, int footprint){ super(type, footprint); } @@ -17,5 +22,24 @@ public ColorFeature(Feature f){ super(f); } + public void link(FlatColor c){ + linkedColor = c; + } + + public void unlink(){ + linkedColor = null; + } + + public void setField(String fieldName, int value){ + if(fields.containsKey(fieldName)){ + unlink(); + fields.put(fieldName, value); + } + } + + public abstract FlatColor getLinkedColor(); public abstract FlatColor getColor(); + public abstract LinkedHashMap getFields(); + public abstract short[] toArray(); + } \ No newline at end of file diff --git a/src/mashine/scene/features/Coords.java b/src/mashine/scene/features/Coords.java new file mode 100644 index 0000000..94fecc0 --- /dev/null +++ b/src/mashine/scene/features/Coords.java @@ -0,0 +1,21 @@ +package mashine.scene.features; + +public final class Coords extends EditableFeature { + + private static final long serialVersionUID = 0xC004D0001L; + + public Coords(){ + super("coords", 4); + fields.put("x", 127); + fields.put("x_", 0); + fields.put("y", 127); + fields.put("y_", 0); + } + + public Coords(Coords f){ + super(f); + } + public Coords(Feature f){ + super(f); + } +} \ No newline at end of file diff --git a/src/mashine/scene/features/EditableFeature.java b/src/mashine/scene/features/EditableFeature.java index 1fdef17..4a24ef3 100644 --- a/src/mashine/scene/features/EditableFeature.java +++ b/src/mashine/scene/features/EditableFeature.java @@ -1,12 +1,10 @@ package mashine.scene.features; -import mashine.scene.*; -import mashine.*; -import java.util.LinkedHashMap; -import java.util.ArrayList; public class EditableFeature extends Feature{ + private static final long serialVersionUID = 0x3D3710001L; + public EditableFeature(String type, int footprint){ super(type, footprint); } @@ -18,9 +16,4 @@ public EditableFeature(EditableFeature f){ public EditableFeature(Feature f){ super(f); } - - public void setField(String fieldName, int value){ - if(fields.containsKey(fieldName)) - fields.put(fieldName, value); - } } \ No newline at end of file diff --git a/src/mashine/scene/features/Feature.java b/src/mashine/scene/features/Feature.java index 4afa0f0..d9f0858 100644 --- a/src/mashine/scene/features/Feature.java +++ b/src/mashine/scene/features/Feature.java @@ -7,12 +7,14 @@ package mashine.scene.features; -import mashine.scene.*; -import mashine.*; +import mashine.MaShine; + +import java.io.Serializable; import java.util.LinkedHashMap; -import java.util.ArrayList; -public abstract class Feature { +public abstract class Feature implements Serializable { + + private static final long serialVersionUID = 0xFEA70001L; protected int footprint; protected LinkedHashMap fields; @@ -38,12 +40,24 @@ public static Feature cloneFeature(Feature f){ Feature n = null; - if(f instanceof RGB){ + if(f instanceof Tradi){ + n = new Tradi(f); + }else if(f instanceof RGB){ n = new RGB(f); }else if(f instanceof RGBW){ n = new RGBW(f); + }else if(f instanceof Coords){ + n = new Coords(f); + }else if(f instanceof Zoom){ + n = new Zoom(f); }else if(f instanceof FixedField){ n = new FixedField(f); + }else if(f instanceof SingleField){ + n = new SingleField(f); + }else if(f instanceof EditableFeature){ + n = new EditableFeature(f); + }else{ + MaShine.println("NULL FEATURE WILL BE RETURNED, exceptions will rain.");// TODO: throw exception if unknow feature } return n; @@ -59,9 +73,18 @@ public LinkedHashMap getFields(){ return new LinkedHashMap(fields); } + public Integer getField(String fieldName){ + if(fields.containsKey(fieldName)){ + return fields.get(fieldName); + }else{ + return null; + } + } + public void setField(String fieldName, int value){ - if(fields.containsKey(fieldName)) - fields.put(fieldName, value); + if(fields.containsKey(fieldName)){ + fields.put(fieldName, Math.min(255, Math.max(0,value))); + } } public String toString(){ @@ -72,10 +95,12 @@ public String toString(){ return type +":"+ footprint + stringFields; } - public ArrayList to255(){ - ArrayList r = new ArrayList(); - for(String f : fields.keySet()){ - r.add(fields.get(f)); + public short[] toArray(){ + short[] r = new short[footprint]; + int i = 0; + for(int v : fields.values()){ + r[i] = (short) v; + i++; } return r; } diff --git a/src/mashine/scene/features/FixedField.java b/src/mashine/scene/features/FixedField.java index e098079..5131fd5 100644 --- a/src/mashine/scene/features/FixedField.java +++ b/src/mashine/scene/features/FixedField.java @@ -1,13 +1,20 @@ package mashine.scene.features; -import mashine.scene.*; public final class FixedField extends Feature { + + private static final long serialVersionUID = 0xF1F31001L; + public FixedField(String fieldName, int fixedValue){ - super("fixed", 1); + super(fieldName, 1); fields.put(fieldName, fixedValue); } + + public FixedField(String fieldName){ + this(fieldName, 0); + } + public FixedField(FixedField f){ super(f); } diff --git a/src/mashine/scene/features/RGB.java b/src/mashine/scene/features/RGB.java index 24567e1..19d3e56 100644 --- a/src/mashine/scene/features/RGB.java +++ b/src/mashine/scene/features/RGB.java @@ -1,9 +1,13 @@ package mashine.scene.features; -import mashine.scene.*; -import mashine.ui.*; +import java.util.LinkedHashMap; + +import mashine.ui.FlatColor; public final class RGB extends ColorFeature { + + private static final long serialVersionUID = 0xC0F10001L; + public RGB(int red, int green, int blue){ super("rgb", 3); fields.put("red", red); @@ -16,7 +20,7 @@ public RGB(int generalDim){ } public RGB(){ - this(255); + this(0); } public RGB(RGB f){ @@ -28,16 +32,57 @@ public RGB(Feature f){ } public void setRed(int v){ - fields.put("red", v); + setField("red", v); } public void setGreen(int v){ - fields.put("green", v); + setField("green", v); } public void setBlue(int v){ - fields.put("blue", v); + setField("blue", v); } public FlatColor getColor(){ - return new FlatColor(fields.get("red"), fields.get("green"), fields.get("blue")); + return getLinkedColor().withAlpha(255); + } + + public FlatColor getLinkedColor(){ + FlatColor rc; + if(linkedColor != null){ + rc = linkedColor; + }else{ + rc = new FlatColor(fields.get("red"), fields.get("green"), fields.get("blue")); + } + return rc; + } + + public LinkedHashMap getFields(){ + LinkedHashMap rf; + + if(linkedColor != null){ + rf = new LinkedHashMap(); + rf.put("red", linkedColor.getRed()); + rf.put("green", linkedColor.getGreen()); + rf.put("blue", linkedColor.getBlue()); + }else{ + rf = new LinkedHashMap(fields); + } + + return rf; + } + + public short[] toArray(){ + if(null == linkedColor){ + return new short[]{ + fields.get("red").shortValue(), + fields.get("green").shortValue(), + fields.get("blue").shortValue() + }; + } + + return new short[]{ + (short)linkedColor.getRed(), + (short)linkedColor.getGreen(), + (short)linkedColor.getBlue() + }; } } \ No newline at end of file diff --git a/src/mashine/scene/features/RGBW.java b/src/mashine/scene/features/RGBW.java index aebe30a..98a20da 100644 --- a/src/mashine/scene/features/RGBW.java +++ b/src/mashine/scene/features/RGBW.java @@ -1,9 +1,13 @@ package mashine.scene.features; -import mashine.scene.*; -import mashine.ui.*; +import java.util.LinkedHashMap; + +import mashine.ui.FlatColor; public final class RGBW extends ColorFeature { + + private static final long serialVersionUID = 0xC0F20001L; + public RGBW(int red, int green, int blue, int white){ super("rgbw", 4); fields.put("red", red); @@ -17,7 +21,7 @@ public RGBW(int generalDim){ } public RGBW(){ - this(255); + this(0); } public RGBW(RGB f){ @@ -29,19 +33,64 @@ public RGBW(Feature f){ } public void setRed(int v){ - fields.put("red", v); + setField("red", v); } public void setGreen(int v){ - fields.put("green", v); + setField("green", v); } public void setBlue(int v){ - fields.put("blue", v); + setField("blue", v); } public void setWhite(int v){ - fields.put("white", v); + setField("white", v); } public FlatColor getColor(){ - return new FlatColor(fields.get("red"), fields.get("green"), fields.get("blue")); + return getLinkedColor().withAlphaAsWhite(); + } + + public FlatColor getLinkedColor(){ + FlatColor rc; + if(linkedColor != null){ + rc = linkedColor; + }else{ + rc = new FlatColor(fields.get("red"), fields.get("green"), fields.get("blue"), fields.get("white")); + } + return rc; } + + public LinkedHashMap getFields(){ + LinkedHashMap rf; + + if(linkedColor != null){ + rf = new LinkedHashMap(); + rf.put("red", linkedColor.getRed()); + rf.put("green", linkedColor.getGreen()); + rf.put("blue", linkedColor.getBlue()); + rf.put("white", linkedColor.getAlpha()); + }else{ + rf = new LinkedHashMap(fields); + } + + return rf; + } + + public short[] toArray(){ + if(null == linkedColor){ + return new short[]{ + fields.get("red").shortValue(), + fields.get("green").shortValue(), + fields.get("blue").shortValue(), + fields.get("white").shortValue() + }; + } + + return new short[]{ + (short)linkedColor.getRed(), + (short)linkedColor.getGreen(), + (short)linkedColor.getBlue(), + (short)linkedColor.getAlpha() + }; + } + } \ No newline at end of file diff --git a/src/mashine/scene/features/SingleField.java b/src/mashine/scene/features/SingleField.java new file mode 100644 index 0000000..ffcd9ee --- /dev/null +++ b/src/mashine/scene/features/SingleField.java @@ -0,0 +1,28 @@ +package mashine.scene.features; + + +public final class SingleField extends EditableFeature { + + private static final long serialVersionUID = 0x11F310001L; + + public SingleField(String fieldName, int singleValue){ + super(fieldName, 1); + fields.put(fieldName, singleValue); + } + + public SingleField(String fieldName){ + this(fieldName, 0); + } + + public SingleField(SingleField f){ + super(f); + } + + public SingleField(Feature f){ + super(f); + } + + public void set(int value){ + setField(type, value); + } +} \ No newline at end of file diff --git a/src/mashine/scene/features/Zoom.java b/src/mashine/scene/features/Zoom.java new file mode 100644 index 0000000..744085f --- /dev/null +++ b/src/mashine/scene/features/Zoom.java @@ -0,0 +1,19 @@ +package mashine.scene.features; + +public final class Zoom extends EditableFeature { + + private static final long serialVersionUID = 0x2003D0001L; + + public Zoom(){ + super("zoom", 2); + fields.put("z", 127); + fields.put("z_", 0); + } + + public Zoom(Coords f){ + super(f); + } + public Zoom(Feature f){ + super(f); + } +} \ No newline at end of file diff --git a/src/mashine/ui/Colors.java b/src/mashine/ui/Colors.java index 3a14b1c..c6c050b 100644 --- a/src/mashine/ui/Colors.java +++ b/src/mashine/ui/Colors.java @@ -13,6 +13,7 @@ private Colors () {} public static final FlatColor WHITE = new FlatColor(0xFF); public static final FlatColor BLACK = new FlatColor(0x00); + public static final FlatColor RED = new FlatColor(0xFF,0xFF,0xFF); public static final class MATERIAL{ private MATERIAL () {} diff --git a/src/mashine/ui/DataViewer.java b/src/mashine/ui/DataViewer.java deleted file mode 100644 index 322b57c..0000000 --- a/src/mashine/ui/DataViewer.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Interface to visualize range inputs - * - * @author procsynth - Antoine Pintout - * @since 13-02-2016` - */ - -package mashine.ui; - -import mashine.*; -import java.util.*; -import java.lang.Math; - -public class DataViewer extends UIBox{ - - public DataViewer (MaShine m) { - super(m, "DATA VIEWER ", 360, 50, 300, 600); - - } - - public void drawUI(){ - canvas.noStroke(); - canvas.textAlign(M.LEFT, M.TOP); - int offset = 30; - ArrayList rangeInputsName = new ArrayList(M.inputs.getRangeSet()); - Collections.sort(rangeInputsName); - - - for(String a : rangeInputsName){ - float val = M.inputs.getRange(a); - - // if 0.0 to 1.0 range - FlatColor.fill(canvas, Colors.MATERIAL.ORANGE.A400); - if(val <= 1 && (val % 1 != 0)){ - canvas.rect(1, offset - 3, M.min(width-1,(int) (width * val)), 14); - - // if 0-127 range (midi) - }else{ - canvas.rect(1, offset - 3, M.min(width-1,(int) (width * (float) (val/256))), 14); - } - FlatColor.fill(canvas, Colors.WHITE); - canvas.text(a + " " + (float) Math.round(val*100000)/100000 , 5, offset); - offset += 14; - } - } - -} \ No newline at end of file diff --git a/src/mashine/ui/DeviceEditor.java b/src/mashine/ui/DeviceEditor.java deleted file mode 100644 index e17db44..0000000 --- a/src/mashine/ui/DeviceEditor.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Interface to edit devices - * - * @author procsynth - Antoine Pintout - * @since 13-02-2016` - */ - -package mashine.ui; - -import mashine.*; -import mashine.ui.elements.*; -import mashine.scene.*; -import mashine.scene.features.*; -import java.util.HashMap; - -// UIREFORM - -public class DeviceEditor extends UIBox{ - - - public DeviceEditor (MaShine m) { - super(m, "DEVICE EDITOR ", 50, 50, 200, 450); - - elements.add(new TextButton(this, "new", 5, 28, - new Do(){public void x(){/*cloneCurrentDevice();*/}} - )); - - elements.add(new TextButton(this, "clone", 65, 28, - new Do(){public void x(){/*newDevice();*/}} - )); - - elements.add(new TextInput(this, "test", 125, 28, 70)); - - } - - public void drawUI(){ - HashMap selectedDevices = M.ui.getSelectedDevices(); - // add or remove elements - drawUI(selectedDevices); - } - - - public void drawUI(HashMap selectedDevices){ - - if(selectedDevices.size()==0){ - FlatColor.fill(canvas, Colors.WHITE); - canvas.textAlign(M.LEFT, M.TOP); - canvas.text("No device selected.", 0, 55); - }else if(selectedDevices.size() == 1){ - editOneDevice(selectedDevices.values().iterator().next()); - }else{ - editMultipleDevice(selectedDevices); - } - } - - public void editOneDevice(Device device){ - FlatColor.fill(canvas, Colors.WHITE); - canvas.textAlign(M.LEFT, M.TOP); - canvas.text(device.getIdentifier()+" selected.", 0, 55); - - // toDo : addr, identifier, universe edit - - for(Feature f : device.getFeatures()){ - if(f instanceof EditableFeature){ - // editable : animable in sequences, just list it, to remove it - }else{ - // uneditable : provide field by field modification possibilities - } - } - } - - public void editMultipleDevice(HashMap devices){ - FlatColor.fill(canvas, Colors.WHITE); - canvas.textAlign(M.LEFT, M.TOP); - canvas.text(devices.size() + " devices selected.", 0, 55); - } - -} \ No newline at end of file diff --git a/src/mashine/ui/Drawable.java b/src/mashine/ui/Drawable.java index 56298a3..6fa8b13 100644 --- a/src/mashine/ui/Drawable.java +++ b/src/mashine/ui/Drawable.java @@ -11,13 +11,13 @@ public abstract class Drawable extends Focusable{ public int width, height; private boolean init = false; - public Drawable(MaShine m, int x, int y, int width, int height){ - this(m, m.g, x, y, width, height); + public Drawable(int x, int y, int width, int height){ + this(MaShine.m.g, x, y, width, height); } - public Drawable(MaShine m, PGraphics parent, int x, int y, int width, int height){ - super(m); - canvas = M.createGraphics(width + 1, height + 1, M.P2D); + public Drawable(PGraphics parent, int x, int y, int width, int height){ + super(); + canvas = MaShine.m.createGraphics(width + 1, height + 1, MaShine.P2D); this.parent = parent; this.x = x; @@ -28,43 +28,43 @@ public Drawable(MaShine m, PGraphics parent, int x, int y, int width, int height public void draw(){ + //parent.endDraw(); + canvas.beginDraw(); + //canvas.clip(x, scroll, width+1, height+1); + if(!init){ + canvas.textFont(MaShine.ui.TEXTFONT); + canvas.textSize(MaShine.ui.TEXTSIZE); init = true; } - //parent.endDraw(); - canvas.beginDraw(); - - canvas.textFont(M.ui.TEXTFONT); - canvas.textSize(M.ui.TEXTSIZE); drawContent(); canvas.endDraw(); //parent.beginDraw(); - parent.image(canvas, x, y, width +1 , height + 1); + parent.image(canvas, x, y); + } - protected void drawContent(){ - M.println("empty drawContent called"); - } + protected void drawContent(){} public void tick(){} public boolean mouseIn(){ - return M.mouseX >= x && - M.mouseX < x + width && - M.mouseY >= y && - M.mouseY < y + height; + return MaShine.m.mouseX >= x && + MaShine.m.mouseX < x + width && + MaShine.m.mouseY >= y && + MaShine.m.mouseY < y + height; } - protected int mouseX() { - return M.mouseX - x; + public int mouseX() { + return MaShine.m.mouseX - x; } - protected int mouseY() { - return M.mouseY - y; + public int mouseY() { + return MaShine.m.mouseY - y; } } \ No newline at end of file diff --git a/src/mashine/ui/Element.java b/src/mashine/ui/Element.java index 1361167..3841d5e 100644 --- a/src/mashine/ui/Element.java +++ b/src/mashine/ui/Element.java @@ -16,9 +16,10 @@ public class Element extends Focusable{ protected int y; protected int width; protected int height; + protected boolean enabled = true; public Element (Drawable parent, int x, int y, int w, int h) { - super(parent.M); + super(); this.x = x; this.y = y; this.width = w; @@ -30,11 +31,19 @@ public void draw(){ drawContent(); } + public void disable(){ + enabled = false; + } + + public void enable(){ + enabled = true; + } + public void drawContent(){} /* Override Focusable.mouseIn() */ public boolean mouseIn(){ - return (M.inputs.getState("mouse.left.hold") && focus && P.hasFocus()) || ( + return enabled && (MaShine.inputs.getState("mouse.left.hold") && focus && P.hasFocus()) || ( P.mouseX() >= x && P.mouseX() < x + width && P.mouseY() >= y && @@ -42,16 +51,23 @@ public boolean mouseIn(){ } public boolean isDragged() { - return M.inputs.getState("mouse.left.hold") && mouseIn() && P.hasFocus(); + return MaShine.inputs.getState("mouse.left.drag") && mouseIn() && P.hasFocus(); } public boolean isClicked() { - return M.inputs.getState("mouse.left.press") && mouseIn() && P.hasFocus(); + return MaShine.inputs.getState("mouse.left.press") && mouseIn() && P.hasFocus(); + } + public boolean isReleased() { + return MaShine.inputs.getState("mouse.left.release") && mouseIn() && P.hasFocus(); } public boolean isHovered() { return mouseIn() && P.hasFocus(); } + + public boolean isEnabled() { + return enabled; + } } \ No newline at end of file diff --git a/src/mashine/ui/FlatColor.java b/src/mashine/ui/FlatColor.java index 76902e3..7cecef2 100644 --- a/src/mashine/ui/FlatColor.java +++ b/src/mashine/ui/FlatColor.java @@ -9,8 +9,11 @@ import java.awt.Color; import processing.core.PGraphics; +import java.io.Serializable; -public class FlatColor { +public class FlatColor implements Serializable{ + + private static final long serialVersionUID = 0xC0100001L; protected int r; protected int g; @@ -98,6 +101,7 @@ public void setAlpha(int alpha){ public void rotateHue(float angle) { h += angle; + h = h % 1.0f; updateRGB(); } @@ -159,6 +163,22 @@ public FlatColor withBrightness(float brightness){ return c; } + public FlatColor withAlphaAsWhite(){ + FlatColor c = new FlatColor(this); + c.setRed(getRed() + getAlpha()/2); + c.setGreen(getGreen() + getAlpha()/2); + c.setBlue(getBlue() + getAlpha()/2); + c.setAlpha(255); + return c; + } + + public FlatColor dim(float multiplier){ + FlatColor c = new FlatColor(this); + c.setBrightness(c.getBrightness() * multiplier); + c.setAlpha(Math.round(c.getAlpha() * multiplier)); + return c; + } + public int getRed(){ return r; } @@ -180,6 +200,9 @@ public float getSaturation(){ public float getBrightness(){ return v; } + public int getGrayscale(){ + return (int)Math.round(v*255.0); + } } \ No newline at end of file diff --git a/src/mashine/ui/Focusable.java b/src/mashine/ui/Focusable.java index e009193..18baaf7 100644 --- a/src/mashine/ui/Focusable.java +++ b/src/mashine/ui/Focusable.java @@ -7,15 +7,12 @@ package mashine.ui; -import mashine.*; public abstract class Focusable{ - public MaShine M; protected boolean focus = false; - public Focusable(MaShine m){ - M = m; + public Focusable(){ } public boolean mouseIn(){ diff --git a/src/mashine/ui/Menu.java b/src/mashine/ui/Menu.java deleted file mode 100644 index 761ef7d..0000000 --- a/src/mashine/ui/Menu.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Unused now... - * - * @author procsynth - Antoine Pintout - * @since 13-02-2016` - */ - -package mashine.ui; - -import mashine.*; -import mashine.ui.elements.*; -import java.util.ArrayList; - -public class Menu extends Drawable{ - - private ArrayList bigButtons; - private ArrayList elements; - - - public Menu(MaShine m){ - super(m, 0, 0, m.width, 40); - bigButtons = new ArrayList(); - elements = new ArrayList(); - focus = true; - - Do switchToScene = new Do(){public void x(){M.ui.open("DeviceEditor");}}; - Do switchToAnimate = new Do(){public void x(){/* OPEN SCENE TOOLS HERE */}}; - Do switchToLive = new Do(){public void x(){/* OPEN SCENE TOOLS HERE */}}; - Do openDataViewer = new Do(){public void x(){M.ui.open("DataViewer");}}; - Do openEventViewer = new Do(){public void x(){M.ui.open("EventViewer");}}; - - bigButtons.add(new TextButton(this, 5, 5, 120, 30, "PATCH", - Colors.WHITE, 60, - Colors.MATERIAL.CYAN._600, - Colors.MATERIAL.CYAN._700, - switchToScene)); - bigButtons.add(new TextButton(this, 130, 5, 120, 30, "ANIMATE", - Colors.WHITE, 60, - Colors.MATERIAL.DEEP_PURPLE._600, - Colors.MATERIAL.DEEP_PURPLE._700, - switchToAnimate)); - bigButtons.add(new TextButton(this, 255, 5, 120, 30, "LIVE", - Colors.WHITE, 60, - Colors.MATERIAL.RED._600, - Colors.MATERIAL.RED._700, - switchToLive)); - bigButtons.add(new TextButton(this, 390, 5, 70, 14, "event in", - Colors.WHITE, 10, - Colors.MATERIAL.TEAL._600, - Colors.MATERIAL.TEAL._800, - openEventViewer)); - bigButtons.add(new TextButton(this, 390, 21, 70, 14, "data in", - Colors.WHITE, 10, - Colors.MATERIAL.TEAL._600, - Colors.MATERIAL.TEAL._800, - openDataViewer)); - } - - public void drawContent(){ - canvas.noStroke(); - canvas.background(127); - FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._600); - canvas.rect(0, 0, width, height); - //canvas.rect(M.mouseX, M.mouseY, 20, 20); - - for(Element bb : bigButtons){ - bb.draw(); - } - } - -} \ No newline at end of file diff --git a/src/mashine/ui/SceneVisualizer.java b/src/mashine/ui/SceneVisualizer.java deleted file mode 100644 index 85d4bab..0000000 --- a/src/mashine/ui/SceneVisualizer.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Instance to visualize devices of the scene - * - * @author procsynth - Antoine Pintout - * @since 13-02-2016` - */ - -package mashine.ui; - -import mashine.*; -import mashine.ui.elements.*; -import mashine.scene.*; -import mashine.scene.features.*; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.ArrayList; - -public class SceneVisualizer extends Drawable { - - HashMap deviceElements; - HashMap selectedDevices; - - public SceneVisualizer(MaShine m){ - super(m, 10, 50, m.width - 10, m.height - 50); - deviceElements = new HashMap(); - selectedDevices = new HashMap(); - } - - public void drawContent(){ - - Frame frame = new Frame(); - - HashMap devices = M.scene.getDevices(); - HashMap frameFeatures = frame.getFeatures(); - - for(String deviceIdentifier : devices.keySet()){ - // add new DeviceElement if not already present - if(!deviceElements.containsKey(deviceIdentifier)){ - deviceElements.put(deviceIdentifier, new DeviceElement(this, devices.get(deviceIdentifier))); - M.println("Adding "+deviceIdentifier); - } - - DeviceElement de = deviceElements.get(deviceIdentifier); - - // selected it maybe - boolean selected = selectedDevices.containsKey(deviceIdentifier); - - if(M.inputs.getState("keyboard.16.hold") && de.isClicked()){ - if(selected){ - selectedDevices.remove(deviceIdentifier); - selected = false; - }else{ - selectedDevices.put(deviceIdentifier, devices.get(deviceIdentifier)); - selected = true; - } - }else if(de.isClicked()){ - selectedDevices.clear(); - selected = true; - selectedDevices.put(deviceIdentifier, devices.get(deviceIdentifier)); - } - - // draw it - de.setSelected(selected); - de.setFrameFeatures(frameFeatures); - de.draw(); - } - - // remove DeviceElement if Device not here anymore - for(String deviceIdentifier : deviceElements.keySet()){ - if(!devices.containsKey(deviceIdentifier)){ - deviceElements.remove(deviceIdentifier); - } - } - - } - - public HashMap getSelectedDevices(){ - return selectedDevices; - } - -} \ No newline at end of file diff --git a/src/mashine/ui/ScrollableView.java b/src/mashine/ui/ScrollableView.java deleted file mode 100644 index e69de29..0000000 diff --git a/src/mashine/ui/Status.java b/src/mashine/ui/Status.java index 04aa8ea..06dd8e9 100644 --- a/src/mashine/ui/Status.java +++ b/src/mashine/ui/Status.java @@ -7,60 +7,59 @@ package mashine.ui; -import mashine.*; -import mashine.ui.elements.*; -import processing.core.*; +import mashine.MaShine; +import processing.core.PApplet; import processing.data.StringDict; -import mashine.ui.Tetrahedron; // TODO, use drawable public class Status{ - private MaShine M; private StringDict status; private Tetrahedron tetrahedron; private int beatFade = 255; - public Status(MaShine m){ - M = m; + public Status(){ status = new StringDict(); - tetrahedron = new Tetrahedron(6, M); + tetrahedron = new Tetrahedron(6); + MaShine.inputs.registerRange("status.tetra.fill"); + MaShine.inputs.registerState("status.tetra.line"); + MaShine.inputs.range("status.tetra.fill", "minim.rms"); + MaShine.inputs.state("status.tetra.line", "minim.beat.interpolated"); } public void draw(){ - M.noStroke(); - if(M.inputs.getState("internal.ola.status")) - M.fill(84, 110, 122); + MaShine.m.noStroke(); + if(MaShine.inputs.getState("internal.ola.status")) + MaShine.m.fill(84, 110, 122); else - M.fill(0xD5, 0, 0); - M.rect(0, M.height - 22, M.width, 22); + MaShine.m.fill(0xD5, 0, 0); + MaShine.m.rect(0, MaShine.m.height - 22, MaShine.m.width, 22); String s = ""; for(String key : status.keys()){ s += key +": "+ status.get(key) + " "; } - M.fill(255, 255, 255); - M.text(s, 4, M.height - 6); + MaShine.m.fill(255, 255, 255); + MaShine.m.text(s.toLowerCase(), 4, MaShine.m.height - 7); + MaShine.m.textAlign(PApplet.RIGHT); + MaShine.m.text(PApplet.round(MaShine.m.frameRate), MaShine.m.width - 29, MaShine.m.height - 7); + MaShine.m.textAlign(PApplet.LEFT); - M.textAlign(PApplet.RIGHT); - M.text(PApplet.round(M.frameRate), M.width - 29, M.height - 7); - M.textAlign(PApplet.LEFT); + MaShine.m.ortho(); - M.ortho(); - - if(M.inputs.getState("minim.beat.interpolated")){ + if(MaShine.inputs.getState("status.tetra.line")){ beatFade = 255; } - M.stroke(0x64, 0xFF, 0xDA, beatFade); - M.strokeWeight((float)1.1); - M.fill(0, M.inputs.getRange("minim.rms")*255); - tetrahedron.draw(M.width - 13, M.height - 11, 120); + MaShine.m.stroke(0x64, 0xFF, 0xDA, beatFade); + MaShine.m.strokeWeight((float)1.1); + MaShine.m.fill(0, (float)MaShine.inputs.getRange("status.tetra.fill")*255); + tetrahedron.draw(MaShine.m.width - 13, MaShine.m.height - 11, 120); - beatFade = M.max(50, beatFade - 15); + beatFade = Math.max(50, beatFade - 15); } public void set(String key, String content){ diff --git a/src/mashine/ui/Tetrahedron.java b/src/mashine/ui/Tetrahedron.java index df2315d..be3141c 100644 --- a/src/mashine/ui/Tetrahedron.java +++ b/src/mashine/ui/Tetrahedron.java @@ -7,15 +7,16 @@ package mashine.ui; -import processing.core.*; +import mashine.MaShine; +import processing.core.PApplet; public class Tetrahedron{ private PApplet app; private int edgeLength; - private double theta, angleX, angleY, rx, ry; + private double theta, angleX, angleY; - public Tetrahedron(int iedgeLength, PApplet p){ - app = p; + public Tetrahedron(int iedgeLength){ + app = MaShine.m; edgeLength = iedgeLength; } diff --git a/src/mashine/ui/Tradi.java b/src/mashine/ui/Tradi.java new file mode 100644 index 0000000..a754bbf --- /dev/null +++ b/src/mashine/ui/Tradi.java @@ -0,0 +1,69 @@ +package mashine.scene.features; + +import java.util.LinkedHashMap; +import mashine.ui.FlatColor; + +public final class Tradi extends ColorFeature { + + private static final long serialVersionUID = 0xD1DD0001L; + + public Tradi(int dim){ + super("Tradi", 1); + fields.put("intensity", dim); + } + + public Tradi(){ + this(0); + } + + public Tradi(Tradi f){ + super(f); + } + + public Tradi(Feature f){ + super(f); + } + + public void setIntensity(int v){ + setField("intensity", v); + } + + public FlatColor getColor(){ + return getLinkedColor().withAlpha(255); + } + + public FlatColor getLinkedColor(){ + FlatColor rc; + if(linkedColor != null){ + rc = linkedColor; + }else{ + rc = new FlatColor(fields.get("intensity")); + } + return rc; + } + + public LinkedHashMap getFields(){ + LinkedHashMap rf; + + if(linkedColor != null){ + rf = new LinkedHashMap(); + rf.put("intensity", linkedColor.getGrayscale()); + }else{ + rf = new LinkedHashMap(fields); + } + + return rf; + } + + public short[] toArray(){ + if(null == linkedColor){ + return new short[]{ + fields.get("intensity").shortValue() + }; + } + + return new short[]{ + (short)linkedColor.getGrayscale() + }; + } +} \ No newline at end of file diff --git a/src/mashine/ui/UIBox.java b/src/mashine/ui/UIBox.java index c33f9b7..9c5b832 100644 --- a/src/mashine/ui/UIBox.java +++ b/src/mashine/ui/UIBox.java @@ -7,65 +7,114 @@ package mashine.ui; -import mashine.*; -import mashine.ui.elements.*; -import processing.core.*; -import java.util.LinkedList; +import java.util.LinkedList; + +import processing.core.PConstants; + +import mashine.MaShine; +import mashine.UI; +import mashine.ui.elements.CloseButton; +import mashine.ui.elements.Grabber; public class UIBox extends Drawable{ protected String title; + private int scroll; + private int vheight; protected LinkedList elements; + private Element grabber; + private Element closeButton; - public UIBox(MaShine m, String t, int x, int y, int w, int h){ - super(m, x, y, w, h); + public UIBox(String t, int x, int y, int w, int h){ + this(t, x, y, w, h, h); + } + public UIBox(String t, int x, int y, int w, int h, int vh){ + super(x, y, w, h); title = t; - this.x = M.min(x, M.width - width); - this.y = M.min(y, M.height - height); - width = M.min(w, M.width); - height = M.min(h, M.height); + this.x = Math.min(x, MaShine.m.width - width); + this.y = Math.min(y, MaShine.m.height - height - 23); + width = Math.min(w, MaShine.m.width); + height = Math.min(h, MaShine.m.height); + vheight = vh; + scroll = 0; elements = new LinkedList(); - elements.add(new Grabber(this)); - elements.add(new CloseButton(this)); + grabber = new Grabber(this); + closeButton = new CloseButton(this); + elements.add(grabber); + elements.add(closeButton); } protected void drawContent(){ - drawFrame(); + FlatColor.stroke(canvas,Colors.MATERIAL.BLUE_GREY._800); + FlatColor.fill(canvas,Colors.MATERIAL.BLUE_GREY._600); + canvas.rect(0, 0, width, height); + + scroll(); + canvas.pushMatrix(); + canvas.translate(0, -scroll); + drawUI(); + canvas.popMatrix(); + drawFixedUI(); + elements.sort(new UI.SortByFocus()); + for(Element el : elements){ - el.draw(); - el.defocus(); + if(el != closeButton && el != grabber) + el.draw(); + if(el == elements.getLast() && el.mouseIn()) + el.focus(); + else + if(el.hasFocus()) + el.defocus(); } - if(elements.getLast().mouseIn()) - elements.getLast().focus(); + drawFrame(); + closeButton.draw(); + grabber.draw(); - drawUI(); } - private void drawFrame(){ - FlatColor.stroke(canvas,Colors.MATERIAL.BLUE_GREY._800); - FlatColor.fill(canvas,Colors.MATERIAL.BLUE_GREY._600); - canvas.rect(0, 0, width, height); + protected void drawFrame(){ canvas.noStroke(); FlatColor.fill(canvas,Colors.MATERIAL.BLUE_GREY._900); - canvas.rect(0, 0, width, 22); - + canvas.rect(0, 0, width+1, 22); + FlatColor.fill(canvas,Colors.WHITE); - canvas.textAlign(M.CENTER, M.TOP); + canvas.textAlign(PConstants.CENTER, PConstants.TOP); canvas.text(title,width / 2, 7); } protected void drawUI() {} + protected void drawFixedUI() {} + + private void scroll(){ + if(mouseIn( )&& hasFocus()){ + if(MaShine.inputs.getState("mouse.wheel.encoder.on")){ + scroll += 25; + }else if(MaShine.inputs.getState("mouse.wheel.encoder.off")){ + scroll -= 25; + } + scroll = Math.max(0, Math.min(vheight-height, scroll)); + } + } + + public void setVirtualHeight(int vh){ + vheight = Math.max(height, vh); + scroll = Math.max(0, Math.min(vheight-height, scroll)); + } public void moveBox(int x, int y) { - this.x = M.max(0, M.min(x, M.width - width)); - this.y = M.max(0, M.min(y, M.height - height)); + this.x = Math.max(0, Math.min(x, MaShine.m.width - width)); + this.y = Math.max(0, Math.min(y, MaShine.m.height - height - 23)); + } + + protected int getScroll(){ + return scroll; } } \ No newline at end of file diff --git a/src/mashine/ui/boxes/ColorPalette.java b/src/mashine/ui/boxes/ColorPalette.java new file mode 100644 index 0000000..6b08f1f --- /dev/null +++ b/src/mashine/ui/boxes/ColorPalette.java @@ -0,0 +1,171 @@ +/** + * Interface to select sequences of frames + * + * @author procsynth - Antoine Pintout + * @since 18-03-2016` + */ + +package mashine.ui.boxes; + +import java.util.ArrayList; +import java.util.HashMap; + +import mashine.MaShine; +import mashine.ui.Colors; +import mashine.ui.Element; +import mashine.ui.FlatColor; +import mashine.ui.UIBox; +import mashine.ui.elements.RangeInput; +import processing.core.PConstants; + +public class ColorPalette extends UIBox{ + + FlatColor color; + HashMap colorParamInputs; + + public ColorPalette () { + super("COLORS", 450, 400, 250, 352); + colorParamInputs = new HashMap(); + + colorParamInputs.put("hue", new RangeInput(this, 0f, 0f, 1f, 0.0001f, 116, 30, 128)); + colorParamInputs.put("sat", new RangeInput(this, 0f, 0f, 1f, 0.0001f, 116, 47, 128)); + colorParamInputs.put("lum", new RangeInput(this, 0f, 0f, 1f, 0.0001f, 116, 64, 128)); + + colorParamInputs.put("red", new RangeInput(this, 0f, 0f, 255f, 1f, 116, 84, 128)); + colorParamInputs.put("green", new RangeInput(this, 0f, 0f, 255f, 1f, 116, 101, 128)); + colorParamInputs.put("blue", new RangeInput(this, 0f, 0f, 255f, 1f, 116, 118, 128)); + + colorParamInputs.put("white", new RangeInput(this, 0f, 0f, 255f, 1f, 116, 138, 128)); + + for(Element e : colorParamInputs.values()){ + elements.add(e); + } + + color = new FlatColor(Colors.WHITE); + } + + public void tick(){ + + if(Math.abs(color.getHue() - colorParamInputs.get("hue").value()) > 0.00011){ + color.setHue(colorParamInputs.get("hue").value()); + updateRGBInputs(); + return; + } + if(Math.abs(color.getSaturation() - colorParamInputs.get("sat").value()) > 0.00011){ + color.setSaturation(colorParamInputs.get("sat").value()); + updateRGBInputs(); + return; + } + if(Math.abs(color.getBrightness() - colorParamInputs.get("lum").value()) > 0.00011){ + color.setBrightness(colorParamInputs.get("lum").value()); + updateRGBInputs(); + return; + } + + if(color.getRed() != colorParamInputs.get("red").value()){ + color.setRed(Math.round(colorParamInputs.get("red").value())); + updateHSLInputs(); + return; + } + if(color.getGreen() != colorParamInputs.get("green").value()){ + color.setGreen(Math.round(colorParamInputs.get("green").value())); + updateHSLInputs(); + return; + } + if(color.getBlue() != colorParamInputs.get("blue").value()){ + color.setBlue(Math.round(colorParamInputs.get("blue").value())); + updateHSLInputs(); + return; + } + if(color.getAlpha() != colorParamInputs.get("white").value()){ + color.setAlpha(Math.round(colorParamInputs.get("white").value())); + } + + } + + public void drawUI(){ + + ArrayList colors = MaShine.bank.getColors(); + + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._800); + canvas.textAlign(PConstants.RIGHT, PConstants.TOP); + canvas.text("hue", 240, 30 + 3); + canvas.text("saturation", 240, 47 + 3); + canvas.text("luminance", 240, 64 + 3); + canvas.text("red", 240, 84 + 3); + canvas.text("green", 240,101 + 3); + canvas.text("blue", 240,118 + 3); + canvas.text("white", 240,138 + 3); + + FlatColor.fill(canvas, color.withAlphaAsWhite()); + canvas.noStroke(); + canvas.rect(8, 30, 103, 103); + + int i = 0; + + for(FlatColor c : colors){ + canvas.noStroke(); + FlatColor.fill(canvas,c.withAlphaAsWhite()); + + int x, y; + + if(i < 154){ + x = 8 + (i % 14) * 17; + y = 160 + (int) Math.floor(i/14)*17; + }else{ + x = 8 + (i % 154) * 17; + y = 138; + } + + if(hasFocus() && + y-1 < mouseY() && + mouseY() < y+16 && + x-1 < mouseX() && + mouseX() < x+16 && + MaShine.inputs.getState("mouse.left.press")) + { + color = c; + updateInputs(); + } + + canvas.rect(x, y, 15, 15); + if(c == color){ + FlatColor.stroke(canvas, Colors.WHITE); + canvas.noFill(); + canvas.rect(x,y,14,14); + } + + i++; + } + } + + private void updateInputs(){ + updateRGBInputs(); + updateHSLInputs(); + colorParamInputs.get("white").setValue(color.getAlpha()); + } + + private void updateRGBInputs(){ + colorParamInputs.get("red").setValue(color.getRed()); + colorParamInputs.get("green").setValue(color.getGreen()); + colorParamInputs.get("blue").setValue(color.getBlue()); + } + private void updateHSLInputs(){ + colorParamInputs.get("hue").setValue(color.getHue()); + colorParamInputs.get("sat").setValue(color.getSaturation()); + colorParamInputs.get("lum").setValue(color.getBrightness()); + } + + public FlatColor getSelectedColor(){ + return color; + } + + public void setSelectedColor(FlatColor c){ + color = c; + } + + public void updateReference(){ + color = new FlatColor(color); + } + +} \ No newline at end of file diff --git a/src/mashine/ui/boxes/ControlSurface.java b/src/mashine/ui/boxes/ControlSurface.java new file mode 100644 index 0000000..d0f2a0c --- /dev/null +++ b/src/mashine/ui/boxes/ControlSurface.java @@ -0,0 +1,86 @@ +/** + * Virtual control surface; + * + * @author procsynth - Antoine Pintout + * @since 09-04-2016` + */ + +package mashine.ui.boxes; + +import java.util.ArrayList; +import java.util.List; +import java.lang.Math; + +import mashine.MaShine; +import mashine.ui.UIBox; +import mashine.ui.FlatColor; +import mashine.ui.Colors; +import mashine.ui.elements.VerticalRangeInput; + +import processing.core.PConstants; + +public class ControlSurface extends UIBox{ + + private final int nbSliders = 32; + ArrayList sliders; + + public ControlSurface () { + super("CONTROL SURFACE", 100, 600, 730, 305); + + int offset = 15; + sliders = new ArrayList(nbSliders); + + for(int i = 0; i < nbSliders; i ++){ + VerticalRangeInput vri = new VerticalRangeInput(this, 0f, 0f, 1f, 0.0001f, offset, 35, 256); + offset += 20; + sliders.add(vri); + elements.add(vri); + } + } + + public void drawUI(){ + canvas.noStroke(); + FlatColor.fill(canvas,Colors.MATERIAL.BLUE_GREY._900); + canvas.rect(0, 0, width, height); + } + + public void drawFrame(){ + + int offset = 23; + + FlatColor.fill(canvas, Colors.WHITE); + canvas.textAlign(PConstants.CENTER, PConstants.CENTER); + for(int i = 0; i < nbSliders; i ++){ + canvas.text(i+1, offset, 44); + offset += 20; + } + + canvas.noStroke(); + FlatColor.fill(canvas,Colors.MATERIAL.GREY._900); + canvas.rect(0, 0, width+1, 22); + + FlatColor.fill(canvas,Colors.WHITE); + canvas.textAlign(PConstants.CENTER, PConstants.TOP); + canvas.text(title,width / 2, 7); + + for(int i = 0; i < nbSliders; i++){ + MaShine.inputs.setRange("console.slider."+String.format("%2s", i+1).replace(' ', '0'), sliders.get(i).value()); + } + + } + + public List getValues(){ + List l = new ArrayList(nbSliders); + for(int i = 0; i < nbSliders; i++){ + l.add(sliders.get(i).value()); + } + return l; + } + + public void setValues(List l){ + for(int i = 0; i < nbSliders; i++){ + sliders.get(i).setValue(l.get(i)); + } + } + +} \ No newline at end of file diff --git a/src/mashine/ui/boxes/DataViewer.java b/src/mashine/ui/boxes/DataViewer.java new file mode 100644 index 0000000..b75ffdf --- /dev/null +++ b/src/mashine/ui/boxes/DataViewer.java @@ -0,0 +1,52 @@ +/** + * Interface to visualize range inputs + * + * @author procsynth - Antoine Pintout + * @since 13-02-2016` + */ + +package mashine.ui.boxes; + +import java.util.ArrayList; +import java.util.Collections; + +import mashine.MaShine; +import mashine.ui.Colors; +import mashine.ui.FlatColor; +import mashine.ui.UIBox; + +public class DataViewer extends UIBox{ + + public DataViewer () { + super("DATA VIEWER ", 360, 50, 200, 400); + } + + public void drawUI(){ + canvas.noStroke(); + canvas.textAlign(MaShine.LEFT, MaShine.TOP); + int offset = 30; + ArrayList rangeInputsName = new ArrayList(MaShine.inputs.getRangeInputSet()); + Collections.sort(rangeInputsName); + + + for(String a : rangeInputsName){ + double val = MaShine.inputs.getRange(a); + + // if 0.0 to 1.0 range + FlatColor.fill(canvas, Colors.MATERIAL.ORANGE.A400); + if(val <= 1 && (val % 1 != 0)){ + canvas.rect(1, offset - 3, Math.min(width-1,(int) (width * val)), 14); + + // if 0-127 range (midi) + }else{ + canvas.rect(1, offset - 3, Math.min(width-1,(int) (width * (double) (val/256))), 14); + } + FlatColor.fill(canvas, Colors.WHITE); + canvas.text(a + " " + (float) Math.round(val*100000)/100000 , 5, offset); + offset += 14; + } + + setVirtualHeight(offset); + } + +} \ No newline at end of file diff --git a/src/mashine/ui/boxes/DeviceEditor.java b/src/mashine/ui/boxes/DeviceEditor.java new file mode 100644 index 0000000..74bd275 --- /dev/null +++ b/src/mashine/ui/boxes/DeviceEditor.java @@ -0,0 +1,419 @@ +/** + * Interface to edit devices + * + * @author procsynth - Antoine Pintout + * @since 13-02-2016` + */ + +package mashine.ui.boxes; + +import java.util.ArrayList; +import java.util.HashMap; + +import mashine.Do; +import mashine.MaShine; +import mashine.Scene; +import mashine.UI; +import mashine.scene.Device; +import mashine.scene.features.EditableFeature; +import mashine.scene.features.Feature; +import mashine.scene.features.FixedField; +import mashine.scene.features.SingleField; +import mashine.ui.Colors; +import mashine.ui.Element; +import mashine.ui.FlatColor; +import mashine.ui.UIBox; +import mashine.ui.elements.RangeInput; +import mashine.ui.elements.TextButton; +import mashine.ui.elements.TextInput; +import processing.core.PConstants; + +// UIREFORM + +public class DeviceEditor extends UIBox{ + + private String featuresHash = ""; + private ArrayList featureElements; + private HashMap featureInputs; + private TextInput deviceNameElement; + private TextInput genericFieldInputElement; + private RangeInput universeElement; + private RangeInput addressElement; + + private RangeInput xPosElement; + private RangeInput yPosElement; + private RangeInput widthElement; + private RangeInput heightElement; + + public DeviceEditor () { + super("SCENE EDITOR", 50, 50, 200, 450); + + featureElements = new ArrayList(); + featureInputs = new HashMap(); + + elements.add(new TextButton(this, "delete", 146, 28, + new Do(){public void x(){removeSelectedDevices();}} + )); + elements.add(new TextButton(this, "clone", 5, 48, + new Do(){public void x(){cloneSelectedDevices();}} + )); + + elements.add(new TextButton(this, "fixed", 146, height - 20, + new Do(){public void x(){addFixedField();}} + )); + elements.add(new TextButton(this, "single", 146, height - 37, + new Do(){public void x(){addSingleField();}} + )); + + int offset = 57; + for(final String featureClass : Scene.FEATURES.keySet()){ + String shortName = featureClass.substring(featureClass.lastIndexOf('.') + 1); + elements.add(new TextButton(this, shortName, 146, height - offset, + new Do(){public void x(){addFeature(featureClass);}} + )); + offset += 17; + } + + deviceNameElement = new TextInput(this, "devName", 0, 28, 93); + universeElement = new RangeInput(this, 42f, 0f, 99f, 1f, 95, 28, 20); + addressElement = new RangeInput(this, 1f, 1f, 512f, 1f, 117, 28, 27); + genericFieldInputElement = new TextInput(this, "generic", 43, height - 20, 100); + + xPosElement = new RangeInput(this, 0f, 0f, 4000f, 1f, 0, height - 74, 60); + yPosElement = new RangeInput(this, 0f, 0f, 3000f, 1f, 62, height - 74, 60); + widthElement = new RangeInput(this, 60f, 60f, 300f, 1f, 0, height - 57, 60); + heightElement = new RangeInput(this, 15f, 15f, 600f, 1f, 62, height - 57, 60); + + elements.add(deviceNameElement); + elements.add(universeElement); + elements.add(addressElement); + elements.add(genericFieldInputElement); + + elements.add(xPosElement); + elements.add(yPosElement); + elements.add(widthElement); + elements.add(heightElement); + + } + + public void tick(){ + moveSelectedDevices(); + } + + public void drawUI(){ + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + // add or remove elements + + + canvas.textAlign(PConstants.LEFT, PConstants.TOP); + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._300); + canvas.text("id", 74, 45); + canvas.text("un", 94, 45); + canvas.text("addr", 116, 45); + + if(!selectedDevices.isEmpty()){ + + Device firstDevice = selectedDevices.get(0); + // set common properties inputs + if(selectedDevices.size() == 1){ + deviceNameElement.enable(); + }else{ + deviceNameElement.disable(); + deviceNameElement.setValue("("+ selectedDevices.size() + " devices)"); + } + + // dress up a list of common features, order does not matter + ArrayList commonFeatures = Device.commonFeatures(selectedDevices); + + Integer commonUniverse = firstDevice.getUniverse(); + Integer commonAddress = firstDevice.getStartAddress(); + Integer commonXPos = firstDevice.getX(); + Integer commonYPos = firstDevice.getY(); + Integer commonWidth = firstDevice.getWidth(); + Integer commonHeight = firstDevice.getHeight(); + + for(Device d : selectedDevices){ + + if(commonUniverse != null && d.getUniverse() != commonUniverse){ + commonUniverse = null; + } + + if(commonAddress != null && d.getStartAddress() != commonAddress){ + commonAddress = null; + } + + if(commonXPos != null && d.getX() != commonXPos){commonXPos = null;} + if(commonYPos != null && d.getY() != commonYPos){commonYPos = null;} + if(commonWidth != null && d.getWidth() != commonWidth){commonWidth = null;} + if(commonHeight != null && d.getHeight() != commonHeight){commonHeight = null;} + + } + + + // compare with elements already present + + + // if list doesn't match in content or position; break and renew elements; else make sure input values match dev values + if(!featuresHash.equals(selectedDevicesHash(selectedDevices))){ + featuresHash = selectedDevicesHash(selectedDevices); + + if(selectedDevices.size() == 1){ + deviceNameElement.setValue(firstDevice.getName()); + addressElement.setValue(firstDevice.getStartAddress()); + universeElement.setValue(firstDevice.getUniverse()); + xPosElement.setValue(firstDevice.getX()); + yPosElement.setValue(firstDevice.getY()); + widthElement.setValue(firstDevice.getWidth()); + heightElement.setValue(firstDevice.getHeight()); + }else{ + if(commonUniverse == null){ + universeElement.setValue(null); + universeElement.setStringValue("_"); + }else{ + universeElement.setValue(commonUniverse); + } + + if(commonAddress == null){ + addressElement.setValue(null); + addressElement.setStringValue("_"); + }else{addressElement.setValue(commonAddress);} + + + if(commonXPos == null){ + xPosElement.setValue(null); + xPosElement.setStringValue("_"); + }else{xPosElement.setValue(commonXPos);} + if(commonYPos == null){ + yPosElement.setValue(null); + yPosElement.setStringValue("_"); + }else{yPosElement.setValue(commonYPos);} + if(commonWidth == null){ + widthElement.setValue(null); + widthElement.setStringValue("_"); + }else{widthElement.setValue(commonWidth);} + if(commonHeight == null){ + heightElement.setValue(null); + heightElement.setStringValue("_"); + }else{heightElement.setValue(commonHeight);} + + + } + + + featureElements = new ArrayList(); + + int offset = 67; + for(final Feature f : commonFeatures){ + // remove button // eventual input field // (move up/down for later) + featureElements.add(new TextButton(this, "delete", 146, offset, + new Do(){public void x(){removeFeature(f.getType());}}, true + )); + offset += 17; + if(!(f instanceof EditableFeature)){ + offset -= 17; + for(String fi : f.getFields().keySet()){ + + RangeInput e = new RangeInput(this, f.getFields().get(fi), 5, offset, 40); + + for(Device d : selectedDevices){ + + Integer devFieldValue = (d).getFeatureField(f.getType() +"."+ fi); + + if(devFieldValue == null || !devFieldValue.equals(f.getFields().get(fi))){ + e.setValue(null); + e.setStringValue("_"); + break; + } + } + featureInputs.put(f.getType() +"."+ fi, e); + featureElements.add(e); + offset += 17; + } + } + } + // else update device objects with new values from inputs + }else{ + + if(selectedDevices.size() == 1){ + MaShine.scene.renameDevice(selectedDevices.get(0), deviceNameElement.value()); + } + + for(String ff : featureInputs.keySet()){ + if(featureInputs.get(ff).value() != null){ + updateFeature(ff, Math.round(featureInputs.get(ff).value())); + } + } + if(universeElement.value() != null){ + updateUniverse(Math.round(universeElement.value())); + } + if(addressElement.value() != null){ + updateAddress(Math.round(addressElement.value())); + } + if(xPosElement.value() != null){ + updateXPos(Math.round(xPosElement.value())); + } + if(yPosElement.value() != null){ + updateYPos(Math.round(yPosElement.value())); + } + if(widthElement.value() != null){ + updateWidth(Math.round(widthElement.value())); + } + if(heightElement.value() != null){ + updateHeight(Math.round(heightElement.value())); + } + } + + featureElements.sort(new UI.SortByFocus()); + + // draw elements + for(Element el : featureElements){ + el.draw(); + if(el.mouseIn()){ + el.focus(); + }else{ + if(el.hasFocus()) + el.defocus(); + } + } + + // draw text + int offset = 70; + canvas.textAlign(PConstants.LEFT, PConstants.TOP); + FlatColor.fill(canvas, Colors.WHITE); + for(Feature f : commonFeatures){ + canvas.text(f.getType(), 65, offset); + offset +=17; + } + + }else{ + if(!featuresHash.equals(selectedDevicesHash(selectedDevices))){ + featuresHash = selectedDevicesHash(selectedDevices); + + deviceNameElement.enable(); + // prefill for new device + deviceNameElement.setValue("newDevice"); // find a valid name using MaShine.scene + } + } + } + + private void removeFeature(String featureId){ + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + featuresHash = ""; + for(Device d : selectedDevices){ + (d).removeFeature(featureId); + } + } + + private void cloneSelectedDevices(){ + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + ArrayList newSelectedDevices = new ArrayList(); + for(Device d : selectedDevices){ + Device newDev = new Device(d, d.getName()); + + MaShine.scene.addDevice(newDev); + newSelectedDevices.add(newDev); + } + + MaShine.ui.setSelectedDevices(newSelectedDevices); + } + + private void removeSelectedDevices(){ + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + for(Device d : selectedDevices){ + MaShine.scene.removeDevice((d)); + } + } + + private void updateFeature(String featureField, int value){ + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + for(Device d : selectedDevices){ + (d).updateFeature(featureField, value); + } + } + + private void addFeature(String featureClassName){ + if(Scene.FEATURES.containsKey(featureClassName)){ + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + for(Device d : selectedDevices){ + try{ + (d).addFeature((Feature) Scene.FEATURES.get(featureClassName).newInstance()); + }catch(Exception ignore){} + } + featuresHash = ""; + } + } + + private void addFixedField(){ + String fieldName = genericFieldInputElement.value(); + if(fieldName != null){ + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + for(Device d : selectedDevices){ + (d).addFeature(new FixedField(fieldName)); + } + featuresHash = ""; + } + } + + private void addSingleField(){ + String fieldName = genericFieldInputElement.value(); + if(fieldName != null){ + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + for(Device d : selectedDevices){ + d.addFeature(new SingleField(fieldName)); + } + featuresHash = ""; + } + } + + private void updateUniverse(int universe){ + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + for(Device d : selectedDevices){ + (d).setUniverse(universe); + } + } + private void updateAddress(int address){ + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + for(Device d : selectedDevices){ + (d).setStartAddress(address); + } + } + + private void updateXPos(int x){for(Device d : MaShine.ui.getSelectedDevices()){(d).setX(x);}} + private void updateYPos(int x){for(Device d : MaShine.ui.getSelectedDevices()){(d).setY(x);}} + private void updateWidth(int x){for(Device d : MaShine.ui.getSelectedDevices()){(d).setWidth(x);}} + private void updateHeight(int x){for(Device d : MaShine.ui.getSelectedDevices()){(d).setHeight(x);}} + + private String selectedDevicesHash(ArrayList selectedDevices){ + String hash = "_" + selectedDevices.size(); + for(Device d : selectedDevices){ + hash += d.getIdentifier(); + } + return hash; + } + + private void moveSelectedDevices(){ + + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + if(MaShine.inputs.getState("keyboard.38.hold") && !MaShine.inputs.getState("keyboard.16.hold")){ + for(Device d : selectedDevices){ + (d).moveUp(); + } + } + if(MaShine.inputs.getState("keyboard.40.hold") && !MaShine.inputs.getState("keyboard.16.hold")){ + for(Device d : selectedDevices){ + (d).moveDown(); + } + } + if(MaShine.inputs.getState("keyboard.37.hold") && !MaShine.inputs.getState("keyboard.16.hold")){ + for(Device d : selectedDevices){ + (d).moveLeft(); + } + } + if(MaShine.inputs.getState("keyboard.39.hold") && !MaShine.inputs.getState("keyboard.16.hold")){ + for(Device d : selectedDevices){ + (d).moveRight(); + } + } + } + +} \ No newline at end of file diff --git a/src/mashine/ui/boxes/DeviceSelector.java b/src/mashine/ui/boxes/DeviceSelector.java new file mode 100644 index 0000000..b1ab9ec --- /dev/null +++ b/src/mashine/ui/boxes/DeviceSelector.java @@ -0,0 +1,295 @@ +/** + * Interface to select and group devices; + * + * @author procsynth - Antoine Pintout + * @since 09-04-2016` + */ + +package mashine.ui.boxes; + +import java.util.ArrayList; +import java.util.Map; +import java.lang.Math; + +import mashine.Do; +import mashine.MaShine; +import mashine.ui.Colors; +import mashine.ui.FlatColor; +import mashine.ui.UIBox; +import mashine.ui.elements.TextButton; +import mashine.ui.elements.RangeInput; +import mashine.ui.elements.TextInput; +import mashine.scene.Device; +import mashine.scene.DeviceGroup; + +import processing.core.PConstants; + +public class DeviceSelector extends UIBox{ + + private DeviceGroup selectedGroup; + private Device selectedDevice; + + private RangeInput weightInput; + private RangeInput univSelectInput; + private TextInput groupNameInput; + + public DeviceSelector () { + super("DEVICES", 100, 600, 410, 230); + + weightInput = new RangeInput(this,205, height-15, 40); + groupNameInput = new TextInput(this,"new group", 0, height-15, 110); + + elements.add(weightInput); + elements.add(groupNameInput); + elements.add(new TextButton(this,"new", 112, height-15, 30, + new Do(){public void x(){cloneGroup();}} + )); + elements.add(new TextButton(this,"delete", 144, height-15, 50, + new Do(){public void x(){deleteGroup();}} + )); + elements.add(new TextButton(this,"remove", 250, height-15, 50, + new Do(){public void x(){removeDevice();}} + )); + elements.add(new TextButton(this,"reverse", 305, height-15, 55, + new Do(){public void x(){reverseWeights();}} + )); + + // SIDE BUTTONS + + elements.add(new TextButton(this,"clear", width-59, 39, 60, + new Do(){public void x(){selectClear();}} + )); + + elements.add(new TextButton(this,"all", width-59, 56, 60, + new Do(){public void x(){selectAll();}} + )); + + elements.add(new TextButton(this,"univ.", width-59, 73, 40, + new Do(){public void x(){selectUniverse();}} + )); + + univSelectInput = new RangeInput(this, 1f, 0f, 99f, 1f, width-19, 73, 20); + elements.add(univSelectInput); + + + elements.add(new TextButton(this,"> set", width-59, height-66, 60, + new Do(){public void x(){setGroupAsSelection();}} + )); + + elements.add(new TextButton(this,"< add", width-59, height-49, 60, + new Do(){public void x(){addCurrent();}} + )); + elements.add(new TextButton(this,"remove", width-59, height-32, 60, + new Do(){public void x(){removeCurrent();}} + )); + + + } + + public void tick(){} + + public void drawFixedUI(){ + canvas.noStroke(); + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._900); + canvas.rect(1, height - 16, width -1, 17); + FlatColor.stroke(canvas, Colors.MATERIAL.BLUE_GREY._900); + canvas.line(199, 20, 199, height-1); + canvas.line(width-65, 20, width-65, height-1); + canvas.noStroke(); + + FlatColor.fill(canvas, Colors.WHITE); + canvas.textAlign(PConstants.RIGHT, PConstants.TOP); + canvas.text("select°:", width -4, 25); + } + + public void drawUI(){ + DeviceGroup currentSelection = new DeviceGroup("(selection)", MaShine.ui.getSelectedDevices()); + ArrayList groups = new ArrayList(MaShine.scene.getGroups()); + groups.add(0, currentSelection); + + if(selectedGroup == null || selectedGroup.getName().equals("(selection)")){ + selectedGroup = currentSelection; + if(!groupNameInput.value().equals("new group")){ + cloneGroup(groupNameInput.value()); + } + }else{ + if(!groupNameInput.value().equals(selectedGroup.getName())){ + selectedGroup.setName(groupNameInput.value()); + } + //if(selectedDevice != null && !weightInput.value().equals(selectedGroup.getWeight(selectedDevice))){ + // selectedGroup.setWeight(selectedDevice, Math.round(weightInput.value())); + //} + } + Map selectedDevices = selectedGroup.getDevices(); + + canvas.noStroke(); + + + int index = 0; + int offset = 30; + + for(DeviceGroup g : groups){ + if(g == selectedGroup){ + FlatColor.fill(canvas,Colors.MATERIAL.ORANGE.A400); + }else{ + if(index % 2 == 0){ + FlatColor.fill(canvas,Colors.MATERIAL.BLUE_GREY._600); + }else{ + FlatColor.fill(canvas,Colors.MATERIAL.BLUE_GREY._500); + } + } + canvas.rect(1, offset - 3, 199, 14); + + if(isClicked(0, offset - 3, 199)){ + setSelectedGroup(g); + } + + canvas.textAlign(PConstants.LEFT, PConstants.TOP); + FlatColor.fill(canvas, Colors.WHITE); + canvas.text(g.getName(), 5, offset); + offset += 14; + index ++; + } + + offset = 30; + index = 0; + + for(Device d : selectedDevices.keySet()){ + if(d == selectedDevice){ + FlatColor.fill(canvas,Colors.MATERIAL.ORANGE.A400); + }else{ + if(index % 2 == 0){ + FlatColor.fill(canvas,Colors.MATERIAL.BLUE_GREY._600); + }else{ + FlatColor.fill(canvas,Colors.MATERIAL.BLUE_GREY._500); + } + } + canvas.rect(200, offset - 3, 145, 14); + + if(isClicked(200, offset - 3, 145)){ + setSelectedDevice(d); + } + + canvas.textAlign(PConstants.LEFT, PConstants.TOP); + FlatColor.fill(canvas, Colors.WHITE); + canvas.text(d.getName() + " ("+ selectedDevices.get(d)+")", 205, offset); + offset += 14; + index ++; + } + + if(selectedGroup != null && !selectedGroup.getName().equals("(selection)")){ + if(selectedDevice != null && !weightInput.value().equals(selectedGroup.getWeight(selectedDevice))){ + selectedGroup.setWeight(selectedDevice, Math.round(weightInput.value())); + } + } + + setVirtualHeight(30 + Math.max(groups.size(), selectedDevices.size()) * 14 + 16); + } + + private boolean isClicked(int offsetX, int offsetY, int width){ + return + offsetX < mouseX() && + mouseX() < + offsetX + width && + mouseY() > 27 && + mouseY() < height-16 && + hasFocus() && + offsetY - getScroll() < mouseY() && + mouseY() < offsetY + 14 - getScroll() && + MaShine.inputs.getState("mouse.left.press"); + } + + private void cloneGroup(){cloneGroup(groupNameInput.value());} + private void cloneGroup(String name){ + DeviceGroup grp = new DeviceGroup(name, selectedGroup); + selectedGroup = grp; + MaShine.scene.addGroup(grp); + } + private void deleteGroup(){ + MaShine.scene.deleteGroup(selectedGroup); + setSelectedGroup(null); + } + + private void removeDevice(){ + selectedGroup.removeDevice(selectedDevice); + setSelectedDevice(selectedGroup.getFirst()); + } + private void reverseWeights(){ + if(null != selectedGroup || !selectedGroup.getName().equals("(selection)")){ + selectedGroup.reverseWeights(); + } + } + + private void selectAll(){MaShine.ui.setSelectedDevices(MaShine.scene.getDevices());} + private void selectClear(){MaShine.ui.clearSelectedDevices();} + private void selectUniverse(){ + ArrayList devices = MaShine.scene.getDevices(); + ArrayList newSelection = new ArrayList(); + int univ = Math.round(univSelectInput.value()); + + for(Device d : devices){ + if(d.getUniverse() == univ){ + newSelection.add(d); + } + } + MaShine.ui.setSelectedDevices(newSelection); + } + + private void setGroupAsSelection(){ + if(null != selectedGroup || !selectedGroup.getName().equals("(selection)")){ + MaShine.ui.setSelectedDevices(new ArrayList(selectedGroup.getDevices().keySet())); + } + } + + private void addCurrent(){ + if(null != selectedGroup || !selectedGroup.getName().equals("(selection)")){ + ArrayList devices = MaShine.ui.getSelectedDevices(); + for(Device d : devices){ + selectedGroup.putDevice(d); + } + } + } + private void removeCurrent(){ + if(null != selectedGroup || !selectedGroup.getName().equals("(selection)")){ + ArrayList devices = MaShine.ui.getSelectedDevices(); + for(Device d : devices){ + selectedGroup.removeDevice(d); + } + } + } + + private void setSelectedDevice(Device d){ + selectedDevice = d; + if(null != d && null != selectedGroup && selectedGroup.isIn(d)){ + weightInput.setValue((float) selectedGroup.getWeight(d)); + } + } + + public DeviceGroup getSelectedGroup(){ + if(null != selectedGroup && selectedGroup.getName().equals("(selection)")){ + return null; + } + return selectedGroup; + } + + public void setSelectedGroup(DeviceGroup g){ + if(g != null && g != selectedGroup){ + selectedGroup = g; + if(g.getName().equals("(selection)")){ + groupNameInput.setValue("new group"); + }else{ + groupNameInput.setValue(g.getName()); + } + if(!g.isIn(selectedDevice)){ + if(!g.isEmpty()){ + setSelectedDevice(g.getFirst()); + }else{ + setSelectedDevice(null); + } + }else{ + // update weight input + setSelectedDevice(selectedDevice); + } + } + } + +} \ No newline at end of file diff --git a/src/mashine/ui/boxes/EngineView.java b/src/mashine/ui/boxes/EngineView.java new file mode 100644 index 0000000..fcbe4f4 --- /dev/null +++ b/src/mashine/ui/boxes/EngineView.java @@ -0,0 +1,115 @@ +/** + * Interface view the state of the engine + * + * @author procsynth - Antoine Pintout + * @since 27-03-2016` + */ + +package mashine.ui.boxes; + +import java.util.Collections; +import java.util.List; +import java.util.ArrayList; + +import mashine.MaShine; +import mashine.engine.Track; +import mashine.engine.Filter; +import mashine.ui.Colors; +import mashine.ui.Drawable; +import mashine.ui.FlatColor; + +public class EngineView extends Drawable { + + + public EngineView(){ + super(10, 50, MaShine.m.displayWidth - 10, MaShine.m.displayHeight - 50); + } + + public void drawContent(){ + canvas.noStroke(); + canvas.clear(); + + int offset = 10; + + for(Track t : MaShine.engine.getTracks()){ + int diff = 32 + (int) Math.ceil((t.sequencer.getSequence().getSize()-1)/24)*17; + + String filtersList = " "; + boolean visible = true; + + for(Filter f : t.getFilters()){ + if(f.isEnabled()){ + filtersList += f.getType() + " "; + if(f.getType().equals("drop_all")) visible = false; + } + } + + if(t.sequencer.isTweaked()){ + FlatColor.fill(canvas, Colors.MATERIAL.TEAL.A700); + }else if(t.sequencer.isManual()){ + FlatColor.fill(canvas, Colors.MATERIAL.ORANGE.A700); + }else if(!visible){ + FlatColor.fill(canvas, Colors.MATERIAL.GREY._400); + }else{ + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._700); + } + canvas.rect(MaShine.m.width - 426, offset - 2, 426, diff); + + for(int i = 0; i < t.sequencer.getSequence().getSize(); i++){ + if(t.sequencer.getIndex() == i){ + FlatColor.fill(canvas, Colors.MATERIAL.ORANGE.A400); + }else if(i < t.sequencer.getOffset()){ + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._300); + }else if(i < t.sequencer.getClip() + t.sequencer.getOffset()){ + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._800); + }else{ + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._500); + } + canvas.rect(MaShine.m.width - 424 + (i%24) * 17, offset + (float) Math.ceil(i/24)*17, 15, 15); + } + + offset += diff; + + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._200); + canvas.text(t.sequencer.getSequence().getName(),MaShine.m.width - 424, offset + 9); + if(t.sequencer.isTweaked() || !visible){ + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._900); + } + + canvas.text( + t.sequencer.getName()+" @"+(t.sequencer.getIndex()+1) +" >"+ t.sequencer.getOffset()+" %"+t.sequencer.getClip() + filtersList, + MaShine.m.width - 424, offset - 6 + ); + offset += 20; + + } + + offset += 20; + + int diff = MaShine.engine.getFilters().size()*20; + + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._700); + canvas.rect(MaShine.m.width - 426, offset - 2, 426, diff); + + List filters = new ArrayList(MaShine.engine.getFilters()); + Collections.reverse(filters); + + for(Filter f : filters){ + + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._300); + if(f.isEnabled()){ + FlatColor.fill(canvas, Colors.MATERIAL.CYAN._700); + canvas.rect(MaShine.m.width - 424, offset, 100, 15); + if(!f.getGroup().getName().equals("")){ + FlatColor.fill(canvas, Colors.MATERIAL.CYAN._600); + canvas.rect(MaShine.m.width - 424+100, offset, 150, 15); + } + FlatColor.fill(canvas, Colors.WHITE); + } + canvas.text(f.getType(), MaShine.m.width - 424 + 3, offset + 12); + canvas.text(f.getGroup().getName(), MaShine.m.width - 424 + 103, offset + 12); + offset += 18; + } + } + +} \ No newline at end of file diff --git a/src/mashine/ui/EventViewer.java b/src/mashine/ui/boxes/EventViewer.java similarity index 58% rename from src/mashine/ui/EventViewer.java rename to src/mashine/ui/boxes/EventViewer.java index 240a733..b873594 100644 --- a/src/mashine/ui/EventViewer.java +++ b/src/mashine/ui/boxes/EventViewer.java @@ -5,29 +5,36 @@ * @since 13-02-2016` */ -package mashine.ui; +package mashine.ui.boxes; -import mashine.*; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +import mashine.MaShine; +import mashine.ui.Colors; +import mashine.ui.FlatColor; +import mashine.ui.UIBox; +import processing.core.PConstants; public class EventViewer extends UIBox{ private HashMap highlight; - public EventViewer (MaShine m) { - super(m, "EVENT VIEWER", 50, 50, 300, 600); - highlight = new HashMap(); + public EventViewer () { + super("EVENT VIEWER", 50, 50, 200, 400); + highlight = new HashMap(); } public void drawUI(){ canvas.noStroke(); int offset = 30; - canvas.textAlign(canvas.LEFT, canvas.TOP); - ArrayList stateInputsName = new ArrayList(M.inputs.getStateSet()); + canvas.textAlign(PConstants.LEFT, PConstants.TOP); + ArrayList stateInputsName = new ArrayList(MaShine.inputs.getStateInputSet()); Collections.sort(stateInputsName); for(String a : stateInputsName){ - if(M.inputs.getState(a)){ + if(MaShine.inputs.getState(a)){ highlight.put(a, 255); } @@ -43,14 +50,16 @@ public void drawUI(){ offset += 14; } + setVirtualHeight(offset); + for(String h : highlight.keySet()){ Integer newVal; if(h.contains("encod") || h.contains("beat") ){ - newVal = highlight.get(h) - 35; + newVal = highlight.get(h) - 45; }else{ newVal = highlight.get(h) - 10; } - newVal = M.max(0, newVal); + newVal = Math.max(0, newVal); highlight.put(h, newVal); } diff --git a/src/mashine/ui/boxes/FilterSelector.java b/src/mashine/ui/boxes/FilterSelector.java new file mode 100644 index 0000000..38c697c --- /dev/null +++ b/src/mashine/ui/boxes/FilterSelector.java @@ -0,0 +1,295 @@ +/** + * Interface to select and apply filters; + * + * @author procsynth - Antoine Pintout + * @since 18-03-2016` + */ + +package mashine.ui.boxes; + +import java.util.Collections; +import java.util.Map; +import java.util.List; +import java.util.ArrayList; + +import mashine.Do; +import mashine.MaShine; +import mashine.engine.Track; +import mashine.engine.Filter; +import mashine.scene.DeviceGroup; +import mashine.ui.Colors; +import mashine.ui.FlatColor; +import mashine.ui.UIBox; +import mashine.ui.elements.TextButton; +import processing.core.PConstants; + +public class FilterSelector extends UIBox{ + + private Filter selectedFilter; + private Filter selectedModel; + + private DeviceGroup selectedGroup; + + public FilterSelector () { + super("FILTERS", 400, 600, 500, 230); + + + MaShine.inputs.registerAction("ui.filt_select.next", new Do(){public void x(){nextFilter();}}); + MaShine.inputs.registerAction("ui.filt_select.prev", new Do(){public void x(){prevFilter();}}); + MaShine.inputs.registerAction("ui.filt_select.toggle", new Do(){public void x(){toggleFilter();}}); + + elements.add(new TextButton(this, "add >", 90, height-15, 55, + new Do(){public void x(){addFilter();}} + )); + + elements.add(new TextButton(this, "up", 155, height-15, 30, + new Do(){public void x(){moveUpFilter();}} + )); + elements.add(new TextButton(this, "down", 187, height-15, 40, + new Do(){public void x(){moveDownFilter();}} + )); + elements.add(new TextButton(this, "delete", 232, height-15, 55, + new Do(){public void x(){removeFilter();}} + )); + elements.add(new TextButton(this, "toggle", width - 74, height-66, 75, + new Do(){public void x(){toggleFilter();}} + )); + + elements.add(new TextButton(this, "set group", width - 74, height-49, 75, + new Do(){public void x(){ + MaShine.ui.open("DeviceSelector"); + if(selectedFilter != null){ + selectedGroup = selectedFilter.getGroup(); + }else{ + selectedGroup = null; + } + MaShine.ui.setSelectedGroup(selectedGroup); + }} + )); + + elements.add(new TextButton(this, "bind", width - 74, height-32, 75, + new Do(){public void x(){ + MaShine.ui.open("Linker"); + if(selectedFilter != null){ + MaShine.ui.linker.setFilterValue(selectedFilter.getName()); + }else{ + MaShine.ui.linker.setFilterValue("filter"); + } + }} + )); + } + + public void tick(){ + if(MaShine.ui.getSelectedGroup() != selectedGroup){ + selectedGroup = MaShine.ui.getSelectedGroup(); + if(selectedFilter != null){ + selectedFilter.setGroup(selectedGroup); + } + } + } + + public void drawFixedUI(){ + canvas.noStroke(); + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._900); + canvas.rect(1, height - 16, width -1, 17); + FlatColor.stroke(canvas, Colors.MATERIAL.BLUE_GREY._900); + canvas.line(149, 20, 149, height-1); + canvas.line(349, 20, 349, height-1); + canvas.noStroke(); + + FlatColor.fill(canvas, Colors.WHITE); + + canvas.text(getListName(), 295, height-13); + + if(selectedFilter != null){ + + int offset = 28; + + canvas.text(selectedFilter.getType() + " (" + (selectedFilter.isEnabled() ? "enabled" : "disabled") + ")", 355, offset); + canvas.text("group: " + (selectedFilter.getGroup() != null ? selectedFilter.getGroup().getName() : "(unset)"), 355, offset+14); + + offset += 34; + + canvas.textAlign(PConstants.RIGHT, PConstants.TOP); + Map params = selectedFilter.getParameters(); + for(String p : params.keySet()){ + String t = ""; + short ti = params.get(p); + if(ti != Filter.FRAME && ti != Filter.LONG){ + if(ti == Filter.RANGE) t = "RANGE"; + if(ti == Filter.STATE) t = "STATE"; + canvas.text(p+" : "+ t, width - 5, offset); + offset += 14; + } + } + }else{ + canvas.text("no filter selected", 355, 28); + } + } + + public void drawUI(){ + canvas.noStroke(); + int offset = 30; + canvas.textAlign(PConstants.LEFT, PConstants.TOP); + + List filterModels = new ArrayList(MaShine.bank.getFilters().values()); + setSelectedModel(drawFilterList(filterModels, selectedModel, 0, offset, 148)); + + List filterList = getFilterList(); + setSelectedFilter(drawFilterList(filterList, selectedFilter, 149, offset, 200)); + setVirtualHeight(45 + Math.max(filterModels.size(), filterList.size()) * 14); + } + + public void setSelectedModel(Filter model){selectedModel = model;} + public void setSelectedFilter(Filter filter){ + selectedFilter = filter; + } + + private boolean isClicked(int offsetX, int offsetY, int width){ + return + offsetX < mouseX() && + mouseX() < + offsetX + width && + mouseY() > 27 && + mouseY() < height-16 && + hasFocus() && + offsetY - 3 - getScroll() < mouseY() && + mouseY() < offsetY + 11 - getScroll() && + MaShine.inputs.getState("mouse.left.press"); + } + + private Filter drawFilterList(List filters, Filter selected, int offsetX, int offsetY, int width){ + int index = 0; + Filter newSelected = selected; + Collections.reverse(filters); + for(Filter m : filters){ + if(m == selected){ + FlatColor.fill(canvas,Colors.MATERIAL.ORANGE.A400); + }else if(m.isEnabled()){ + if(index % 2 == 0){ + FlatColor.fill(canvas,Colors.MATERIAL.CYAN._700); + }else{ + FlatColor.fill(canvas,Colors.MATERIAL.CYAN._600); + } + }else{ + if(index % 2 == 0){ + FlatColor.fill(canvas,Colors.MATERIAL.BLUE_GREY._600); + }else{ + FlatColor.fill(canvas,Colors.MATERIAL.BLUE_GREY._500); + } + } + canvas.rect(offsetX+1, offsetY - 3, width, 14); + + if(isClicked(offsetX, offsetY, width)){ + newSelected = m; + } + + FlatColor.fill(canvas,Colors.WHITE); + canvas.text(m.getName(), offsetX+5, offsetY); + offsetY += 14; + index ++; + } + + Collections.reverse(filters); + return newSelected; + } + + private void addFilter(){ + if(selectedModel != null){ + ArrayList tracks = MaShine.engine.getTracks(); + for(Track t : tracks){ + if(t.isTweaked()){ + t.addFilter(selectedModel.getType()); + return; + } + } + MaShine.engine.addFilter(selectedModel.getType()); + } + return; + } + + private void nextFilter(){ + List filters = getFilterList(); + if(filters.size() == 0){ + setSelectedFilter(null); + }else{ + int i = 0; + if(selectedFilter != null){ + i = filters.indexOf(selectedFilter); + i ++; + if(i >= filters.size()){ + i = 0; + } + }else{ + i = 0; + } + setSelectedFilter(filters.get(i)); + } + } + + private void prevFilter(){ + List filters = getFilterList(); + if(filters.size() == 0){ + setSelectedFilter(null); + }else{ + int i = 0; + if(selectedFilter != null){ + i = filters.indexOf(selectedFilter); + i --; + if(i <= -1){ + i = filters.size() - 1; + } + }else{ + i = filters.size() - 1; + } + setSelectedFilter(filters.get(i)); + } + } + + private void toggleFilter(){ + if(null != selectedFilter){ + selectedFilter.toggle(); + } + } + + private void moveDownFilter(){ + List filters = getFilterList(); + int i = filters.indexOf(selectedFilter); + if(i != -1 && i != 0) + Collections.swap(filters, i, i - 1); + } + private void moveUpFilter(){ + List filters = getFilterList(); + int i = filters.indexOf(selectedFilter); + if(i != -1 && i != filters.size() -1) + Collections.swap(filters, i, i + 1); + } + + private void removeFilter(){ + List fl = getFilterList(); + fl.remove(selectedFilter); + if(fl.size() != 0){ + selectedFilter = fl.get(fl.size()-1); + }else{ + selectedFilter = null; + } + } + + private List getFilterList(){ + ArrayList tracks = MaShine.engine.getTracks(); + for(Track t : tracks){ + if(t.isTweaked()){ + return t.getFilters(); + } + } + return MaShine.engine.getFilters(); + } + private String getListName(){ + ArrayList tracks = MaShine.engine.getTracks(); + for(Track t : tracks){ + if(t.isTweaked()){ + return "track."+t.getName(); + } + } + return "mixer"; + } +} \ No newline at end of file diff --git a/src/mashine/ui/boxes/FrameViewer.java b/src/mashine/ui/boxes/FrameViewer.java new file mode 100644 index 0000000..1a76c82 --- /dev/null +++ b/src/mashine/ui/boxes/FrameViewer.java @@ -0,0 +1,57 @@ +/** + * Interface to view frame properties + * + * @author procsynth - Antoine Pintout + * @since 13-02-2016` + */ + +package mashine.ui.boxes; + +import mashine.ui.UIBox; + +public class FrameViewer extends UIBox{ + + public FrameViewer () { + super("FRAME VIEWER", 50, 50, 300, 600); + } + + public void drawUI(){ + // canvas.noStroke(); + // int offset = 30; + // canvas.textAlign(canvas.LEFT, canvas.TOP); + // ArrayList stateInputsName = new ArrayList(M.inputs.getStateSet()); + // Collections.sort(stateInputsName); + + // for(String a : stateInputsName){ + // if(M.inputs.getState(a)){ + // highlight.put(a, 255); + // } + + // if(!highlight.containsKey(a)){ + // highlight.put(a, 0); + // } + + // FlatColor.fill(canvas,Colors.MATERIAL.ORANGE.A400.withAlpha(highlight.get(a))); + // canvas.rect(1, offset - 3, width - 1, 14); + + // FlatColor.fill(canvas,Colors.WHITE); + // canvas.text(a, 5, offset); + // offset += 14; + // } + + // for(String h : highlight.keySet()){ + // Integer newVal; + // if(h.contains("encod") || h.contains("beat") ){ + // newVal = highlight.get(h) - 35; + // }else{ + // newVal = highlight.get(h) - 10; + // } + // newVal = M.max(0, newVal); + // highlight.put(h, newVal); + //} + + + //canvas.rect(mouseX(), mouseY(), 20, 20); + } + +} \ No newline at end of file diff --git a/src/mashine/ui/boxes/Linker.java b/src/mashine/ui/boxes/Linker.java new file mode 100644 index 0000000..5b01af9 --- /dev/null +++ b/src/mashine/ui/boxes/Linker.java @@ -0,0 +1,296 @@ +/** + * Interface to link actions to inputs + * + * @author procsynth - Antoine Pintout + * @since 30-03-2016` + */ + +package mashine.ui.boxes; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import processing.core.PConstants; + +import mashine.Do; +import mashine.MaShine; +import mashine.ui.Colors; +import mashine.ui.FlatColor; +import mashine.ui.UIBox; +import mashine.ui.elements.TextButton; +import mashine.ui.elements.TextInput; + +public class Linker extends UIBox{ + + private TextInput filterInput; + private TextInput linkInput; + private String learnedAction = ""; + private String learnedState = ""; + private String learnedRange = ""; + private String selectedAction = ""; + private String selectedState = ""; + private String selectedRange = ""; + private boolean learningAll = false; + + public Linker () { + super("LINKS", 50, 50, 300, 500, 1500); + + filterInput = new TextInput(this, "", 0, 55, 100, "\\."); + linkInput = new TextInput(this, "", 0, 28, 189, "\\."); + elements.add(filterInput); + elements.add(linkInput); + elements.add(new TextButton(this, "learn", width-54, 45, 55, + new Do(){public void x(){learn();}} + )); + elements.add(new TextButton(this, "learn all", width-141, 45, 85, + new Do(){public void x(){learnAll();}} + )); + elements.add(new TextButton(this, "unlink", width-54, 28, 55, + new Do(){public void x(){unlink();}} + )); + elements.add(new TextButton(this, "link", width-111, 28, 55, + new Do(){public void x(){link();}} + )); + } + + public void drawFixedUI(){ + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._800); + canvas.rect(1, 22, width-1, 45); + } + + public void learn(){ + learnedAction = selectedAction; + learnedState = selectedState; + learnedRange = selectedRange; + linkInput.setValue("LEARNING..."); + learningAll = false; + } + public void learnAll(){ + if(!filterInput.value().equals("")){ + learnedAction = ""; + learnedState = ""; + learnedRange = ""; + learningAll = true; + linkInput.setValue("LEARNING ALL..."); + } + } + public void unlink(){ + MaShine.inputs.unlink(selectedState); + MaShine.inputs.unrange(selectedRange); + MaShine.inputs.unstate(selectedState); + } + + public void link(){ + if(!isLearning()){ + if(selectedAction != ""){ + if(null != linkInput.value()){ + MaShine.inputs.link(selectedAction, linkInput.value()); + }else{ + MaShine.inputs.unlink(selectedAction); + } + }else if(selectedRange != ""){ + if(null != linkInput.value()){ + MaShine.inputs.range(selectedRange, linkInput.value()); + }else{ + MaShine.inputs.unrange(selectedRange); + } + }else if(selectedState != ""){ + if(null != linkInput.value()){ + MaShine.inputs.state(selectedState, linkInput.value()); + }else{ + MaShine.inputs.unstate(selectedState); + } + } + + } + } + + public void drawUI(){ + ArrayList actionSet = new ArrayList(MaShine.inputs.getActionSet()); + ArrayList stateSet = new ArrayList(MaShine.inputs.getStateSet()); + ArrayList rangeSet = new ArrayList(MaShine.inputs.getRangeSet()); + + if(learnedAction != ""){ + if(null != MaShine.inputs.getLastState()){ + MaShine.inputs.link(learnedAction, MaShine.inputs.getLastState()); + learnedAction = ""; + linkInput.setValue(MaShine.inputs.getLastState()); + } + }else if(learnedState != ""){ + if(null != MaShine.inputs.getLastState()){ + MaShine.inputs.state(learnedState, MaShine.inputs.getLastState()); + learnedState = ""; + linkInput.setValue(MaShine.inputs.getLastState()); + } + }else if(learnedRange != ""){ + if(null != MaShine.inputs.getLastRange()){ + MaShine.inputs.range(learnedRange, MaShine.inputs.getLastRange()); + learnedRange = ""; + linkInput.setValue(MaShine.inputs.getLastRange()); + } + }else if(learningAll){ + if(null != MaShine.inputs.getLastState()){ + for(String a : actionSet){ + if(a.contains(filterInput.value())){ + MaShine.inputs.link(a, MaShine.inputs.getLastState()); + } + } + for(String a : stateSet){ + if(a.contains(filterInput.value())){ + MaShine.inputs.state(a, MaShine.inputs.getLastState()); + } + } + linkInput.setValue(MaShine.inputs.getLastState()); + learningAll = false; + }else if(null != MaShine.inputs.getLastRange()){ + for(String a : rangeSet){ + if(a.contains(filterInput.value())){ + MaShine.inputs.range(a, MaShine.inputs.getLastState()); + } + } + linkInput.setValue(MaShine.inputs.getLastRange()); + learningAll = false; + } + } + + canvas.noStroke(); + canvas.textAlign(PConstants.LEFT, PConstants.TOP); + + HashMap actionLinks = MaShine.inputs.getActionLinks(); + HashMap stateLinks = MaShine.inputs.getStateLinks(); + HashMap rangeLinks = MaShine.inputs.getRangeLinks(); + + Collections.sort(actionSet); + Collections.sort(stateSet); + Collections.sort(rangeSet); + + int offset = 75; + int index = 0; + + for(String a : actionSet){ + if(a.contains(filterInput.value())){ + drawBackground(a, actionLinks, selectedAction, index, offset, + Colors.MATERIAL.CYAN._700, + Colors.MATERIAL.CYAN._600, + Colors.MATERIAL.BLUE_GREY._600, + Colors.MATERIAL.BLUE_GREY._500 + ); + FlatColor.fill(canvas,Colors.WHITE); + canvas.text(a, 5, offset); + + if(isClicked(offset)){ + if(actionLinks.containsKey(a)){ + linkInput.setValue(actionLinks.get(a)); + }else{ + linkInput.setValue(""); + } + selectAction(a); + } + offset += 14; + index ++; + } + } + + for(String a : stateSet){ + if(a.contains(filterInput.value())){ + drawBackground(a, stateLinks, selectedState, index, offset, + Colors.MATERIAL.CYAN._700, + Colors.MATERIAL.CYAN._600, + Colors.MATERIAL.BLUE_GREY._600, + Colors.MATERIAL.BLUE_GREY._500 + ); + FlatColor.fill(canvas,Colors.WHITE); + canvas.text(a, 5, offset); + + if(isClicked(offset)){ + if(stateLinks.containsKey(a)){ + linkInput.setValue(stateLinks.get(a)); + }else{ + linkInput.setValue(""); + } + selectState(a); + } + offset += 14; + index ++; + } + } + + for(String a : rangeSet){ + if(a.contains(filterInput.value())){ + drawBackground(a, rangeLinks, selectedRange, index, offset, + Colors.MATERIAL.TEAL._600, + Colors.MATERIAL.TEAL._400, + Colors.MATERIAL.INDIGO._600, + Colors.MATERIAL.INDIGO._500 + ); + FlatColor.fill(canvas,Colors.WHITE); + canvas.text(a, 5, offset); + + if(isClicked(offset)){ + if(rangeLinks.containsKey(a)){ + linkInput.setValue(rangeLinks.get(a)); + }else{ + linkInput.setValue(""); + } + selectRange(a); + } + offset += 14; + index ++; + } + } + + setVirtualHeight(offset); + } + + private void drawBackground(String a, Map links, String selected, int index, int offset, FlatColor c1,FlatColor c2,FlatColor c3,FlatColor c4){ + if(a.equals(selected)){ + FlatColor.fill(canvas,Colors.MATERIAL.ORANGE.A400); + }else if(links.containsKey(a)){ + if(index % 2 == 0){ + FlatColor.fill(canvas,c1); + }else{ + FlatColor.fill(canvas,c2); + } + }else{ + if(index % 2 == 0){ + FlatColor.fill(canvas,c3); + }else{ + FlatColor.fill(canvas,c4); + } + } + + canvas.rect(1, offset - 3, width - 1, 14); + } + + private boolean isLearning(){ + return learningAll || !learnedAction.equals("") || !learnedState.equals("") || !learnedRange.equals(""); + } + + private void selectAction(String a){ + selectedAction = a; + selectedRange = selectedState = learnedAction = learnedRange = learnedState = ""; + } + private void selectRange(String a){ + selectedRange = a; + selectedAction = selectedState = learnedAction = learnedRange = learnedState = ""; + } + private void selectState(String a){ + selectedState = a; + selectedAction = selectedRange = learnedAction = learnedRange = learnedState = ""; + } + + private boolean isClicked(int offset){ + return mouseY() > 70 && + hasFocus() && + offset - 3 - getScroll() < mouseY() && + mouseY() < offset + 11 - getScroll() && + MaShine.inputs.getState("mouse.left.press"); + } + + public void setFilterValue(String v){ + filterInput.setValue(v); + } + +} \ No newline at end of file diff --git a/src/mashine/ui/boxes/Menu.java b/src/mashine/ui/boxes/Menu.java new file mode 100644 index 0000000..33f55a4 --- /dev/null +++ b/src/mashine/ui/boxes/Menu.java @@ -0,0 +1,108 @@ +/** + * Unused now... + * + * @author procsynth - Antoine Pintout + * @since 13-02-2016` + */ + +package mashine.ui.boxes; + +import java.util.ArrayList; + +import mashine.Do; +import mashine.MaShine; +import mashine.ui.Colors; +import mashine.ui.Drawable; +import mashine.ui.Element; +import mashine.ui.FlatColor; +import mashine.ui.elements.TextButton; +import processing.core.PImage; + +public class Menu extends Drawable{ + + private ArrayList bigButtons; + private PImage mashineImage; + + + public Menu(){ + super(0, 0, MaShine.m.displayWidth, 40); + bigButtons = new ArrayList(); + new ArrayList(); + focus = true; + + Do switchToScene = new Do(){public void x(){MaShine.ui.open("DeviceEditor");MaShine.ui.open("DeviceSelector");}}; + Do switchToAnimate = new Do(){public void x(){MaShine.ui.open("DeviceSelector");MaShine.ui.open("SequenceEditor");MaShine.ui.open("SequenceSelector");MaShine.ui.open("ColorPalette");}}; + Do switchToLive = new Do(){public void x(){MaShine.ui.open("FilterSelector");}}; + Do openDataViewer = new Do(){public void x(){MaShine.ui.open("DataViewer");}}; + Do openEventViewer = new Do(){public void x(){MaShine.ui.open("EventViewer");}}; + Do openLinker = new Do(){public void x(){MaShine.ui.open("Linker");}}; + Do openControlSurface = new Do(){public void x(){MaShine.ui.open("ControlSurface");}}; + + Do save = new Do(){public void x(){MaShine.inputs.getAction("mashine.save").x();}}; + Do saveAs = new Do(){public void x(){MaShine.inputs.getAction("mashine.save_as").x();}}; + Do open = new Do(){public void x(){MaShine.inputs.getAction("mashine.open").x();}}; + Do restore = new Do(){public void x(){MaShine.inputs.getAction("mashine.restore").x();}}; + + bigButtons.add(new TextButton(this, 115, 0, 120, 32, "PATCH", + Colors.WHITE, 60, + Colors.MATERIAL.CYAN._600, + Colors.MATERIAL.CYAN._700, + switchToScene)); + bigButtons.add(new TextButton(this, 240, 0, 120, 32, "ANIMATE", + Colors.WHITE, 60, + Colors.MATERIAL.DEEP_PURPLE._600, + Colors.MATERIAL.DEEP_PURPLE._700, + switchToAnimate)); + bigButtons.add(new TextButton(this, 365, 0, 120, 32, "LIVE", + Colors.WHITE, 60, + Colors.MATERIAL.RED._600, + Colors.MATERIAL.RED._700, + switchToLive)); + bigButtons.add(new TextButton(this, 502, 0, 70, 15, "event in", + Colors.WHITE, 10, + Colors.MATERIAL.TEAL._600, + Colors.MATERIAL.TEAL._800, + openEventViewer)); + bigButtons.add(new TextButton(this, 502, 17, 70, 15, "data in", + Colors.WHITE, 10, + Colors.MATERIAL.TEAL._600, + Colors.MATERIAL.TEAL._800, + openDataViewer)); + bigButtons.add(new TextButton(this, 575, 0, 70, 15, "links", + Colors.WHITE, 10, + Colors.MATERIAL.TEAL._600, + Colors.MATERIAL.TEAL._800, + openLinker)); + bigButtons.add(new TextButton(this, 648, 0, 70, 15, "console", + Colors.WHITE, 10, + Colors.MATERIAL.ORANGE._600, + Colors.MATERIAL.ORANGE._800, + openControlSurface)); + + bigButtons.add(new TextButton(this, 750, 0, 70, 15, "save", + Colors.WHITE, 10,Colors.MATERIAL.YELLOW._600,Colors.MATERIAL.YELLOW._800, + save)); + bigButtons.add(new TextButton(this, 750, 17, 70, 15, "save as", + Colors.WHITE, 10,Colors.MATERIAL.YELLOW._600,Colors.MATERIAL.YELLOW._800, + saveAs)); + + bigButtons.add(new TextButton(this, 822, 0, 70, 15, "open", + Colors.WHITE, 10,Colors.MATERIAL.YELLOW._600,Colors.MATERIAL.YELLOW._800, + open)); + bigButtons.add(new TextButton(this, 822, 17, 70, 15, "restore", + Colors.WHITE, 10,Colors.MATERIAL.YELLOW._600,Colors.MATERIAL.YELLOW._800, + restore)); + mashineImage = MaShine.m.loadImage("data/mashine.png"); + } + + public void drawContent(){ + canvas.noStroke(); + FlatColor.background(canvas, Colors.MATERIAL.BLUE_GREY._900); + canvas.image(mashineImage, 5, 8); + + for(Element bb : bigButtons){ + bb.draw(); + } + } + +} \ No newline at end of file diff --git a/src/mashine/ui/boxes/SceneView.java b/src/mashine/ui/boxes/SceneView.java new file mode 100644 index 0000000..2272c01 --- /dev/null +++ b/src/mashine/ui/boxes/SceneView.java @@ -0,0 +1,165 @@ +/** + * Instance to visualize devices of the scene + * + * @author procsynth - Antoine Pintout + * @since 13-02-2016` + */ + +package mashine.ui.boxes; + +import mashine.*; +import mashine.ui.*; +import mashine.ui.elements.*; +import mashine.scene.*; +import mashine.scene.features.*; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Iterator; +import java.util.ArrayList; + +import processing.core.PConstants; + +public class SceneView extends Drawable { + + LinkedHashMap deviceElements; + ArrayList selectedDevices; + Frame displayFrame; + + boolean dragging = false; + int dragStartX = 0; + int dragStartY = 0; + + public SceneView(){ + super(0, 40, MaShine.m.displayWidth, MaShine.m.displayHeight - 40); + deviceElements = new LinkedHashMap(); + selectedDevices = new ArrayList(); + displayFrame = new Frame(); + } + + public void drawContent(){ + + canvas.clear(); + + Frame frame = displayFrame; + + ArrayList devices = MaShine.scene.getDevices(); + HashMap frameFeatures = frame.getFeatures(); + + boolean selectionMade = false; + + for(Device d : devices){ + // add new DeviceElement if not already present + if(!deviceElements.containsKey(d)){ + deviceElements.put(d, new DeviceElement(this, d)); + } + + DeviceElement de = deviceElements.get(d); + + // selected it maybe + boolean selected = selectedDevices.contains(d); + + if(MaShine.inputs.getState("keyboard.16.hold") && de.isClicked()){ + if(selected){ + selectedDevices.remove(d); + selected = false; + }else{ + selectedDevices.add(d); + selected = true; + } + selectionMade = true; + }else if(de.isClicked()){ + selectedDevices.clear(); + selected = true; + selectionMade = true; + selectedDevices.add(d); + } + + // draw it + de.setSelected(selected); + de.setFrameFeatures(frameFeatures); + de.draw(); + } + + if(hasFocus() && !selectionMade && MaShine.inputs.getState("mouse.left.press") && !dragging){ + selectedDevices.clear(); + } + + if(hasFocus() && !selectionMade && MaShine.inputs.getState("mouse.left.drag") && !dragging){ + dragging = true; + dragStartX = mouseX(); + dragStartY = mouseY(); + } + + if(!MaShine.inputs.getState("mouse.left.drag") && dragging && hasFocus()){ + dragging = false; + + if(!MaShine.inputs.getState("keyboard.16.hold")){ + selectedDevices.clear(); + } + + for(Device d : devices){ + int dx = d.getX(); + int dy = d.getY(); + int dw = d.getWidth(); + int dh = d.getHeight(); + + if( + Math.min(mouseX(), dragStartX) < dx && dx < Math.max(mouseX(), dragStartX) && + Math.min(mouseY(), dragStartY) < dy && dy < Math.max(mouseY(), dragStartY) || + Math.min(mouseX(), dragStartX) < (dx + dw) && (dx + dw) < Math.max(mouseX(), dragStartX) && + Math.min(mouseY(), dragStartY) < (dy + dh) && (dy + dh) < Math.max(mouseY(), dragStartY) || + Math.min(mouseX(), dragStartX) < dx && dx < Math.max(mouseX(), dragStartX) && + Math.min(mouseY(), dragStartY) < (dy + dh) && (dy + dh) < Math.max(mouseY(), dragStartY) || + Math.min(mouseX(), dragStartX) < (dx + dw) && (dx + dw) < Math.max(mouseX(), dragStartX) && + Math.min(mouseY(), dragStartY) < dy && dy < Math.max(mouseY(), dragStartY) + ){ + //d.setSelected(true); + selectedDevices.add(d); + }else{ + if(!MaShine.inputs.getState("keyboard.16.hold")){ + //d.setSelected(false); + } + } + } + } + + if(dragging){ + canvas.rectMode(PConstants.CORNERS); + FlatColor.fill(canvas, Colors.MATERIAL.GREEN.A200.withAlpha(20)); + FlatColor.stroke(canvas, Colors.MATERIAL.CYAN.A200); + canvas.rect(dragStartX, dragStartY, mouseX(), mouseY()); + canvas.rectMode(PConstants.CORNER); + } + + // remove DeviceElement if Device not here anymore + for(Iterator> it = deviceElements.entrySet().iterator(); it.hasNext(); ) { + Map.Entry entry = it.next(); + if(!devices.contains(entry.getKey())) { + it.remove(); + } + } + + } + + public ArrayList getSelectedDevices(){ + return selectedDevices; + } + + public void setSelectedDevices(ArrayList newSelection){ + selectedDevices = newSelection; + } + + public void clearSelectedDevices(){ + selectedDevices.clear(); + } + + public void reloadElements(){ + deviceElements.clear(); + } + + public void setFrame(Frame f){ + displayFrame = f; + } + +} \ No newline at end of file diff --git a/src/mashine/ui/boxes/SequenceEditor.java b/src/mashine/ui/boxes/SequenceEditor.java new file mode 100644 index 0000000..d3eb546 --- /dev/null +++ b/src/mashine/ui/boxes/SequenceEditor.java @@ -0,0 +1,374 @@ +/** + * Interface to edit sequences of animation + * + * @author procsynth - Antoine Pintout + * @since 18-03-2016` + */ + +package mashine.ui.boxes; + +import mashine.*; +import mashine.ui.*; +import mashine.ui.elements.*; +import mashine.scene.*; +import mashine.scene.features.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.ArrayList; +import java.lang.Math; + +import processing.core.PConstants; + +public class SequenceEditor extends UIBox{ + + private HashMap ui; + private Sequence selectedSequence; + private FlatColor selectedColor; + private Frame currentFrame; + private int currentFrameIndex = 0; + private int lastFrameIndex = 0; + private String lastSelectedDeviceHash = ""; + private boolean sendFrameOnOutputs = false; + + private HashMap featureInputs; + private HashMap featureEnables; + private ArrayList featureElements; + + public SequenceEditor () { + super("ANIM EDITOR", 650, 50, 270, 450); + + selectedSequence = MaShine.bank.getSequence(0); + + MaShine.inputs.registerAction("ui.seq_edit.nextframe", new Do(){public void x(){nextFrame();}}); + MaShine.inputs.registerAction("ui.seq_edit.prevframe", new Do(){public void x(){prevFrame();}}); + + ui = new HashMap(); + + ui.put("seqName", new TextInput(this, selectedSequence.getName(), 0, 28, 120)); + + ui.put("deleteFrame", new TextButton(this, "delete", 216, 45, + new Do(){public void x(){deleteFrame();}} + )); + ui.put("prevFrame", new TextButton(this, "<-", 0, 45, + new Do(){public void x(){prevFrame();}} + )); + ui.put("nextFrame", new TextButton(this, "->", 57, 45, + new Do(){public void x(){nextFrame();}} + )); + ui.put("copyFrame", new TextButton(this, "copy", 114, 45, 40, + new Do(){public void x(){copyFrame();}} + )); + ui.put("newFrame", new TextButton(this, "+", 156, 45, 16, + new Do(){public void x(){newFrame();}} + )); + ui.put("indexInput", new RangeInput(this, 1f, 1f, 1f, 1f, 174, 45, 30)); + + ui.put("sendOnOutputs", new Checkbox(this, width - 14, height - 20, + new Do(){public void x(){sendFrameOnOutputs = false;}}, + new Do(){public void x(){sendFrameOnOutputs = true;}} + )); + + for (String el : ui.keySet()){ + elements.add(ui.get(el)); + } + } + + public void tick(){ + if(selectedSequence != MaShine.ui.getSelectedSequence()){ + selectedSequence = MaShine.ui.getSelectedSequence(); + ((TextInput) ui.get("seqName")).setValue(selectedSequence.getName()); + currentFrameIndex = selectedSequence.getSize()-1; + ((RangeInput)ui.get("indexInput")).setMax(currentFrameIndex +1); + ((RangeInput)ui.get("indexInput")).setValue(currentFrameIndex +1); + lastSelectedDeviceHash = ""; + }else{ + selectedSequence.setName(((TextInput) ui.get("seqName")).value()); + } + + if(currentFrameIndex != lastFrameIndex){ + lastFrameIndex = currentFrameIndex; + ((RangeInput) ui.get("indexInput")).setValue(currentFrameIndex + 1); + }else{ + currentFrameIndex = (int) Math.floor(((RangeInput) ui.get("indexInput")).value()) -1; + } + + currentFrame = selectedSequence.getFrame(currentFrameIndex); + MaShine.ui.setDisplayedFrame(new Frame(currentFrame)); + if(sendFrameOnOutputs){ + MaShine.outputs.setFrame(new Frame(currentFrame)); + } + + if(selectedColor != MaShine.ui.getSelectedColor()){ + selectedColor = MaShine.ui.getSelectedColor(); + linkColorToFeatureForCurrentFrame(selectedColor); + } + + if(!lastSelectedDeviceHash.equals(selectedDevicesHash(MaShine.ui.getSelectedDevices()))){ + + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + lastSelectedDeviceHash = selectedDevicesHash(selectedDevices); + ArrayList commonFeatures = Device.commonFeatures(selectedDevices); + //renew elements + + featureElements = new ArrayList(); + featureInputs = new HashMap(); + featureEnables = new HashMap(); + + int offset = 70; + + for(final Feature f : commonFeatures){ + + if(f instanceof EditableFeature){ + + + Checkbox c = new Checkbox(this, width - 20, offset, + new Do(){public void x(){enableFeatureForCurrentFrame((EditableFeature)f);}}, + new Do(){public void x(){disableFeatureForCurrentFrame((EditableFeature)f);}} + ); + boolean featureInFrame = true; + + for(Device d : selectedDevices){ + Feature df = currentFrame.getFeature(d,f); + if(df == null){ + featureInFrame = false; + } + } + + c.setState(featureInFrame); + featureEnables.put(f.getType(), c); + featureElements.add(c); + + if(f instanceof ColorFeature){ + offset += 17; + }else{ + for(String fi : f.getFields().keySet()){ + + RangeInput e = new RangeInput(this, f.getFields().get(fi), 5, offset, 40); + + if(!featureInFrame) + e.disable(); + + Feature firstFeature = currentFrame.getFeature(selectedDevices.get(0), f); + + if(firstFeature != null){ + + Integer commonFieldValue = firstFeature.getField(fi); + + e.setValue(commonFieldValue); + // TODO : get most significant field value from frame for selected devices + + for(Device d : selectedDevices){ + Feature devFeature = currentFrame.getFeature(d, f); + if(devFeature == null || commonFieldValue == null || commonFieldValue != devFeature.getField(fi)){ + e.setValue(null); + e.setStringValue("_"); + break; + } + } + }else{ + e.setValue(null); + e.setStringValue("_"); + } + + featureInputs.put(f.getType() +"."+ fi, e); + featureElements.add(e); + offset += 17; + } + } + + offset +=3; + } + } + }else{ + for(String ri : featureInputs.keySet()){ + if(featureInputs.get(ri).isEnabled()){ + if(featureInputs.get(ri).value() != null){ + updateFeatureFieldForCurrentFrame(ri, (int) Math.floor(featureInputs.get(ri).value())); + } + } + } + } + } + + public void drawUI(){ + + ArrayList commonFeatures = Device.commonFeatures(MaShine.ui.getSelectedDevices()); + int offset = 73; + int index = 0; + + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._700); + canvas.noStroke(); + canvas.rect(0, height - 20, width - 15, 15); + + FlatColor.fill(canvas, Colors.WHITE); + canvas.textAlign(PConstants.RIGHT, PConstants.TOP); + canvas.text("Send edited frame on outputs", width - 19, height - 16); + + if(selectedSequence.getSize() < 2){ + canvas.text(selectedSequence.getSize() +" frame", 205, 32); + }else{ + canvas.text(selectedSequence.getSize() +" frames", 205, 32); + } + + + // draw background + + FlatColor.fill(canvas, Colors.MATERIAL.BLUE_GREY._700); + + for(Feature f : commonFeatures){ + if(f instanceof EditableFeature){ + int diff = 0; + + if(f instanceof ColorFeature){ + diff = 20; + }else{ + diff = 17 * f.getFields().size() + 3; + } + if(index % 2 == 0){ + canvas.rect(1, offset - 6, width -1, diff + 1); + } + offset += diff; + index ++; + } + } + + offset = 73; + + canvas.textAlign(PConstants.LEFT, PConstants.TOP); + FlatColor.fill(canvas, Colors.WHITE); + for(Feature f : commonFeatures){ + if(f instanceof EditableFeature){ + + canvas.textAlign(PConstants.RIGHT, PConstants.TOP); + canvas.text(f.getType(), width - 25, offset); + + if(f instanceof ColorFeature){ + // canvas.textAlign(canvas.LEFT, canvas.TOP); + // canvas.text("unlinked", 5, offset ); + offset += 17; + }else{ + if(f.getFields().size() != 1){ + for(String fi : f.getFields().keySet()){ + canvas.textAlign(PConstants.LEFT, PConstants.TOP); + canvas.text(fi, 50, offset); + offset +=17; + } + }else{ + offset +=17; + } + } + + offset +=3; + } + } + // draw elements + for(Element el : featureElements){ + el.draw(); + if(el.mouseIn()){ + el.focus(); + }else{ + if(el.hasFocus()) + el.defocus(); + } + } + + } + + public void enableFeatureForCurrentFrame(EditableFeature feature){ + MaShine.println("Enabling "+ feature.getType()); + for(Device d : MaShine.ui.getSelectedDevices()){ + currentFrame.addFeature(d,feature); + } + + if(feature instanceof ColorFeature){ + linkColorToFeatureForCurrentFrame(selectedColor); + } + + for(String el : featureInputs.keySet()){ + MaShine.println(el); + if(el.split("\\.")[0].equals(feature.getType())) + featureInputs.get(el).enable(); + } + } + + public void disableFeatureForCurrentFrame(EditableFeature feature){ + MaShine.println("Disabling "+ feature.getType()); + for(Device d : MaShine.ui.getSelectedDevices()){ + currentFrame.removeFeature(d, feature); + } + + for(String el : featureInputs.keySet()){ + MaShine.println(el); + if(el.split("\\.")[0].equals(feature.getType())) + featureInputs.get(el).disable(); + } + } + + public void updateFeatureFieldForCurrentFrame(String featureField, int value){ + for(Device d : MaShine.ui.getSelectedDevices()){ + currentFrame.updateFeature(d, featureField.split("\\.")[0],featureField.split("\\.")[1], value); + } + } + + public void linkColorToFeatureForCurrentFrame(FlatColor color){ + Map frameFeatures = currentFrame.getFeatures(); + ArrayList selectedDevices = MaShine.ui.getSelectedDevices(); + for(String featureKey : frameFeatures.keySet()){ + String id = featureKey.split("\\.")[0]; + if(frameFeatures.get(featureKey) instanceof ColorFeature){ + for(Device d : selectedDevices){ + if(d.getIdentifier().equals(id)){ + ((ColorFeature) frameFeatures.get(featureKey)).link(color); + break; + } + } + } + } + } + + + public void prevFrame(){ + currentFrameIndex --; + if(currentFrameIndex < 0){ + currentFrameIndex = selectedSequence.getSize() + currentFrameIndex; + } + ((RangeInput)ui.get("indexInput")).setValue(currentFrameIndex +1); + lastSelectedDeviceHash = ""; + } + public void nextFrame(){ + currentFrameIndex ++; + currentFrameIndex = currentFrameIndex % selectedSequence.getSize(); + ((RangeInput)ui.get("indexInput")).setValue(currentFrameIndex +1); + lastSelectedDeviceHash = ""; + } + public void deleteFrame(){ + selectedSequence.deleteFrame(currentFrameIndex); + currentFrameIndex = selectedSequence.getSize()-1; + ((RangeInput)ui.get("indexInput")).setMax(currentFrameIndex +1); + ((RangeInput)ui.get("indexInput")).setValue(currentFrameIndex +1); + lastSelectedDeviceHash = ""; + } + public void newFrame(){ + selectedSequence.addFrame(new Frame()); + currentFrameIndex = selectedSequence.getSize()-1; + ((RangeInput)ui.get("indexInput")).setMax(currentFrameIndex +1); + ((RangeInput)ui.get("indexInput")).setValue(currentFrameIndex +1); + lastSelectedDeviceHash = ""; + } + public void copyFrame(){ + selectedSequence.addFrame(new Frame(currentFrame)); + currentFrameIndex = selectedSequence.getSize()-1; + ((RangeInput)ui.get("indexInput")).setMax(currentFrameIndex +1); + ((RangeInput)ui.get("indexInput")).setValue(currentFrameIndex +1); + lastSelectedDeviceHash = ""; + } + + private String selectedDevicesHash(ArrayList dev){ + String hash = "_" + dev.size(); + for(Device d : dev){ + hash += d.getIdentifier(); + } + return hash; + } +} \ No newline at end of file diff --git a/src/mashine/ui/boxes/SequenceSelector.java b/src/mashine/ui/boxes/SequenceSelector.java new file mode 100644 index 0000000..a50f38e --- /dev/null +++ b/src/mashine/ui/boxes/SequenceSelector.java @@ -0,0 +1,143 @@ +/** + * Interface to select sequences of frames + * + * @author procsynth - Antoine Pintout + * @since 18-03-2016` + */ + +package mashine.ui.boxes; + +import java.util.Collections; +import java.util.List; + +import mashine.Do; +import mashine.MaShine; +import mashine.scene.Sequence; +import mashine.engine.Track; +import mashine.ui.Colors; +import mashine.ui.FlatColor; +import mashine.ui.UIBox; +import mashine.ui.elements.TextButton; +import processing.core.PConstants; + +public class SequenceSelector extends UIBox{ + + private Sequence selectedSequence; + + public SequenceSelector () { + super("SEQUENCES", 950, 600, 150, 350); + + selectedSequence = MaShine.bank.getSequence(0); + + MaShine.inputs.registerAction("ui.seq_select.next", new Do(){public void x(){nextSequence();}}); + MaShine.inputs.registerAction("ui.seq_select.prev", new Do(){public void x(){prevSequence();}}); + + elements.add(new TextButton(this, "new", 0, 310, 55, + new Do(){public void x(){newSequence();}} + )); + + elements.add(new TextButton(this, "delete", 96, 330, 55, + new Do(){public void x(){deleteSequence();}} + )); + elements.add(new TextButton(this, "up", 0, 330, 30, + new Do(){public void x(){moveUpSequence();}} + )); + elements.add(new TextButton(this, "down", 32, 330, 40, + new Do(){public void x(){moveDownSequence();}} + )); + } + + public void tick(){ + } + + public void drawUI(){ + + canvas.noStroke(); + int offset = 30; + int index = 0; + canvas.textAlign(PConstants.LEFT, PConstants.TOP); + + if(MaShine.bank.getSequencesSize() != 0){ + List sequences = MaShine.bank.getSequences(); + + for(Sequence s : sequences){ + if(s == selectedSequence){ + FlatColor.fill(canvas,Colors.MATERIAL.ORANGE.A400); + }else{ + if(index % 2 == 0){ + FlatColor.fill(canvas,Colors.MATERIAL.BLUE_GREY._700); + }else{ + canvas.noFill(); + } + } + canvas.rect(1, offset - 3, width - 1, 14); + + if(hasFocus() && offset - 3 < mouseY() && mouseY() < offset + 11 && MaShine.inputs.getState("mouse.left.press")){ + setSelectedSequence(s); + } + + FlatColor.fill(canvas,Colors.WHITE); + canvas.text(s.getName() + " ("+s.getSize()+")", 5, offset); + offset += 14; + index ++; + } + }else{ + + FlatColor.fill(canvas,Colors.WHITE); + canvas.text("No sequences yet.", 5, offset); + + } + } + + private void newSequence(){ + selectedSequence = new Sequence("unamed sequence"); + MaShine.bank.addSequence(selectedSequence); + } + + private void deleteSequence(){ + MaShine.bank.deleteSequence(selectedSequence); + if(MaShine.bank.getSequencesSize() == 0){ + MaShine.bank.addSequence(new Sequence("unamed sequence")); + } + selectedSequence = MaShine.bank.getSequence(0); + } + + private void nextSequence(){ + List sequences = MaShine.bank.getSequences(); + int i = sequences.indexOf(selectedSequence) + 1; + if(i == sequences.size()) + i = 0; + setSelectedSequence(MaShine.bank.getSequence(i)); + } + private void prevSequence(){ + List sequences = MaShine.bank.getSequences(); + int i = sequences.indexOf(selectedSequence) - 1; + if(i == -1) + i = sequences.size() -1; + setSelectedSequence(MaShine.bank.getSequence(i)); + } + + private void moveUpSequence(){ + List sequences = MaShine.bank.getSequences(); + int i = sequences.indexOf(selectedSequence); + if(i != 0) + Collections.swap(sequences, i, i - 1); + } + private void moveDownSequence(){ + List sequences = MaShine.bank.getSequences(); + int i = sequences.indexOf(selectedSequence); + if(i != sequences.size() -1) + Collections.swap(sequences, i, i + 1); + } + public Sequence getSelectedSequence(){return selectedSequence;} + public void setSelectedSequence(Sequence s){ + selectedSequence = s; + + for(Track t : MaShine.engine.getTracks()){ + if(t.isTweaked()){ + t.sequencer.setSequence(s); + } + } + + } +} \ No newline at end of file diff --git a/src/mashine/ui/elements/Button.java b/src/mashine/ui/elements/Button.java index 5d5c077..68f9922 100644 --- a/src/mashine/ui/elements/Button.java +++ b/src/mashine/ui/elements/Button.java @@ -13,14 +13,22 @@ public class Button extends Element{ private Do action; + private boolean actionOnRelease = false; public Button (Drawable parent, int x, int y, int w, int h, Do action) { super(parent, x, y, w, h); this.action = action; + } + public Button (Drawable parent, int x, int y, int w, int h, Do action, boolean actionOnRelease) { + super(parent, x, y, w, h); + this.action = action; + this.actionOnRelease = actionOnRelease; } public void drawContent(){ - if(isClicked()){ + if(!actionOnRelease && isClicked()){ + action.x(); + }else if(actionOnRelease && isReleased()){ action.x(); } } diff --git a/src/mashine/ui/elements/Checkbox.java b/src/mashine/ui/elements/Checkbox.java new file mode 100644 index 0000000..ee7c917 --- /dev/null +++ b/src/mashine/ui/elements/Checkbox.java @@ -0,0 +1,71 @@ +/** + * A checkbox element + * + * @author procsynth - Antoine Pintout + * @since 13-02-2016` + */ + +package mashine.ui.elements; + +import mashine.*; +import mashine.ui.*; + +public class Checkbox extends Element{ + + boolean state = false; + + private FlatColor on; + private FlatColor off; + private FlatColor hover; + + private Do onEnable; + private Do onDisable; + + public Checkbox (Drawable parent, int x, int y, FlatColor off, FlatColor on, FlatColor hover, Do onEnable, Do onDisable) { + super(parent, x, y, 14, 14); + this.on = on; + this.off = off; + this.hover = hover; + this.onEnable = onEnable; + this.onDisable = onDisable; + } + public Checkbox (Drawable parent, int x, int y, Do onEnable, Do onDisable) { + this(parent, x, y, Colors.MATERIAL.BLUE_GREY._200, + Colors.MATERIAL.BLUE_GREY._800, + Colors.MATERIAL.BLUE_GREY._100,onEnable, onDisable); + + } + + public void drawContent(){ + + if(state){ + FlatColor.fill(P.canvas, on); + }else{ + if(isHovered()){ + FlatColor.fill(P.canvas, hover); + }else{ + FlatColor.fill(P.canvas, off); + } + } + + if(isReleased()){ + state = !state; + if(state){ + onEnable.x(); + }else{ + onDisable.x(); + } + } + + FlatColor.stroke(P.canvas, off); + P.canvas.rect(x, y, width, height); + } + + public boolean getState(){ + return state; + } + + public void setState(boolean s){ + state = s; + } +} \ No newline at end of file diff --git a/src/mashine/ui/elements/CloseButton.java b/src/mashine/ui/elements/CloseButton.java index 7d8000b..4aef7bd 100644 --- a/src/mashine/ui/elements/CloseButton.java +++ b/src/mashine/ui/elements/CloseButton.java @@ -14,9 +14,9 @@ public class CloseButton extends Button{ protected UIBox P; - public CloseButton (UIBox parent) { + public CloseButton (final UIBox parent) { // UIREFORM - super(parent, parent.width-22, 0, 22, 22, new Do(){public void x(){parent.M.ui.close(parent);}}); + super(parent, parent.width-22, 0, 22, 22, new Do(){public void x(){MaShine.ui.close(parent);}}); P = parent; } diff --git a/src/mashine/ui/elements/DeviceElement.java b/src/mashine/ui/elements/DeviceElement.java index 283a4fe..c942702 100644 --- a/src/mashine/ui/elements/DeviceElement.java +++ b/src/mashine/ui/elements/DeviceElement.java @@ -7,18 +7,27 @@ package mashine.ui.elements; -import mashine.*; -import mashine.ui.*; -import mashine.scene.*; -import mashine.scene.features.*; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.HashMap; +import java.util.LinkedHashMap; + +import processing.core.PConstants; + +import mashine.scene.Device; +import mashine.scene.features.ColorFeature; +import mashine.scene.features.Coords; +import mashine.scene.features.EditableFeature; +import mashine.scene.features.Feature; +import mashine.scene.features.FixedField; +import mashine.scene.features.SingleField; +import mashine.ui.Colors; +import mashine.ui.Drawable; +import mashine.ui.Element; +import mashine.ui.FlatColor; public class DeviceElement extends Element{ private Device d; - private ArrayList devFeatures; private HashMap frameFeatures; private boolean selected = false; @@ -26,7 +35,6 @@ public DeviceElement (Drawable parent, Device device) { // TODO : generalize translate + push/popMatrix in elements/elements sublclass super(parent, device.getX(), device.getY(), device.getWidth(), device.getHeight()); this.d = device; - this.devFeatures = device.getFeatures(); } public void setFrameFeatures(HashMap frameFeatures){ @@ -38,11 +46,23 @@ public void setSelected(boolean selected){ public void drawContent(){ + x = d.getX(); + y = d.getY(); + width = d.getWidth(); + height = d.getHeight(); + + boolean drawcrossline = false; + boolean drawreticule = false; + int reticuleX = 0; + int reticuleY = 0; + ArrayList devFeatures = d.getFeatures(); P.canvas.noStroke(); // LHM to display all the fields contained in devices LinkedHashMap devFields = new LinkedHashMap(); + FlatColor.fill(P.canvas, Colors.MATERIAL.BLUE_GREY._600); + for(Feature feature : devFeatures){ // for each feature in device : @@ -52,11 +72,22 @@ public void drawContent(){ if(frameFeatures.containsKey(d.getIdentifier() +"."+ feature.getType())){ feature = frameFeatures.get(d.getIdentifier() +"."+ feature.getType()); + featureFields = feature.getFields(); // special case color, coordinates, ... + if(feature instanceof Coords){ + drawreticule = true; + reticuleX = (int) Math.round((double)feature.getField("x")/255.0 * d.getWidth()); + reticuleY = (int) Math.round((double)feature.getField("y")/255.0 * d.getHeight()); + } + if(feature instanceof ColorFeature){ FlatColor.fill(P.canvas, ((ColorFeature)feature).getColor()); P.canvas.noStroke(); + }else if(feature instanceof SingleField){ + for(String f : featureFields.keySet()){ + devFields.put(f, featureFields.get(f)); + } }else{ for(String f : featureFields.keySet()){ devFields.put(feature.getType() +"."+ f, featureFields.get(f)); @@ -68,15 +99,18 @@ public void drawContent(){ // - draw default place holder // - display fields in consequences - if(feature instanceof ColorFeature){ - FlatColor.stroke(P.canvas, Colors.MATERIAL.GREY._700); + if(feature instanceof ColorFeature){ // draw the background in grey FlatColor.fill(P.canvas, Colors.MATERIAL.GREY._800); - + drawcrossline = true; }else if(feature instanceof FixedField){ for(String f : featureFields.keySet()){ devFields.put("(f) "+ f, featureFields.get(f)); + } + }else if(feature instanceof SingleField){ + for(String f : featureFields.keySet()){ + devFields.put(f, featureFields.get(f)); } }else{ for(String f : featureFields.keySet()){ @@ -87,14 +121,32 @@ public void drawContent(){ } // actually draws the device in the canvas + + //FlatColor.stroke(P.canvas, Colors.MATERIAL.GREY._900); + FlatColor.stroke(P.canvas, Colors.MATERIAL.GREY._700); P.canvas.rect(d.getX(), d.getY(), d.getWidth(), d.getHeight()); - P.canvas.line(d.getX(), d.getHeight() + d.getY(), d.getWidth() +d.getX(), d.getY()); + + if(drawcrossline) + P.canvas.line(d.getX(), d.getHeight() + d.getY(), d.getWidth() +d.getX(), d.getY()); + + if(drawreticule){ + FlatColor.stroke(P.canvas, Colors.MATERIAL.CYAN.A700); + P.canvas.noFill(); + P.canvas.line(d.getX() + reticuleX, d.getY() + 1, d.getX() + reticuleX, d.getY() + d.getHeight() - 1); + P.canvas.line(d.getX() + 1, d.getY() + reticuleY, d.getX() + d.getWidth() - 1, d.getY() + reticuleY); + P.canvas.rectMode(PConstants.CENTER); + FlatColor.stroke(P.canvas, Colors.MATERIAL.RED.A700); + P.canvas.rect(d.getX() + reticuleX, d.getY() + reticuleY, 6, 6); + P.canvas.rectMode(PConstants.CORNER); + } + // identifier FlatColor.fill(P.canvas, Colors.MATERIAL.GREEN.A700); - P.canvas.textAlign(P.canvas.LEFT, P.canvas.TOP); - P.canvas.text(d.getIdentifier(), d.getX() + 5, d.getY() + 5); + P.canvas.textAlign(PConstants.LEFT, PConstants.TOP); + P.canvas.text(d.getName(), d.getX() + 5, d.getY() + 4); + P.canvas.noStroke(); // all the fields FlatColor.fill(P.canvas, Colors.MATERIAL.GREEN._400); @@ -103,7 +155,7 @@ public void drawContent(){ for(String f : devFields.keySet()){ if(offset > (d.getHeight() - 5)){ - P.canvas.textAlign(P.canvas.LEFT, P.canvas.BOTTOM); + P.canvas.textAlign(PConstants.LEFT, PConstants.BOTTOM); P.canvas.text("...", d.getX() + 5, d.getY() + d.getHeight()); break; } @@ -118,9 +170,9 @@ public void drawContent(){ // patch infos - P.canvas.textAlign(P.canvas.RIGHT, P.canvas.BOTTOM); + P.canvas.textAlign(PConstants.RIGHT, PConstants.BOTTOM); FlatColor.fill(P.canvas, Colors.MATERIAL.GREEN.A700); - P.canvas.text(d.getUniverse() +":"+ d.getStartAddress(), d.getX() + d.getWidth() - 5, d.getY() + d.getHeight() - 5); + P.canvas.text(d.getUniverse() +":"+ d.getStartAddress(), d.getX() + d.getWidth() - 5, d.getY() + d.getHeight() - 1); if(selected){ FlatColor.fill(P.canvas, Colors.MATERIAL.CYAN.A700); diff --git a/src/mashine/ui/elements/Grabber.java b/src/mashine/ui/elements/Grabber.java index e5dceb0..65a2ee3 100644 --- a/src/mashine/ui/elements/Grabber.java +++ b/src/mashine/ui/elements/Grabber.java @@ -28,7 +28,7 @@ public void drawContent(){ if(isDragged()){ // TODO, no induced move - P.moveBox(M.mouseX - P.width/2, M.mouseY - 11 ); + P.moveBox(MaShine.m.mouseX - P.width/2, MaShine.m.mouseY - 11 ); } } } \ No newline at end of file diff --git a/src/mashine/ui/elements/RangeInput.java b/src/mashine/ui/elements/RangeInput.java new file mode 100644 index 0000000..09e74ca --- /dev/null +++ b/src/mashine/ui/elements/RangeInput.java @@ -0,0 +1,174 @@ +package mashine.ui.elements; + +import processing.core.PConstants; +import mashine.MaShine; +import mashine.ui.Colors; +import mashine.ui.Drawable; +import mashine.ui.Element; +import mashine.ui.FlatColor; + +public class RangeInput extends Element{ + + private Float value; + private String stringValue; + private Float min; + private Float max; + private Float step; + + public RangeInput(Drawable parent, Float defaultValue, Float min, Float max, Float step, int x, int y, int width){ + super(parent, x, y, width, 15); + value = defaultValue; + this.min = min; + this.max = max; + this.step = step; + updateStringValue(); + + } + public RangeInput(Drawable parent, int x, int y, int width){ + this(parent, 0f, 0f, 255f, 1f, x, y, width); + } + + public RangeInput(Drawable parent, int defaultValue, int x, int y, int width){ + this(parent, (float) defaultValue, 0f, 255f, 1f, x, y, width); + } + + public Float value(){ + return value; + } + + public void setMin(Float min){this.min = min;} + public void setMax(Float max){this.max = max;} + public void setStep(Float step){this.step = step;} + + public void setMin(int min){setMin((float) min);} + public void setMax(int max){setMax((float) max);} + public void setStep(int step){setStep((float) step);} + + public void drawContent(){ + P.canvas.noStroke(); + + boolean f = enabled && hasFocus() && P.hasFocus(); + + if(f){ + if(MaShine.inputs.getState("keyboard.8.press")){ + if(stringValue.length() > 0) + stringValue = stringValue.substring(0, stringValue.length() - 1); + }else if(MaShine.inputs.getState("keyboard.9.press") || MaShine.inputs.getState("keyboard.10.press")){ + focus = false; + value = Float.parseFloat(stringValue); + value = normalize(value); + updateStringValue(); + }else if(MaShine.inputs.getState("keyboard.147.press")){ + stringValue = ""; + }else if(MaShine.inputs.getLastKey() != "" && stringValue.length() < maxLength()){ + String newStringValue = stringValue + MaShine.inputs.getLastKey(); + try{ + Float.parseFloat(newStringValue); + stringValue += MaShine.inputs.getLastKey(); + }catch(Exception e){ + + } + } + + if(isDragged()){ + int relX = MaShine.max(0, Math.min(P.mouseX() - x, width)); + value = normalize((float) (max - min + step) * ((float) relX / width)); + updateStringValue(); + } + + } + + if(enabled) + FlatColor.fill(P.canvas, Colors.WHITE); + else + FlatColor.fill(P.canvas, Colors.MATERIAL.GREY._400); + P.canvas.rect(x, y, width, height); + + P.canvas.noStroke(); + + if(enabled) + FlatColor.fill(P.canvas, Colors.MATERIAL.CYAN._300); + else + FlatColor.fill(P.canvas, Colors.MATERIAL.GREY._600); + P.canvas.rect(x, y, Math.min(width,1 + width * (tempValueFromString(stringValue)/(max - min + step))), height); + + if(f){ + FlatColor.stroke(P.canvas, Colors.MATERIAL.CYAN.A700); + P.canvas.noFill(); + P.canvas.rect(x, y, width-1, height-1); + } + + FlatColor.fill(P.canvas, Colors.MATERIAL.BLUE_GREY._900); + P.canvas.textAlign(PConstants.LEFT, PConstants.CENTER); + P.canvas.text(stringValue + (MaShine.m.millis() % 1200 > 600 && enabled && stringValue.length() < maxLength() && hasFocus() ? "_" : ""), x + 3, y +height/2+1); + } + + private Float normalize(Float val){ + if(val != null){ + val = Math.min(max, Math.max(min, val)); + val = val - val % step; + } + + return val; + } + + protected float tempValueFromString(String s){ + + float val = min; + + try{ + if(stringValue.length() > 0){ + val = Float.parseFloat(stringValue); + val = normalize(val); + }else{ + val = min; + } + }catch(Exception e){} + return val; + } + + protected void onDefocus(){ + try{ + if(stringValue.length() > 0){ + value = Float.parseFloat(stringValue); + value = normalize(value); + }else{ + value = min; + } + + updateStringValue(); + }catch(Exception e){} + } + + private void updateStringValue(){ + // if integer step : no point + if(step % 1 == 0){ + try{ + stringValue = Integer.toString(Math.round(value)); + }catch(Exception ignore){} + }else{ + stringValue = Float.toString(value); + } + } + + private int maxLength(){ + // if integer step : no point + if(step % 1 == 0){ + return Integer.toString(Math.round(max)).length(); + }else{ + return Float.toString(value).length(); + } + } + + public void setValue(int v){setValue(new Float(v));} + public void setValue(Float v){ + value = v; + value = normalize(value); + updateStringValue(); + } + + public void setStringValue(String s){ + stringValue = s; + } + +} \ No newline at end of file diff --git a/src/mashine/ui/elements/TextButton.java b/src/mashine/ui/elements/TextButton.java index c67accc..567c7c7 100644 --- a/src/mashine/ui/elements/TextButton.java +++ b/src/mashine/ui/elements/TextButton.java @@ -7,8 +7,11 @@ package mashine.ui.elements; -import mashine.*; -import mashine.ui.*; +import mashine.Do; +import mashine.ui.Colors; +import mashine.ui.Drawable; +import mashine.ui.FlatColor; +import processing.core.PConstants; public class TextButton extends Button{ @@ -27,6 +30,14 @@ public TextButton (Drawable parent, int x, int y, int w, int h, String caption, this.caption = caption; this.marginLeft = marginLeft; } + public TextButton (Drawable parent, int x, int y, int w, int h, String caption, FlatColor text, int marginLeft, FlatColor button, FlatColor hover, Do action, boolean actionOnRelease) { + super(parent, x, y, w, h, action, actionOnRelease); + this.text = text; + this.button = button; + this.hover = hover; + this.caption = caption; + this.marginLeft = marginLeft; + } public TextButton (Drawable parent, String caption, int x, int y, Do action){ this(parent, x, y, 55, 15, caption, @@ -36,6 +47,22 @@ public TextButton (Drawable parent, String caption, int x, int y, Do action){ action ); } + public TextButton (Drawable parent, String caption, int x, int y, int w, Do action){ + this(parent, x, y, w, 15, caption, + Colors.MATERIAL.BLUE_GREY._800, 3, + Colors.MATERIAL.BLUE_GREY._100, + Colors.MATERIAL.BLUE_GREY._200, + action + ); + } + public TextButton (Drawable parent, String caption, int x, int y, Do action, boolean actionOnRelease){ + this(parent, x, y, 55, 15, caption, + Colors.MATERIAL.BLUE_GREY._800, 3, + Colors.MATERIAL.BLUE_GREY._100, + Colors.MATERIAL.BLUE_GREY._200, + action, actionOnRelease + ); + } public void drawContent(){ @@ -50,7 +77,7 @@ public void drawContent(){ P.canvas.noStroke(); P.canvas.rect(x, y, width, height); - P.canvas.textAlign(P.canvas.LEFT, P.canvas.CENTER); + P.canvas.textAlign(PConstants.LEFT, PConstants.CENTER); FlatColor.fill(P.canvas, text); P.canvas.text(caption, x + marginLeft, y + height/2); diff --git a/src/mashine/ui/elements/TextInput.java b/src/mashine/ui/elements/TextInput.java index 081789f..ad7e046 100644 --- a/src/mashine/ui/elements/TextInput.java +++ b/src/mashine/ui/elements/TextInput.java @@ -1,42 +1,63 @@ package mashine.ui.elements; -import mashine.*; -import mashine.ui.*; -import java.util.HashMap; +import java.util.regex.Pattern; + +import processing.core.PConstants; + +import mashine.MaShine; +import mashine.ui.Colors; +import mashine.ui.Drawable; +import mashine.ui.Element; +import mashine.ui.FlatColor; public class TextInput extends Element{ private String value; + private String validChar; public TextInput(Drawable parent, String defaultValue, int x, int y, int width){ + this(parent, defaultValue, x, y, width, ""); + } + public TextInput(Drawable parent, String defaultValue, int x, int y, int width, String additionalsChars){ super(parent, x, y, width, 15); value = defaultValue; + validChar = "["+additionalsChars+"a-zA-Z0-9_ \\-\\@\\#]+"; } public void drawContent(){ P.canvas.noStroke(); - if(hasFocus() && P.hasFocus()){ + if(enabled && hasFocus() && P.hasFocus()){ FlatColor.stroke(P.canvas, Colors.MATERIAL.ORANGE.A700); - if(M.inputs.getState("keyboard.8.press")){ + if(MaShine.inputs.getState("keyboard.8.press")){ if(value.length() > 0) value = value.substring(0, value.length() - 1); - }else if(M.inputs.getState("keyboard.9.press") || M.inputs.getState("keyboard.10.press")){ + }else if(MaShine.inputs.getState("keyboard.9.press") || MaShine.inputs.getState("keyboard.10.press")){ focus = false; - }else if(M.inputs.getState("keyboard.147.press")){ + }else if(MaShine.inputs.getState("keyboard.147.press")){ value = ""; - }else if(M.inputs.getLastKey() != ""){ - value += M.inputs.getLastKey(); + }else if(Pattern.matches(validChar, MaShine.inputs.getLastKey())){ + value += MaShine.inputs.getLastKey(); } } - // UIREFORM - FlatColor.fill(P.canvas, Colors.WHITE); + if(enabled) + FlatColor.fill(P.canvas, Colors.WHITE); + else + FlatColor.fill(P.canvas, Colors.MATERIAL.GREY._400); P.canvas.rect(x, y, width, height); FlatColor.fill(P.canvas, Colors.MATERIAL.BLUE_GREY._900); - P.canvas.textAlign(P.canvas.LEFT, P.canvas.CENTER); - P.canvas.text(value + (M.millis() % 1200 > 600 && hasFocus() ? "_" : ""), x + 3, y +height/2); + P.canvas.textAlign(PConstants.LEFT, PConstants.CENTER); + P.canvas.text(value + (MaShine.m.millis() % 1200 > 600 && enabled && hasFocus() ? "_" : ""), x + 3, y +height/2); + } + + public void setValue(String v){ + value = v; + } + + public String value(){ + return value; } } \ No newline at end of file diff --git a/src/mashine/ui/elements/VerticalRangeInput.java b/src/mashine/ui/elements/VerticalRangeInput.java new file mode 100644 index 0000000..44afc9f --- /dev/null +++ b/src/mashine/ui/elements/VerticalRangeInput.java @@ -0,0 +1,179 @@ +package mashine.ui.elements; + +import processing.core.PConstants; +import mashine.MaShine; +import mashine.ui.Colors; +import mashine.ui.Drawable; +import mashine.ui.Element; +import mashine.ui.FlatColor; + +public class VerticalRangeInput extends Element{ + + private Float value; + private String stringValue; + private Float min; + private Float max; + private Float step; + + public VerticalRangeInput(Drawable parent, Float defaultValue, Float min, Float max, Float step, int x, int y, int height){ + super(parent, x, y, 16, height); + value = defaultValue; + this.min = min; + this.max = max; + this.step = step; + updateStringValue(); + + } + public VerticalRangeInput(Drawable parent, int x, int y, int height){ + this(parent, 0f, 0f, 255f, 1f, x, y, height); + } + + public VerticalRangeInput(Drawable parent, int defaultValue, int x, int y, int height){ + this(parent, (float) defaultValue, 0f, 255f, 1f, x, y, height); + } + + public Float value(){ + return value; + } + + public void setMin(Float min){this.min = min;} + public void setMax(Float max){this.max = max;} + public void setStep(Float step){this.step = step;} + + public void setMin(int min){setMin((float) min);} + public void setMax(int max){setMax((float) max);} + public void setStep(int step){setStep((float) step);} + + public void drawContent(){ + P.canvas.noStroke(); + + boolean f = enabled && hasFocus() && P.hasFocus(); + + if(f){ + if(MaShine.inputs.getState("keyboard.8.press")){ + if(stringValue.length() > 0) + stringValue = stringValue.substring(0, stringValue.length() - 1); + }else if(MaShine.inputs.getState("keyboard.9.press") || MaShine.inputs.getState("keyboard.10.press")){ + focus = false; + value = Float.parseFloat(stringValue); + value = normalize(value); + updateStringValue(); + }else if(MaShine.inputs.getState("keyboard.147.press")){ + stringValue = ""; + }else if(MaShine.inputs.getLastKey() != "" && stringValue.length() < maxLength()){ + String newStringValue = stringValue + MaShine.inputs.getLastKey(); + try{ + Float.parseFloat(newStringValue); + stringValue += MaShine.inputs.getLastKey(); + }catch(Exception e){ + + } + } + + if(isDragged()){ + int relX = MaShine.max(0, Math.min(P.mouseY() - y, height)); + value = normalize((float) (max - min + step) * ((float) (height - relX) / height)); + updateStringValue(); + } + + } + + if(enabled) + FlatColor.fill(P.canvas, Colors.MATERIAL.GREY._800); + else + FlatColor.fill(P.canvas, Colors.MATERIAL.GREY._400); + P.canvas.rect(x, y, width, height); + + P.canvas.noStroke(); + + if(enabled) + FlatColor.fill(P.canvas, Colors.MATERIAL.BLUE._700); + else + FlatColor.fill(P.canvas, Colors.MATERIAL.GREY._600); + float barHeight = Math.min(height,1 + height * (tempValueFromString(stringValue)/(max - min + step))); + P.canvas.rect(x, y+(height-barHeight), width, barHeight); + + if(f){ + FlatColor.stroke(P.canvas, Colors.MATERIAL.CYAN.A700); + P.canvas.noFill(); + P.canvas.rect(x, y, width-1, height-1); + } + + FlatColor.fill(P.canvas, Colors.WHITE); + P.canvas.pushMatrix(); + P.canvas.translate(x+8, y +height - 10); + P.canvas.rotate(3*PConstants.HALF_PI); + P.canvas.textAlign(PConstants.LEFT, PConstants.CENTER); + P.canvas.text(stringValue + (MaShine.m.millis() % 1200 > 600 && enabled && stringValue.length() < maxLength() && hasFocus() ? "_" : ""), 0, 0); + P.canvas.popMatrix(); + } + + private Float normalize(Float val){ + if(val != null){ + val = Math.min(max, Math.max(min, val)); + val = val - val % step; + } + + return val; + } + + protected float tempValueFromString(String s){ + + float val = min; + + try{ + if(stringValue.length() > 0){ + val = Float.parseFloat(stringValue); + val = normalize(val); + }else{ + val = min; + } + }catch(Exception e){} + return val; + } + + protected void onDefocus(){ + try{ + if(stringValue.length() > 0){ + value = Float.parseFloat(stringValue); + value = normalize(value); + }else{ + value = min; + } + + updateStringValue(); + }catch(Exception e){} + } + + private void updateStringValue(){ + // if integer step : no point + if(step % 1 == 0){ + try{ + stringValue = Integer.toString(Math.round(value)); + }catch(Exception ignore){} + }else{ + stringValue = Float.toString(value); + } + } + + private int maxLength(){ + // if integer step : no point + if(step % 1 == 0){ + return Integer.toString(Math.round(max)).length(); + }else{ + return Float.toString(value).length(); + } + } + + public void setValue(int v){setValue(new Float(v));} + public void setValue(Float v){ + value = v; + value = normalize(value); + updateStringValue(); + } + + public void setStringValue(String s){ + stringValue = s; + } + +} \ No newline at end of file