diff --git a/src/main/java/sh/ball/gui/Gui.java b/src/main/java/sh/ball/gui/Gui.java index ec5bbce..23fd32d 100644 --- a/src/main/java/sh/ball/gui/Gui.java +++ b/src/main/java/sh/ball/gui/Gui.java @@ -5,21 +5,15 @@ import javafx.application.Platform; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; import javafx.stage.Stage; import sh.ball.audio.ShapeAudioPlayer; import sh.ball.audio.engine.AudioDevice; import sh.ball.audio.engine.ConglomerateAudioEngine; import sh.ball.audio.midi.MidiCommunicator; -import sh.ball.engine.Vector3; import sh.ball.gui.components.CodeEditor; import sh.ball.gui.controller.MainController; import sh.ball.shapes.Vector2; @@ -32,6 +26,9 @@ public class Gui extends Application { public static final MidiCommunicator midiCommunicator = new MidiCommunicator(); public static ShapeAudioPlayer audioPlayer; public static AudioDevice defaultDevice; + public static CodeEditor editor; + public static Stage editorStage; + public static Scene editorScene; static { try { @@ -47,6 +44,15 @@ public class Gui extends Application { public void start(Stage stage) throws Exception { System.setProperty("prism.lcdtext", "false"); + editor = new CodeEditor(); + editor.initialize(); + editorStage = new Stage(); + editorScene = new Scene(editor); + editorStage.setScene(editorScene); + editorStage.getIcons().add(new Image(Objects.requireNonNull(Gui.class.getResourceAsStream("/icons/icon.png")))); + editor.prefHeightProperty().bind(editorStage.heightProperty()); + editor.prefWidthProperty().bind(editorStage.widthProperty()); + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/main.fxml")); Parent root = loader.load(); MainController controller = loader.getController(); @@ -103,19 +109,18 @@ public class Gui extends Application { Platform.exit(); System.exit(0); }); + } - CodeEditor editor = new CodeEditor("theta = theta + math.sqrt(step) / 1000000000\n" + - "return {0.3 * math.tan(theta * step), 0.3 * math.tan(theta * step + math.pi / 2), theta}"); - - // display the scene. - Scene editorScene = new Scene(editor); - Stage editorStage = new Stage(); - - editor.prefHeightProperty().bind(stage.heightProperty()); - editor.prefWidthProperty().bind(stage.widthProperty()); - - editorStage.setScene(editorScene); + public static void launchCodeEditor(String code, String fileName) { + editor.setCode(code, fileName); editorStage.show(); + editorStage.setTitle(fileName); + } + + public static void closeCodeEditor() { + if (editorStage != null) { + editorStage.close(); + } } public static void main(String[] args) { diff --git a/src/main/java/sh/ball/gui/components/CodeEditor.java b/src/main/java/sh/ball/gui/components/CodeEditor.java index 74c1d13..d10663e 100644 --- a/src/main/java/sh/ball/gui/components/CodeEditor.java +++ b/src/main/java/sh/ball/gui/components/CodeEditor.java @@ -10,28 +10,46 @@ import netscape.javascript.JSObject; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; public class CodeEditor extends StackPane { - final WebView webview = new WebView(); + private WebView webview; - private String editingCode; + private String editingCode = ""; + private String fileName = ""; + private BiConsumer callback; private String applyEditingTemplate() throws URISyntaxException, IOException { String template = Files.readString(Path.of(getClass().getResource("/html/code_editor.html").toURI())); return template.replace("${code}", editingCode); } - public void updateCode() { - this.editingCode = (String) webview.getEngine().executeScript("editor.getValue();"); - System.out.println(editingCode); + public void setCode(String editingCode, String fileName) { + this.fileName = fileName; + this.editingCode = editingCode; + JSObject window = (JSObject) webview.getEngine().executeScript("window"); + window.setMember("newCode", editingCode); + webview.getEngine().executeScript("editor.setValue(newCode);"); } - public CodeEditor(String editingCode) throws URISyntaxException, IOException { - this.editingCode = editingCode; + public void updateCode() throws Exception { + editingCode = (String) webview.getEngine().executeScript("editor.getValue();"); + if (callback != null) { + callback.accept(editingCode.getBytes(StandardCharsets.UTF_8), fileName); + } + } + public void setCallback(BiConsumer callback) { + this.callback = callback; + } + + public CodeEditor() {} + + public void initialize() throws URISyntaxException, IOException { + webview = new WebView(); webview.getEngine().getLoadWorker().stateProperty().addListener((e, old, state) -> { if (state == Worker.State.SUCCEEDED) { JSObject window = (JSObject) webview.getEngine().executeScript("window"); @@ -47,4 +65,9 @@ public class CodeEditor extends StackPane { this.getChildren().add(webview); } + + @FunctionalInterface + public interface BiConsumer { + void accept(T t, U u) throws Exception; + } } \ No newline at end of file diff --git a/src/main/java/sh/ball/gui/controller/MainController.java b/src/main/java/sh/ball/gui/controller/MainController.java index 3c26a33..3bf9850 100644 --- a/src/main/java/sh/ball/gui/controller/MainController.java +++ b/src/main/java/sh/ball/gui/controller/MainController.java @@ -16,7 +16,9 @@ import org.w3c.dom.NodeList; import sh.ball.audio.*; import java.io.*; +import java.net.URISyntaxException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.text.*; import java.util.*; import java.util.concurrent.*; @@ -59,8 +61,7 @@ import sh.ball.parser.ParserFactory; import sh.ball.shapes.Shape; import sh.ball.shapes.Vector2; -import static sh.ball.gui.Gui.audioPlayer; -import static sh.ball.gui.Gui.defaultDevice; +import static sh.ball.gui.Gui.*; import static sh.ball.math.Math.parseable; public class MainController implements Initializable, FrequencyListener, MidiListener, AudioInputListener { @@ -100,6 +101,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis private List frameSourcePaths = new ArrayList<>(); private List> sampleSources = new ArrayList<>(); private List>> frameSources = new ArrayList<>(); + private LuaParser luaParser = new LuaParser(); private FrameSource sampleSource; private FrameProducer> producer; private int currentFrameSource; @@ -498,6 +500,8 @@ public class MainController implements Initializable, FrequencyListener, MidiLis } } + editor.setCallback(this::updateFileData); + objectServer = new ObjectServer(this::enableObjectServerRendering, this::disableObjectServerRendering); new Thread(objectServer).start(); @@ -620,6 +624,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis if (frames != null) { frames.enable(); audioPlayer.removeSampleSource(); + closeCodeEditor(); Object oldSettings = producer.getFrameSettings(); producer = new FrameProducer<>(audioPlayer, frames); @@ -635,6 +640,9 @@ public class MainController implements Initializable, FrequencyListener, MidiLis executor.submit(producer); } else if (samples != null) { samples.enable(); + String code = new String(openFiles.get(currentFrameSource), StandardCharsets.UTF_8); + closeCodeEditor(); + launchCodeEditor(code, frameSourcePaths.get(currentFrameSource)); audioPlayer.setSampleSource(samples); } else { throw new RuntimeException("Expected to have either a frame source or sample source"); @@ -647,6 +655,32 @@ public class MainController implements Initializable, FrequencyListener, MidiLis objTitledPane.setDisable(!ObjParser.isObjFile(frameSourcePaths.get(index))); } + void updateFileData(byte[] file, String name) throws Exception { + int index = frameSourcePaths.indexOf(name); + if (index == -1) { + throw new RuntimeException("Can't find open file with name: " + name); + } + if (LuaParser.isLuaFile(name)) { + luaParser.setScriptFromInputStream(new ByteArrayInputStream(file)); + sampleSources.set(index, luaParser.parse()); + frameSources.set(index, null); + } else { + frameSources.set(index, ParserFactory.getParser(name, file).parse()); + sampleSources.set(index, null); + } + frameSourcePaths.set(index, name); + openFiles.set(index, file); + + frameSources.stream().filter(Objects::nonNull).forEach(FrameSource::disable); + sampleSources.stream().filter(Objects::nonNull).forEach(FrameSource::disable); + + FrameSource samples = sampleSources.get(index); + if (samples != null) { + samples.enable(); + audioPlayer.setSampleSource(samples); + } + } + void updateFiles(List files, List names, int startingFrameSource) throws Exception { List> newSampleSources = new ArrayList<>(); List>> newFrameSources = new ArrayList<>(); @@ -661,7 +695,8 @@ public class MainController implements Initializable, FrequencyListener, MidiLis for (int i = 0; i < files.size(); i++) { try { if (LuaParser.isLuaFile(names.get(i))) { - newSampleSources.add(new LuaParser(new ByteArrayInputStream(files.get(i))).parse()); + luaParser.setScriptFromInputStream(new ByteArrayInputStream(files.get(i))); + newSampleSources.add(luaParser.parse()); newFrameSources.add(null); } else { newFrameSources.add(ParserFactory.getParser(names.get(i), files.get(i)).parse()); diff --git a/src/main/java/sh/ball/parser/lua/LuaParser.java b/src/main/java/sh/ball/parser/lua/LuaParser.java index 66aaac0..c85c29e 100644 --- a/src/main/java/sh/ball/parser/lua/LuaParser.java +++ b/src/main/java/sh/ball/parser/lua/LuaParser.java @@ -1,6 +1,5 @@ package sh.ball.parser.lua; -import org.luaj.vm2.lib.jse.JsePlatform; import sh.ball.audio.FrameSource; import sh.ball.parser.FileParser; import sh.ball.shapes.Vector2; @@ -11,9 +10,11 @@ import java.util.stream.Collectors; public class LuaParser extends FileParser> { + private final LuaSampleSource sampleSource = new LuaSampleSource(); + private String script; - public LuaParser(InputStream inputStream) { + public void setScriptFromInputStream(InputStream inputStream) { this.script = new BufferedReader(new InputStreamReader(inputStream)) .lines().collect(Collectors.joining("\n")); } @@ -29,7 +30,9 @@ public class LuaParser extends FileParser> { Compilable e = (Compilable) sem.getEngineByName("luaj"); CompiledScript compiledScript = e.compile(script); - return new LuaSampleSource(compiledScript); + sampleSource.setScript(script, compiledScript); + + return sampleSource; } public static boolean isLuaFile(String path) { diff --git a/src/main/java/sh/ball/parser/lua/LuaSampleSource.java b/src/main/java/sh/ball/parser/lua/LuaSampleSource.java index b60ecb1..7e39be1 100644 --- a/src/main/java/sh/ball/parser/lua/LuaSampleSource.java +++ b/src/main/java/sh/ball/parser/lua/LuaSampleSource.java @@ -10,29 +10,56 @@ import javax.script.SimpleBindings; public class LuaSampleSource implements FrameSource { - private final CompiledScript script; private final Bindings bindings = new SimpleBindings(); + private CompiledScript lastWorkingScript; + private String lastWorkingTextScript; + private CompiledScript script; + private String textScript; private boolean active = true; private long step = 1; private double theta = 0.0; - public LuaSampleSource(CompiledScript script) { + public void setScript(String textScript, CompiledScript script) { + if (lastWorkingScript == null) { + lastWorkingScript = script; + lastWorkingTextScript = textScript; + } this.script = script; + this.textScript = textScript; } @Override public Vector2 next() { try { + boolean updatedFile = false; + bindings.put("step", (double) step); bindings.put("theta", theta); - LuaValue result = (LuaValue) script.eval(bindings); + + LuaValue result; + try { + result = (LuaValue) script.eval(bindings); + if (!lastWorkingTextScript.equals(textScript)) { + lastWorkingScript = script; + lastWorkingTextScript = textScript; + updatedFile = true; + } + } catch (Exception e) { + result = (LuaValue) lastWorkingScript.eval(bindings); + } step++; theta = result.get(3).checkdouble(); + + if (updatedFile) { + // reset variables + step = 1; + theta = 0; + } + return new Vector2(result.get(1).checkdouble(), result.get(2).checkdouble()); - } catch (Exception e) { - e.printStackTrace(); - } + } catch (Exception ignored) {} + return new Vector2(); }