kopia lustrzana https://github.com/jameshball/osci-render
Open code editor when lua file is opened and allow live error-resistant editing
rodzic
d2cd0ea2a1
commit
c37bab596d
|
@ -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) {
|
||||
|
|
|
@ -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<byte[], String> 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<byte[], String> 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<T, U> {
|
||||
void accept(T t, U u) throws Exception;
|
||||
}
|
||||
}
|
|
@ -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<String> frameSourcePaths = new ArrayList<>();
|
||||
private List<FrameSource<Vector2>> sampleSources = new ArrayList<>();
|
||||
private List<FrameSource<List<Shape>>> frameSources = new ArrayList<>();
|
||||
private LuaParser luaParser = new LuaParser();
|
||||
private FrameSource<Vector2> sampleSource;
|
||||
private FrameProducer<List<Shape>> 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<Vector2> samples = sampleSources.get(index);
|
||||
if (samples != null) {
|
||||
samples.enable();
|
||||
audioPlayer.setSampleSource(samples);
|
||||
}
|
||||
}
|
||||
|
||||
void updateFiles(List<byte[]> files, List<String> names, int startingFrameSource) throws Exception {
|
||||
List<FrameSource<Vector2>> newSampleSources = new ArrayList<>();
|
||||
List<FrameSource<List<Shape>>> 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());
|
||||
|
|
|
@ -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<FrameSource<Vector2>> {
|
||||
|
||||
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<FrameSource<Vector2>> {
|
|||
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) {
|
||||
|
|
|
@ -10,29 +10,56 @@ import javax.script.SimpleBindings;
|
|||
|
||||
public class LuaSampleSource implements FrameSource<Vector2> {
|
||||
|
||||
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();
|
||||
return new Vector2(result.get(1).checkdouble(), result.get(2).checkdouble());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (updatedFile) {
|
||||
// reset variables
|
||||
step = 1;
|
||||
theta = 0;
|
||||
}
|
||||
|
||||
return new Vector2(result.get(1).checkdouble(), result.get(2).checkdouble());
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
return new Vector2();
|
||||
}
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue