JavaFX 场景中的 Java Processing 3 PAplet 作为 FXNode [英] Java Processing 3 PAplet in JavaFX scene as FXNode

查看:29
本文介绍了JavaFX 场景中的 Java Processing 3 PAplet 作为 FXNode的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试制作一个用于可视化分析分形集的程序.我选择 Processing 3 作为绘图库,选择 JavaFX 作为用户界面.有一些当前状态的截图:

我的图形用户界面:

有启动器代码:

import Graphics.Canvas2D;导入 javafx.application.Application;导入 javafx.fxml.FXMLLoader;导入 javafx.scene.Parent;导入 javafx.scene.Scene;导入 javafx.stage.Stage;导入 processing.core.PGraphics;导入 java.io.IOException;公共类启动器扩展应用程序{私有静态阶段primaryStage;公共静态无效主(字符串 [] args){发射(参数);}@覆盖公共无效开始(阶段primaryStage){父根 = loadFXML("MainUI.fxml");场景场景 = 新场景(root, 500, 400);primaryStage.setTitle("分形分析器");primaryStage.setScene(场景);primaryStage.show();primaryStage.setMaximized(true);Launcher.primaryStage = primaryStage;}@覆盖公共无效初始化(){}@覆盖公共无效停止(){System.exit(0);}公共静态舞台 getPrimaryStage() {返回primaryStage;}公共无效 setCanvas(Canvas2D 画布){}私有父 loadFXML(字符串路径){尝试 {返回 FXMLLoader.load(getClass().getResource(path));} catch (IOException e) {e.printStackTrace();}System.exit(1);返回空;}}

测试分形 PAplet:

有这个PAplet的代码:

包分形;导入处理.core.PApplet;公共类 SirpenskiTriangle 扩展了 PApplet {公共静态无效主(字符串 [] args){PApplet.main("Fractal.SirpenskiTriangle");}公共无效设置(){大小(640, 640);光滑的();如果(帧!= null){frame.setResizable(true);}}公共无效画(){drawTriangle(new Position(300, 20), new Position(620, 620), new Position(20, 620), 0);无环();规模(10f);}公共无效设置(){}public void drawTriangle(位置顶部,位置右,位置左,整数深度){如果(深度> 10)返回;线(top.x,top.y,right.x,right.y);线(right.x,right.y,left.x,left.y);线(left.x,left.y,top.x,top.y);drawTriangle(top, top.middleWith(right), top.middleWith(left), depth + 1);drawTriangle(top.middleWith(left), left.middleWith(right), left, depth + 1);drawTriangle(top.middleWith(right), right, left.middleWith(right), depth + 1);}班级位置{最终浮点数 x;最终浮动 y;位置(浮动 x,浮动 y){this.x = x;这.y = y;}位置 middleWith(Position other) {返回新位置((x + other.x)/2, (y + other.y)/2);}}}

有什么方法可以将处理 PAplet 放入画布或类似的 JavaFX 场景中吗?

我希望它可以这样工作,但这段代码无效:

解决方案

我设计了两种方法:首先,我们绕过 Processing 的 JavaFX 阶段创建并指向 Processing 以绘制到从 FXML 文件加载的 JavaFX 阶段;在第二个中,我们将 Processing 的默认 JavaFX 场景替换为 在运行时从 FXML 文件加载的场景.

1.从 FXML 启动

使用第一种方法,我们像启动 JavaFX 应用程序一样启动应用程序(使用 Application.launch(Launcher.class);),完全绕过 Processing 的 JavaFX 阶段创建代码.

您必须下载稍微修改过的 core.jar 才能使这种方法起作用,我在其中更改了 PSurfaceFX 的一些成员的可见性,并且PGraphicsFX2D 类从 ProtectedPublic.这些更改允许我们从我们自己的 ... extends Application 类启动 JavaFX,同时保持对 Processing 在启动期间需要设置的成员的访问权限.

