├── .gitignore ├── ProcessingFX.pde ├── README.md ├── assets └── screen.PNG ├── lib └── core-10.jar └── src └── com └── micycle ├── javafx ├── App.java ├── Controller.java └── ProcessingFX.fxml ├── main └── Main.java └── processing └── MyPApplet.java /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | .idea/ 3 | ProcessingFX.iml -------------------------------------------------------------------------------- /ProcessingFX.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * Stand-alone example of using an FXML file from a Processing .pde sketch. 3 | * Does not rely on the ProcessingFX library. 4 | * 5 | * Load a new scene from an FXML file and replace the the default scene Processing 6 | * makes with this new one (during runtime). 7 | * 8 | * https://discourse.processing.org/t/fxml-file-integrate-in-processing/18325/10 9 | */ 10 | 11 | import java.util.Map; 12 | import java.nio.file.Paths; 13 | 14 | import javafx.application.Platform; 15 | import javafx.fxml.FXMLLoader; 16 | import javafx.scene.Parent; 17 | import javafx.scene.Scene; 18 | import javafx.scene.SceneAntialiasing; 19 | import javafx.scene.canvas.Canvas; 20 | import javafx.scene.layout.AnchorPane; 21 | import javafx.stage.Stage; 22 | 23 | import processing.javafx.PSurfaceFX; 24 | 25 | public void setup() { 26 | size(800, 800, FX2D); 27 | strokeWeight(3); 28 | } 29 | 30 | protected PSurface initSurface() { 31 | surface = (PSurfaceFX) super.initSurface(); 32 | final Canvas canvas = (Canvas) surface.getNative(); 33 | final Scene oldScene = canvas.getScene(); 34 | final Stage stage = (Stage) oldScene.getWindow(); 35 | 36 | try { 37 | FXMLLoader loader = new FXMLLoader(Paths.get("C:\\path--to--fxml\\stage.fxml").toUri().toURL()); // abs path to fxml file 38 | final Parent sceneFromFXML = loader.load(); 39 | final Map namespace = loader.getNamespace(); 40 | 41 | final Scene newScene = new Scene(sceneFromFXML, stage.getWidth(), stage.getHeight(), false, 42 | SceneAntialiasing.BALANCED); 43 | final AnchorPane pane = (AnchorPane) namespace.get("anchorPane"); // get element by fx:id 44 | 45 | pane.getChildren().add(canvas); // processing to stackPane 46 | canvas.widthProperty().bind(pane.widthProperty()); // bind canvas dimensions to pane 47 | canvas.heightProperty().bind(pane.heightProperty()); // bind canvas dimensions to pane 48 | 49 | Platform.runLater(new Runnable() { 50 | @Override 51 | public void run() { 52 | stage.setScene(newScene); 53 | } 54 | } 55 | ); 56 | } 57 | catch (IOException e) { 58 | e.printStackTrace(); 59 | } 60 | return surface; 61 | } 62 | 63 | public void draw() { 64 | background(125, 125, 98); 65 | ellipse(200, 200, 200, 200); 66 | line(0, 0, width, height); 67 | line(width, 0, 0, height); 68 | } 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProcessingFX 2 | ### Description 3 | A basic project showing how a Processing sketch and a JavaFX window (a stage loaded from an *.FXML* file) may be combined/integrated (both as a Java Project and PDE file), and how each of the two components may interact with each other (Java project only). 4 | 5 | ### Extending 6 | If you use this code as inspiration, ensure you download the [*core-10.jar file*](https://github.com/micycle1/ProcessingFX/blob/master/lib/core-10.jar) 7 | and use this in place of your existing Processing library: *core.jar*. 8 | 9 | *core-10.jar* is a modified version of the Processing library in which there have been minor modifications to the *PSurfaceFX* and *PGraphicsFX2D* classes. 10 | The changes allow us to launch Processing embedded within our own *.....extends Application* (JavaFX stage) class, rather than using the stage Processing would otherwise 11 | create during the sketch's initialisation. 12 | 13 | ### Methodology (simple) 14 | Before its call to setup(), a PApplet in FX2D rendering mode creates a JavaFX *stage*, *scene* & *stackPane* and embeds the drawing canvas (a JavaFX *Canvas* object) within the stackPane. 15 | In this example project we circumvent this step (enabled by using *core-10.jar*), creating a customised stage (from loading the FXML file) and then embedding the PApplet's drawing canvas into this stage. 16 | 17 | ## Features 18 | I have included examples of the sketch modifying the UI, and the UI modifying the sketch, so see can see how the two components can interact. 19 | See the headings below for more information about each: 20 | 21 | ### JavaFX UI --> Processing 22 | There are 2 JavaFX sliders and a colour-picker that modify sketch parameters (the colour of the pen). Behind the scenes, these UI elements trigger `@FXML` decorated methods 23 | in the *Controller.java* class, which in turn call *strokeColor()* on the sketch (*myPApplet.class*) to change pen colour. The method in the controller class that each UI element 24 | calls is prescribed with with `onAction` tag, found accompanying each in the *.FXML* file. 25 | 26 | ### Processing --> JavaFX UI 27 | In the example project, the sketch updates 3 text labels found in the *Info* tab of the JavaFX UI: frameCount, frameRate and canvasWidth. 28 | The text label objects are exposed in the *myPApplet* class by looking up the `fx:id` (prescribed in the *.FXML* file) of each element in the scene. 29 | The labels are then updated during the *draw()* loop of the sketch. 30 | 31 | ## Screenshot 32 | The stage is a splitPane, with an *Accordion* on the left and a *StackPane* on the right. 33 | The drawing canvas is added as a node to the *StackPane*. By adding to a stackpane, it is possible to draw JavaFX primitives on top the of the canvas. 34 |

