diff --git a/app/src/main/java/com/annimon/hotarufx/Main.java b/app/src/main/java/com/annimon/hotarufx/Main.java index 9d99e39..0816167 100644 --- a/app/src/main/java/com/annimon/hotarufx/Main.java +++ b/app/src/main/java/com/annimon/hotarufx/Main.java @@ -1,12 +1,17 @@ package com.annimon.hotarufx; -import com.annimon.hotarufx.visual.Composition; -import com.annimon.hotarufx.visual.KeyFrame; -import com.annimon.hotarufx.visual.objects.CircleNode; -import com.annimon.hotarufx.visual.visitors.RenderVisitor; +import com.annimon.hotarufx.bundles.BundleLoader; +import com.annimon.hotarufx.bundles.CompositionBundle; +import com.annimon.hotarufx.bundles.NodesBundle; +import com.annimon.hotarufx.lexer.HotaruLexer; +import com.annimon.hotarufx.lib.Context; +import com.annimon.hotarufx.parser.HotaruParser; +import com.annimon.hotarufx.parser.visitors.InterpreterVisitor; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; import javafx.application.Application; -import javafx.scene.paint.Color; -import javafx.scene.paint.Paint; import javafx.stage.Stage; import lombok.val; @@ -14,49 +19,17 @@ public class Main extends Application { @Override public void start(Stage primaryStage) { - val composition = new Composition(1280, 720, 30); - val scene = composition.getScene(); + val input = readProgram("/main.hfx"); + val context = new Context(); + BundleLoader.load(context, Arrays.asList( + CompositionBundle.class, + NodesBundle.class + )); - val colors = new Paint[] {Color.GREEN, Color.RED}; - val halfWidth = scene.getVirtualWidth() / 2; - val halfHeight = scene.getVirtualHeight() / 2; - val renderVisitor = new RenderVisitor(composition.getTimeline()); - for (int y = -1; y <= 1; y++) { - for (int x = -1; x <= 1; x++) { - val node = new CircleNode(); - val colorIndex = Math.abs(x * y); - node.circle.setFill(colors[colorIndex]); - node.circle.setCenterX(x * halfWidth); - node.circle.setCenterY(y * halfHeight); - node.circle.setRadius(50); - node.radiusProperty() - .add(KeyFrame.of(30), 70) - .add(KeyFrame.of(90), 20) - .add(KeyFrame.of(300), 70); - node.fillProperty() - .add(KeyFrame.of(150), colors[1 - colorIndex]) - .add(KeyFrame.of(300), colors[colorIndex]); - if (x == 0 && y == 0) { - node.centerXProperty() - .add(KeyFrame.of(60), 0) - .add(KeyFrame.of(90), -400) - .add(KeyFrame.of(150), 400) - .add(KeyFrame.of(180), 0); - node.centerYProperty() - .add(KeyFrame.of(180), 0) - .add(KeyFrame.of(210), -400) - .add(KeyFrame.of(270), 400) - .add(KeyFrame.of(300), 0); - node.radiusProperty() - .add(KeyFrame.of(320), 180); - node.fillProperty() - .add(KeyFrame.of(300), node.circle.getFill()) - .add(KeyFrame.of(320), Color.WHITE); - } - node.accept(renderVisitor, scene); - } - } + HotaruParser.parse(HotaruLexer.tokenize(input)) + .accept(new InterpreterVisitor(), context); + val composition = context.composition(); primaryStage.setTitle("HotaruFX"); primaryStage.setScene(composition.produceAnimationScene()); composition.getTimeline().getFxTimeline().play(); @@ -66,4 +39,23 @@ public class Main extends Application { public static void main(String[] args) { launch(args); } + + private static String readProgram(String path) { + val fallbackProgram = "composition(640, 480, 25)"; + try (InputStream is = Main.class.getResourceAsStream(path)) { + if (is == null) { + return fallbackProgram; + } + val baos = new ByteArrayOutputStream(); + val bufferSize = 4096; + val buffer = new byte[bufferSize]; + int read; + while ((read = is.read(buffer, 0, bufferSize)) != -1) { + baos.write(buffer, 0, read); + } + return baos.toString("UTF-8"); + } catch (IOException ioe) { + return fallbackProgram; + } + } } diff --git a/app/src/main/java/com/annimon/hotarufx/bundles/CompositionBundle.java b/app/src/main/java/com/annimon/hotarufx/bundles/CompositionBundle.java index cd28c89..cb90300 100644 --- a/app/src/main/java/com/annimon/hotarufx/bundles/CompositionBundle.java +++ b/app/src/main/java/com/annimon/hotarufx/bundles/CompositionBundle.java @@ -1,8 +1,12 @@ package com.annimon.hotarufx.bundles; import com.annimon.hotarufx.lib.Context; +import com.annimon.hotarufx.lib.NodeValue; import com.annimon.hotarufx.lib.NumberValue; +import com.annimon.hotarufx.lib.Types; import com.annimon.hotarufx.visual.Composition; +import com.annimon.hotarufx.visual.visitors.RenderVisitor; +import java.util.Arrays; import lombok.val; public class CompositionBundle implements Bundle { @@ -44,5 +48,15 @@ public class CompositionBundle implements Bundle { context.variables().put("HalfHeight", NumberValue.of(scene.getVirtualHeight() / 2)); return NumberValue.ZERO; }); + + context.functions().put("render", args -> { + val renderVisitor = new RenderVisitor(context.composition().getTimeline()); + val scene = context.composition().getScene(); + Arrays.stream(args) + .filter(v -> v.type() == Types.NODE) + .map(v -> ((NodeValue) v).getNode()) + .forEach(node -> node.accept(renderVisitor, scene)); + return NumberValue.ZERO; + }); } } diff --git a/app/src/main/resources/main.hfx b/app/src/main/resources/main.hfx new file mode 100644 index 0000000..0cbdd5c --- /dev/null +++ b/app/src/main/resources/main.hfx @@ -0,0 +1,10 @@ +composition(1280, 720, 30) + +A = circle({ + centerX: 0, + centerY: 0, + radius: 50, + fill: "red" +}) + +render(A) \ No newline at end of file