当使用的 JDK 高于 Java 8 时,在 FX2D 模式下处理 3 崩溃,所以我也为 8+ 制作了一个工作版本,因为 FXML 文件通常至少需要 Java 9 才能工作.

  • 将修改后的 core.jar 添加到项目的类路径后,使用以下代码段覆盖 PApplet 类中的 initSurface().使用此代码,我们绕过了 PApplet 对 initFrame() 的调用 - 这是处理创建自己的 JavaFX 阶段的地方,我们不希望它这样做.

    @Override受保护的 PSurface initSurface() {g = createPrimaryGraphics();PSurface genericSurface = g.createSurface();PSurfaceFX fxSurface = (PSurfaceFX) genericSurface;fxSurface.sketch = 这个;Launcher.surface = fxSurface;新线程(新运行(){公共无效运行(){Application.launch(Launcher.class);}}).开始();而(fxSurface.stage == null){尝试 {线程睡眠(5);} catch (InterruptedException e) {}}this.surface = fxSurface;返回 fxSurface;}

    将 PApplet 的渲染模式设置为 FX2D,如下所示:

    @Override公共无效设置(){大小(0, 0, FX2D);}

    将以下内容或类似内容放入您的 Launcher 类中.在这个例子中,我手动找到了要添加画布对象的节点.有更好的、更程序化的方法来做到这一点(例如 .lookup() 使用所需节点的 fx:id —— 这可以在 FXML 文件中定义).我还将画布的尺寸绑定到其父级的尺寸,因此当分隔 MasterView 窗格的除数被拖动时,处理画布会相应地调整大小.>

    public class Launcher extends Application {公共静态 PSurfaceFX 表面;@覆盖public void start(Stage primaryStage) 抛出异常 {Canvas canvas = (Canvas)surface.getNative();//样板GraphicsContext graphicsContext = canvas.getGraphicsContext2D();//样板surface.fx.context = graphicsContext;//样板primaryStage.setTitle("FXML/处理");VBox root = FXMLLoader.load(new File("c:/Users/Mike/desktop/test.fxml").toURI().toURL());SplitPane 窗格 = (SplitPane) root.getChildren().get(1);//手动获取我想要添加画布的项目AnchorPane pane2 = (AnchorPane) pane.getItems().get(0);//手动获取我想要添加画布的项目pane2.getChildren().add(canvas);//手动获取我想要添加画布的项目canvas.widthProperty().bind(pane2.widthProperty());canvas.heightProperty().bind(pane2.heightProperty());场景场景 = 新场景(root, 800, 800);primaryStage.setScene(场景);primaryStage.show();surface.stage = 初级阶段;//样板}}

    结果如下:

    另见

    ...使用这个 FXML 文件:

    I am trying to make a program for visual analyzing Fractal sets. I choose Processing 3 as drawing library and JavaFX for the user interface. There are some screenshots of the current state:

    My GUI:

    there is Launcher code:

    import Graphics.Canvas2D;
    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    import processing.core.PGraphics;
    
    import java.io.IOException;
    
    public class Launcher extends Application {
        private static Stage primaryStage;
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage primaryStage) {
            Parent root = loadFXML("MainUI.fxml");
            Scene scene = new Scene(root, 500, 400);
            primaryStage.setTitle("Fractal Analyzer");
            primaryStage.setScene(scene);
            primaryStage.show();
            primaryStage.setMaximized(true);
    
            Launcher.primaryStage = primaryStage;
        }
    
        @Override
        public void init() {
    
        }
    
        @Override
        public void stop() {
            System.exit(0);
        }
    
        public static Stage getPrimaryStage() {
            return primaryStage;
        }
    
        public  void setCanvas(Canvas2D canvas){
    
        }
    
    
    
        private Parent loadFXML(String path) {
            try {
                return FXMLLoader.load(getClass().getResource(path));
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.exit(1);
            return null;
        }
    }
    

    Testing fractal PAplet:

    There is a code of this PAplet:

    package Fractal;
    
    import processing.core.PApplet;
    
    public class SirpenskiTriangle extends PApplet {
    
        public static void main(String[] args) {
            PApplet.main("Fractal.SirpenskiTriangle");
        }
    
        public void settings() {
            size(640, 640);
            smooth();
            if (frame != null) {
                frame.setResizable(true);
            }
        }
    
        public void draw() {
            drawTriangle(new Position(300, 20), new Position(620, 620), new Position(20, 620), 0);
            noLoop();
            scale(10f);
        }
    
        public void setup(){}
    
        public void drawTriangle(Position top, Position right, Position left, int depth) {
            if (depth > 10) return;
    
            line(top.x, top.y, right.x, right.y);
            line(right.x, right.y, left.x, left.y);
            line(left.x, left.y, top.x, top.y);
    
            drawTriangle(top, top.middleWith(right), top.middleWith(left), depth + 1);
            drawTriangle(top.middleWith(left), left.middleWith(right), left, depth + 1);
            drawTriangle(top.middleWith(right), right, left.middleWith(right), depth + 1);
        }
    
        class Position {
            final float x;
            final float y;
    
            Position(float x, float y) {
                this.x = x;
                this.y = y;
            }
    
            Position middleWith(Position other) {
                return new Position((x + other.x) / 2, (y + other.y) / 2);
            }
        }
    }
    

    Is there any way to put processing PAplet into JavaFX scene like canvas or something similar?

    I hope it can work like this, but this code is invalid:

    解决方案

    I have devised two approaches: in the first, we bypass Processing's JavaFX stage creation and point Processing to draw into a JavaFX stage loaded from an FXML file; in the second, we replace Processing's default JavaFX scene with one loaded from an FXML file during runtime.

    1. Launching from an FXML

    With the first approach we launch the application like we would a JavaFX app (using Application.launch(Launcher.class);), completely bypassing Processing's JavaFX stage creation code.

    You'll have to download a slightly modified core.jar for this approach to work, where I've changed the visibility of a few members of the PSurfaceFX and PGraphicsFX2D classes from Protected to Public. The changes allow us to launch JavaFX from our own ... extends Application class, while maintaining access to the members that Processing needs to set during the launch to function.

    Processing 3 crashes in FX2D mode when the JDK in use is above Java 8, so I've also made a working version for 8+, since the FXML files usually need at least Java 9 to work.

    This is the FXML file I am working with in this example:

    With the modified core.jar added to your project's classpath, override initSurface() in your PApplet class with the following snippet. With this code, we bypass the PApplet's call to initFrame() - this is where processing creates its own JavaFX stage, which we do not want it to do.

    @Override
    protected PSurface initSurface() {
        g = createPrimaryGraphics();
        PSurface genericSurface = g.createSurface();
        PSurfaceFX fxSurface = (PSurfaceFX) genericSurface;
    
        fxSurface.sketch = this;
    
        Launcher.surface = fxSurface;
    
        new Thread(new Runnable() {
            public void run() {
                Application.launch(Launcher.class);
            }
        }).start();
    
        while (fxSurface.stage == null) {
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
            }
        }
    
        this.surface = fxSurface;
        return fxSurface;
    }
    

    Set the PApplet's renderering mode to FX2D like so:

    @Override
    public void settings() {
        size(0, 0, FX2D);
    }
    

    Put the following, or similar, in your Launcher class. In this example, I have manually found the Node that I want to add the canvas object into. There are better, more programmatic, ways of doing this (such as .lookup() using the fx:id of the desired node -- this can be defined in the FXML file). I have also bound the dimensions of the canvas to those of its parent, so when the divisor separating the Master and View panes is dragged, the Processing canvas resizes accordingly.

    public class Launcher extends Application {
    
        public static PSurfaceFX surface;
    
        @Override
        public void start(Stage primaryStage) throws Exception {
    
            Canvas canvas = (Canvas) surface.getNative(); // boilerplate
            GraphicsContext graphicsContext = canvas.getGraphicsContext2D(); // boilerplate
            surface.fx.context = graphicsContext; // boilerplate
    
            primaryStage.setTitle("FXML/Processing");
    
            VBox root = FXMLLoader.load(new File("c:/Users/Mike/desktop/test.fxml").toURI().toURL());
            SplitPane pane = (SplitPane) root.getChildren().get(1); // Manually get the item I want to add canvas to
            AnchorPane pane2 = (AnchorPane) pane.getItems().get(0); // Manually get the item I want to add canvas to
            pane2.getChildren().add(canvas); // Manually get the item I want to add canvas to
    
            canvas.widthProperty().bind(pane2.widthProperty());
            canvas.heightProperty().bind(pane2.heightProperty());
    
            Scene scene = new Scene(root, 800, 800);
            primaryStage.setScene(scene);
            primaryStage.show();
    
            surface.stage = primaryStage; // boilerplate
        }
    }
    

    This is the result:

    Also see this Github project -- a basic project showing how a Processing sketch and a FXML JavaFX stage may be integrated using this first approach, but includes a JavaFX Controller to populate @FXMLannotated fields (providing an easy way to first get, and then reference, JavaFX objects in code).


    2. Launching, then loading a FXML

    This approach works with vanilla Processing. Here, we launch Processing like normal and then replace the default scene with new scene loaded from an FXML file during runtime. This is a simpler approach (and doesn't require using a modified .jar!) but will make JavaFX/Processing interoperability more difficult because we can't use a JavaFX Controller to get fields via FXML injection.

    Example PDE code:

    import java.util.Map;
    import java.nio.file.Paths;
    
    import javafx.application.Platform;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.scene.SceneAntialiasing;
    import javafx.scene.canvas.Canvas;
    import javafx.scene.layout.AnchorPane;
    import javafx.stage.Stage;
    
    import processing.javafx.PSurfaceFX;
    
    public void setup() {
      size(800, 800, FX2D);
      strokeWeight(3);
    }
    
    protected PSurface initSurface() {
      surface = (PSurfaceFX) super.initSurface();
      final Canvas canvas = (Canvas) surface.getNative();
      final Scene oldScene = canvas.getScene();
      final Stage stage = (Stage) oldScene.getWindow();
    
      try {
        FXMLLoader loader = new FXMLLoader(Paths.get("C:\path--to--fxml\stage.fxml").toUri().toURL()); // abs path to fxml file
        final Parent sceneFromFXML = loader.load();
        final Map<String, Object> namespace = loader.getNamespace();
    
        final Scene newScene = new Scene(sceneFromFXML, stage.getWidth(), stage.getHeight(), false, 
          SceneAntialiasing.BALANCED);
        final AnchorPane pane = (AnchorPane) namespace.get("anchorPane"); // get element by fx:id
    
        pane.getChildren().add(canvas); // processing to stackPane
        canvas.widthProperty().bind(pane.widthProperty()); // bind canvas dimensions to pane
        canvas.heightProperty().bind(pane.heightProperty()); // bind canvas dimensions to pane
    
        Platform.runLater(new Runnable() {
          @Override
            public void run() {
            stage.setScene(newScene);
          }
        }
        );
      } 
      catch (IOException e) {
        e.printStackTrace();
      }
      return surface;
    }
    
    public void draw() {
      background(125, 125, 98);
      ellipse(200, 200, 200, 200);
      line(0, 0, width, height);
      line(width, 0, 0, height);
    }
    

    Result:

    …using this FXML file:

    这篇关于JavaFX 场景中的 Java Processing 3 PAplet 作为 FXNode的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