35 | 36 |

37 | -------------------------------------------------------------------------------- /assets/screen.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micycle1/ProcessingFX/eada49cee891978162c1c478853c5fbea4e7823f/assets/screen.PNG -------------------------------------------------------------------------------- /lib/core-10.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micycle1/ProcessingFX/eada49cee891978162c1c478853c5fbea4e7823f/lib/core-10.jar -------------------------------------------------------------------------------- /src/com/micycle/javafx/App.java: -------------------------------------------------------------------------------- 1 | package com.micycle.javafx; 2 | 3 | import javafx.application.Application; 4 | import javafx.fxml.FXMLLoader; 5 | import javafx.scene.Parent; 6 | import javafx.scene.Scene; 7 | import javafx.stage.Stage; 8 | import processing.javafx.PSurfaceFX; 9 | 10 | public class App extends Application { 11 | 12 | public static PSurfaceFX surface; 13 | 14 | @Override 15 | public void start(Stage primaryStage) throws Exception { 16 | 17 | FXMLLoader loader = new FXMLLoader(getClass().getResource("ProcessingFX.fxml")); 18 | Parent root = loader.load(); 19 | Controller.stage = primaryStage; 20 | Scene scene = new Scene(root, 1280, 720); 21 | 22 | primaryStage.setTitle("ProcessingFX Demo"); 23 | primaryStage.setScene(scene); 24 | primaryStage.show(); 25 | 26 | surface.stage = primaryStage; 27 | Controller.stage = primaryStage; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/com/micycle/javafx/Controller.java: -------------------------------------------------------------------------------- 1 | package com.micycle.javafx; 2 | 3 | import com.micycle.processing.MyPApplet; 4 | import javafx.fxml.FXML; 5 | import javafx.fxml.Initializable; 6 | import javafx.scene.canvas.Canvas; 7 | import javafx.scene.control.ColorPicker; 8 | import javafx.scene.control.Slider; 9 | import javafx.scene.layout.AnchorPane; 10 | import javafx.scene.layout.StackPane; 11 | import javafx.stage.Stage; 12 | import processing.javafx.PSurfaceFX; 13 | 14 | import java.net.URL; 15 | import java.util.ResourceBundle; 16 | 17 | /** 18 | * Communicates JavaFX events back to the running PApplet 19 | */ 20 | public class Controller implements Initializable { 21 | 22 | public static PSurfaceFX surface; 23 | public static MyPApplet p; 24 | protected static Stage stage; 25 | 26 | @FXML 27 | AnchorPane superParent; 28 | @FXML 29 | Slider bgBrightness, penSize; 30 | @FXML 31 | StackPane processing; 32 | @FXML 33 | ColorPicker colorPicker; 34 | 35 | @Override 36 | public void initialize(URL location, ResourceBundle resources) { 37 | Canvas canvas = (Canvas) surface.getNative(); 38 | surface.fx.context = canvas.getGraphicsContext2D(); 39 | processing.getChildren().add(canvas); 40 | canvas.widthProperty().bind(processing.widthProperty()); 41 | canvas.heightProperty().bind(processing.heightProperty()); 42 | 43 | penSize.valueProperty().addListener((observable, oldValue, newValue) -> { 44 | p.strokeWeight(newValue.intValue()); 45 | }); 46 | 47 | bgBrightness.valueProperty().addListener((observable, oldValue, newValue) -> { 48 | p.bgColor = newValue.intValue(); 49 | p.redraw(); 50 | }); 51 | } 52 | 53 | @FXML 54 | private void redPen() { 55 | p.stroke(255, 0, 0); 56 | } 57 | 58 | @FXML 59 | private void greenPen() { 60 | p.stroke(0, 255, 0); 61 | } 62 | 63 | @FXML 64 | private void bluePen() { 65 | p.stroke(0, 0, 255); 66 | } 67 | 68 | @FXML 69 | private void exit() { 70 | stage.close(); 71 | } 72 | 73 | @FXML 74 | private void clearCanvas() { 75 | p.redraw(); 76 | } 77 | 78 | @FXML 79 | private void pickColor() { 80 | p.stroke((int) (colorPicker.getValue().getRed() * 255), (int) (colorPicker.getValue().getGreen() * 255), 81 | (int) (colorPicker.getValue().getBlue() * 255)); 82 | } 83 | } -------------------------------------------------------------------------------- /src/com/micycle/javafx/ProcessingFX.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 49 | 51 | 52 | 53 | 55 | 57 | 59 | 60 | 61 | 66 | 71 | 76 | 81 | 87 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 107 | 108 | 110 | 112 | 113 | 114 | 116 | 118 | 120 | 121 | 122 | 127 | 132 | 137 | 139 | 142 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 159 | 160 | 161 | 162 | 167 | 173 | 179 | 185 | 186 | 187 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /src/com/micycle/main/Main.java: -------------------------------------------------------------------------------- 1 | package com.micycle.main; 2 | 3 | import com.micycle.processing.MyPApplet; 4 | import processing.core.PApplet; 5 | 6 | public class Main { 7 | 8 | public static void main(String[] args) { 9 | PApplet.main(MyPApplet.class); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/com/micycle/processing/MyPApplet.java: -------------------------------------------------------------------------------- 1 | package com.micycle.processing; 2 | 3 | import com.micycle.javafx.App; 4 | import com.micycle.javafx.Controller; 5 | import javafx.application.Application; 6 | import javafx.scene.canvas.Canvas; 7 | import javafx.scene.control.Label; 8 | import processing.core.PApplet; 9 | import processing.core.PSurface; 10 | import processing.javafx.PSurfaceFX; 11 | 12 | public class MyPApplet extends PApplet { 13 | 14 | public int bgColor = 255; 15 | private Label framecount, framerate, canvaswidth; 16 | 17 | @Override 18 | protected PSurface initSurface() { 19 | g = createPrimaryGraphics(); 20 | PSurface genericSurface = g.createSurface(); 21 | PSurfaceFX fxSurface = (PSurfaceFX) genericSurface; 22 | fxSurface.sketch = this; 23 | App.surface = fxSurface; // todo remove? 24 | Controller.surface = fxSurface; 25 | 26 | new Thread(() -> Application.launch(App.class)).start(); 27 | 28 | while (fxSurface.stage == null) { 29 | try { 30 | Thread.sleep(5); 31 | } catch (InterruptedException e) { 32 | } 33 | } 34 | 35 | this.surface = fxSurface; 36 | Canvas canvas = (Canvas) surface.getNative(); 37 | framecount = (Label) canvas.getScene().lookup("#frameCount"); 38 | framerate = (Label) canvas.getScene().lookup("#frameRate"); 39 | canvaswidth = (Label) canvas.getScene().lookup("#canvasWidth"); 40 | return surface; 41 | } 42 | 43 | @Override 44 | public void settings() { 45 | size(0, 0, FX2D); 46 | } 47 | 48 | @Override 49 | public void setup() { 50 | Controller.p = this; 51 | background(255); 52 | strokeWeight(5); 53 | } 54 | 55 | @Override 56 | public void draw() { 57 | framecount.setText(String.valueOf(frameCount)); 58 | framerate.setText(String.valueOf(frameRate)); 59 | canvaswidth.setText(String.valueOf(width)); 60 | 61 | } 62 | 63 | @Override 64 | public void mouseDragged() { 65 | line(mouseX, mouseY, pmouseX, pmouseY); 66 | } 67 | 68 | public void redraw() { 69 | background(bgColor); 70 | } 71 | } 72 | --------------------------------------------------------------------------------