JavaFX Shape.intersect() performance issue - java

I have start writing a shooting game JavaFX application. I am using Shape.intersect() to check the collision of bullet and the target. Below is my code and I made it simple so as to post here.
public class TestShapeIntersect extends Application{
AnchorPane anchorPane;
ArrayList<Rectangle> targetObjects;
public static void main(String[] arg){
launch(arg);
}
#Override
public void start(Stage stage) throws Exception {
final Rectangle gun = new Rectangle(50, 50, Color.RED);
anchorPane = new AnchorPane();
anchorPane.getChildren().add(gun);
generateTargetObjects(50); // Number of target objects
anchorPane.getChildren().addAll(targetObjects);
gun.setX(50);
gun.setY(200);
Scene scene = new Scene(anchorPane,300,300,Color.GREEN);
stage.setScene(scene);
stage.show();
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
Rectangle bullet = new Rectangle(5,10,Color.ORANGE);
bullet.setX(75);
bullet.setY(200);
anchorPane.getChildren().add(bullet);
animateBullet(bullet);
}
});
}
private void generateTargetObjects(int noOfTargetObj) {
targetObjects = new ArrayList<Rectangle>();
for(int i=1; i<=noOfTargetObj;i++){
Rectangle rect = new Rectangle(30, 30, Color.YELLOW);
targetObjects.add(rect);
}
}
void animateBullet(final Rectangle bullet){
Timeline timeline = new Timeline();
timeline.setCycleCount(500);
final KeyFrame kf = new KeyFrame(Duration.millis(2), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
bullet.setY(bullet.getY()-1);
checkCollision(bullet);
}
});
timeline.getKeyFrames().add(kf);
timeline.play();
}
//This method will check if there is any collision happened between the bullets and the targets.
//If collision happens then both bullet and target object will be disappeared.
void checkCollision(Rectangle bullet){
int noOfTargetObjs = targetObjects.size();
for(int i=0; i<noOfTargetObjs;i++)
{
if(targetObjects.get(i).isVisible()==true && bullet.isVisible()==true){
Shape intersectShape= Shape.intersect(bullet, targetObjects.get(i));
if(intersectShape.getBoundsInLocal().getWidth() != -1){
targetObjects.get(i).setVisible(false);
bullet.setVisible(false);
}
}
}
}
}
I have not yet aligned the nodes properly.Here the 'gun' rectangle will fire 'bullet' rectangle whenever any key press event is detected.
The problem is for every every first bullet fired in each application session, the very first bullet is not animated properly (means the bullet is not going in it path continuously). But after the first bullet has gone the remaining bullets are animated properly. This performance issue increases with the number of 'target' objects increases.
I have found out that the issue is because of this line:
Shape intersectShape= Shape.intersect(bullet, targetObjects.get(i));
Could anyone let me know why this happens and what could be the solution to resolve this issue? Or is it because of the way that I'm implementing?

I experienced a different behaviour when i executed your application. My first shot was moving fine without any interruptions in the translation. But after several shots the application began to slow down. I tried to improve the performance of your code by doing the following steps:
void animateBullet(final Rectangle bullet){
final Timeline timeline = new Timeline();
timeline.setCycleCount(125); //changed
final KeyFrame kf = new KeyFrame(Duration.millis(16), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
bullet.setY(bullet.getY()-8); //changed
checkCollisionThreaded(bullet); //changed
//added
if(bullet.getX() < 0 || bullet.getX() > bullet.getParent().getBoundsInParent().getWidth()
|| bullet.getY() < 0 || bullet.getY() > bullet.getParent().getBoundsInParent().getHeight())
{
bullet.setVisible(false);
timeline.stop();
AnchorPane ap = (AnchorPane) bullet.getParent();
ap.getChildren().remove(bullet);
}
}
});
timeline.getKeyFrames().add(kf);
timeline.play();
}
Your value for the Duration.millis factor in the KeyFrame was 2, which is not really necessary to run a fluent animation, because JavaFX has a fixed framerate of 60 frame per second, which means that every 16,7 milliseconds a new frame is rendered and displayed. So you can use 16ms as frame-duration without making the animation stutter.
The if-statement checks if the bullet is outside the visible screen, which could happen in your previous code. Non-visible nodes should be removed from the scene graph. It doesn't help if you set a node to setVisible(false), because the node will stay on the scene graph. The Timeline animation should also be stopped, because it would trigger new checkCollision calls. As you can see, i changed the method checkCollision to checkCollisionThreaded. The method is shown below.
public void checkCollisionThreaded(final Rectangle bullet)
{
final int noOfTargetObjs = targetObjects.size();
Task<Integer> t = new Task<Integer>()
{
#Override
protected Integer call() throws Exception
{
for(int i=0; i<noOfTargetObjs;i++)
{
if(targetObjects.get(i).isVisible()==true && bullet.isVisible()==true){
Shape intersectShape= Shape.intersect(bullet, targetObjects.get(i));
if(intersectShape.getBoundsInLocal().getWidth() != -1){
return i;
}
}
}
return -1;
}
#Override
protected void succeeded()
{
super.succeeded();
if(this.getValue().intValue() != -1)
{
Node obj = targetObjects.get(this.getValue().intValue());
obj.setVisible(false);
AnchorPane ap = (AnchorPane) obj.getParent();
ap.getChildren().remove(obj);
targetObjects.remove(this.getValue().intValue());
bullet.setVisible(false);
}
}
};
Thread thread = new Thread(t);
thread.start();
}
There are some violations against the rule "don't touch any objects on the scene graph with a thread different to the JavaFX application thread", but as far as i can see, only reading methods access the scene graph (and it's objects) in the call() method. This method is run on a new Thread, which improves performance. The method succeeded() is run on the JavaFX Application Thread, so that we can safely remove things from our scene graph. I assumed that you want to remove your targets from the scene once they were hit.
It should be said that there might be issues related to the multithreaded code. There could be errors when getting final int noOfTargetObjs = targetObjects.size(); while modifying it on another thread. I left out any synchronization to reduce the complexity of the code.

My guess is that you're sending way too many requests to Shape.intersect(...), which is probably a fairly expensive method to execute. Initially this is causing performance problems, but when the number of calls to the method hits some threshold, the JVM's JIT compiler kicks in and compiles that method, relieving some of the problems. (Again, this is all guesswork.)
Using a TranslateTransition for the bullet and listening to its boundsInParent property to check for collisions seems to work better. I think the reason is that using this technique only checks for collisions when the JavaFX machinery actually moves the bullet. In your code you are performing these checks much more often.
Here's an example.

Related

JavaFX: Moving hundreds of ImageViews simultaneously inside a Pane (Performance problems)

I am currently developing a tower defence game for a university project.
At higher waves there are hundreds of enemies moving arround. My problem is that it's getting very inperforment at about 300+ enemies moving simultaneously.
Every enemy is a children of a Pane which is displayed in my scene.
I've got a method in my EnemyGraphics class which updates the positon by calling the update method:
public class EnemyGraphics extends ImageView implements EventObserver {
...
#Override
public void update() {
Platform.runLater(() -> {
relocate(enemy.getxCoordinate(), enemy.getyCoordinate());
});
}
}
I guess it's getting laggy because every enemy is updating its location on its own every time it moves by calling update().
Is there a way that I can set new coordinates for my ImageView object, without redrawing the scene and in the main FX-Thread creating a timer which redraws the entire scene in a certain interval? Or is there a other solution / method I can call to move images performantly over the pane?
Ok I've found my mistake. Everytime my enemyLogic class compute a new position it called the enemyGraphic object to update its position. During testing I've removed the functionality of the method in the enemyGraphics class but not the call.
To update the postions of all the enemys I've written a method in my GUIcontroller class:
private void startUpdateTicker() {
final AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long timestamp) {
updateEnemies();
}
};
timer.start();
}
public synchronized void updateEnemies() {
for (EnemieGUI enemy : enemyList) {
enemy.relocate(enemy.getEnemie().getxCoordinate(), enemy.getEnemie().getyCoordinate());
}
}

Mouse release event doesn't happen on Ubuntu when target Node changes scene?

This question deals with mouse behavior across operating systems; specifically, my code works on Windows and Mac OS X, but not on Ubuntu.
Ultimately what I am trying to do is make a special Pane subclass ("ConvertiblePane") that exists within a parent Pane on a main stage/scene, but is magically transferred into its own temporary stage/scene when dragged, thus becoming independent and able to be placed anywhere on the screen. When the user releases the mouse button, the ConvertiblePane should drop back onto its original parent Pane and lose the temporary stage. (In my full program, the original parent Stage resizes/repositions itself to accommodate the ConvertiblePane wherever it is dropped.)
This brings me to my issue. When I press the mouse on the ConvertiblePane, it triggers a MousePress within the main scene as expected, at which point the ConvertiblePane is moved to the temporary stage. As I drag the mouse, it triggers MouseDrag within the temporary scene and moves the temp stage. Ok, great.
When I release the mouse button, however, I experience different behavior on different operating systems. On Windows (7) and Mac OS X (10.12.6), a MouseRelease occurs in the temp scene, sending the pane back to its original parent in the main stage as expected. On Ubuntu, however, no MouseRelease seems to be generated in either the main scene or the temp scene.
Here's the relevant code as an MCV example:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class ConvertibleTest extends Application {
#Override
public void start(Stage primaryStage) {
// Set up the main stage and scene with a
// Pane as root and a ConvertiblePane child:
primaryStage.initStyle(StageStyle.TRANSPARENT);
Pane root = new Pane();
ConvertiblePane conv = new ConvertiblePane();
root.getChildren().add(conv);
Scene scene = new Scene(root, 400, 400, Color.PINK);
primaryStage.setTitle("Convertible Test");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class ConvertiblePane extends Pane
{
private final Group TEMP_ROOT = new Group();
private final Stage TEMP_STAGE = new Stage(StageStyle.TRANSPARENT);
private final Scene TEMP_SCENE = new Scene(TEMP_ROOT);
private Pane originalParent = null;
private double deltaX = 0.0;
private double deltaY = 0.0;
private String name = null;
public void onMousePress(MouseEvent event)
{
// Save deltaX/Y for later:
Point2D delta = this.sceneToLocal(event.getX(), event.getY());
deltaX = delta.getX();
deltaY = delta.getY();
if (!isIndependent())
{
makeIndependent();
}
}
public void onMouseDrag(MouseEvent event)
{
// Keep the TEMP_STAGE relative to the original click point:
TEMP_STAGE.setX(event.getScreenX()-deltaX);
TEMP_STAGE.setY(event.getScreenY()-deltaY);
}
public void onMouseRelease(MouseEvent event)
{
if (isIndependent())
{
returnToParent();
}
}
public ConvertiblePane()
{
this.setPrefSize(100, 100);
this.setBackground(new Background(new BackgroundFill(Color.GREEN, new CornerRadii(10), Insets.EMPTY)));
this.setVisible(true);
// Attach event code and report to System.out what is happening:
this.setOnMousePressed((MouseEvent event) -> {
if (this.getScene() == TEMP_SCENE)
System.out.println("Pressed as Independent");
else
System.out.println("Pressed as Child");
onMousePress(event);
});
this.setOnMouseDragged((MouseEvent event) -> {
if (this.getScene() == TEMP_SCENE)
System.out.println("Dragged as Independent");
else
System.out.println("Dragged as Child");
onMouseDrag(event);
});
this.setOnMouseReleased((MouseEvent event) -> {
if (this.getScene() == TEMP_SCENE)
System.out.println("Released as Independent");
else
System.out.println("Released as Child");
onMouseRelease(event);
});
}
public boolean isIndependent()
{
// Return whether this ConvertiblePane is "independent" (exists in its own temp scene)
return this.getScene() == TEMP_SCENE;
}
public void makeIndependent()
{
// Get the point where this ConvertiblePane appears on screen:
Point2D screenPt = this.localToScreen(0, 0);
// Save the originaParent of this ConvertiblePane; we will return to it later:
originalParent = (Pane)getParent();
// Remove this ConvertiblePane from its originalParent:
originalParent.getChildren().remove(this);
// Set this ConvertiblePane as the root of the TEMP_SCENE on the TEMP_STAGE:
TEMP_SCENE.setRoot(this);
TEMP_STAGE.setScene(TEMP_SCENE);
System.out.println("Transferred to TEMP.");
this.relocate(0, 0);
// Show the TEMP_STAGE in the same location on screen where this ConvertiblePane originally was:
TEMP_STAGE.setX(screenPt.getX());
TEMP_STAGE.setY(screenPt.getY());
TEMP_STAGE.show();
}
public void returnToParent()
{
// Reset deltas:
deltaX = 0;
deltaY = 0;
// Get the location of this ConvertiblePane on screen:
Point2D screenPt = this.localToScreen(0, 0);
// Set TEMP_ROOT as the root of TEMP_SCENE; this will allow us to detach
// this ConvertiblePane from being the scene root (since root cannot == null).
TEMP_SCENE.setRoot(TEMP_ROOT);
// Hide the TEMP_STAGE:
TEMP_STAGE.hide();
// Add this ConvertiblePane back to the originalParent:
originalParent.getChildren().add(this);
System.out.println("Transferred to MAIN.");
// Relocate this ConvertiblePane within the originalParent to maintain its position on screen
Point2D parentPt = originalParent.screenToLocal(screenPt);
this.relocate(parentPt.getX(), parentPt.getY());
}
}
As you can see, there is some basic reporting in the event handling methods; and the makeIndependent() and returnToParent() methods output "Transferred to TEMP." and "Transferred to MAIN." respectively.
If I click the mouse on the ConvertiblePane, drag a few pixels, and release it, this is the output:
(on Windows or Mac OS X)
Pressed as Child
Transferred to TEMP.
Dragged as Independent
Dragged as Independent
Dragged as Independent
Released as Independent
Transferred to MAIN.
(on Ubuntu)
Pressed as Child
Transferred to TEMP.
Dragged as Independent
Dragged as Independent
Dragged as Independent
I have also tried adding event filters to the two Scenes; but the result is the same: MouseRelease occurs on Win/Mac, but not Ubuntu.
If anyone can explain this behavior, or suggest something, that would be great. Alternatively... is there any "global" (pre-scene) creation of MouseEvents that I could catch? I mean, I don't really care about the details of the mouse release; I just want an event to tell me when to add the ConvertiblePane back to the main stage.
Thanks!
After spending several weeks on this, I couldn't find a way to fire a proper MouseReleased event on Ubuntu for this situation; however, I did come up with a hack that does the job well enough. Basically, instead of being notified when MouseReleased occurs, I'm checking every 10 milliseconds to see whether the mouse button is no longer down.
Explanation: When the node is transferred to the temporary Scene, the Timeline is started to "move" the mouse pointer in place every 10 milliseconds. This triggers either a MouseDragged event (if the mouse button is still down) or a MouseMoved event (if the mouse button is up); so I can then simulate the MouseReleased event and call my procedure for adding the node back to the main stage. At that point, of course, I also stop the Timeline.
Here's the relevant code to demonstrate this; perhaps it will be of use to someone else as well.
// The robot is needed to "move" the mouse in place,
// triggering a MOUSE_MOVED event.
private static Robot robot = null;
static {
try {
robot = new Robot();
} catch (AWTException ex) {
Logger.getLogger(ConvertiblePane.class.getName()).log(Level.SEVERE, null, ex);
}
}
// clickWaiter will move the mouse in place every 10 milliseconds,
// triggering a MOUSE_MOVED event if the mouse is no longer pressed.
private final Timeline clickWaiter = new Timeline(new KeyFrame(Duration.millis(10), (ActionEvent event) -> {
// Get the mouse position from MouseInfo class.
Point mouse = MouseInfo.getPointerInfo().getLocation();
// "Move" the mouse in place to trigger a mouseMove or mouseDrag event.
robot.mouseMove(mouse.x, mouse.y);
}));
public ConvertiblePane()
{
...
// MOUSE_MOVED will be triggered by the robot when the mouse button is no longer pressed:
TEMP_SCENE.addEventFilter(MouseEvent.MOUSE_MOVED, (MouseEvent event) ->
{
if (!event.isPrimaryButtonDown())
{
System.out.println("Temp.release (sim)");
clickWaiter.stop();
// Simulate MOUSE_RELEASED event.
}
});
}
public void makeIndependent()
{
...
// Start the clickWaiter, part of the Linux hack:
clickWaiter.playFromStart();
}

Making a game in JavaFX8. How can I switch from Splash page to Cutscene to Level?

this is my first game using JavaFX so I admittedly have made some bad design decisions, probably.
Anyway, I want to transition from a splash page (Splash class) to a cutscene (Cutscene class) and then to a playable level (PlayableLevel class). The game is launched from my Main class and transitions are supposed to be done with keyboard inputs (Enter button).
The method in Main that starts the game looks like this:
public void start (Stage s) {
// create your own game here
Splash splashPage = new Splash();
Cutscene cs = new Cutscene();
PlayableLevel play = new PlayableLevel();
// attach game to the stage and display it
Scene scene0 = splashPage.init(SIZE, SIZE);
Scene scene1 = cs.init(SIZE, SIZE, 0);
Scene scene2 = play.init(SIZE, SIZE, 0);
s.setScene(scene0);
s.show();
// sets the game's loop
KeyFrame frame = new KeyFrame(Duration.millis(MILLISECOND_DELAY),
e -> myGame.step(SECOND_DELAY));
Timeline animation = new Timeline();
animation.setCycleCount(Timeline.INDEFINITE);
animation.getKeyFrames().add(frame);
animation.play();
}
My question in particular is what should I do to make it so that the Splash class can communicate with the Main class so that once a keystroke event is recorded, the stage can set a new scene? I'm currently reading about EventHandlers but I'm unsure of the exact implementation as of now.
EDIT: One idea I had was to make a linked list of Scenes, and then once some event happens (keystroke), then I would set the scene to the next one in the list.
You could do something like this:
public class Splash {
private Runnable nextSceneHandler ;
public void setNextSceneHandler(Runnable handler) {
nextSceneHandler = handler ;
}
public Scene init(double width, double height) {
Scene scene = new Scene();
// Just an example handler, you could do the same for
// button events, menus, etc., or even just handlers for the
// end of an animation
scene.setOnKeyPressed(e -> {
if (nextSceneHandler != null) {
if (e.getCode() == ...) {
nextSceneHandler.run();
}
}
}
// existing code...
}
// existing code ...
}
and similarly for CutScene.
Then
public void start (Stage s) {
// create your own game here
Splash splashPage = new Splash();
Cutscene cs = new Cutscene();
PlayableLevel play = new PlayableLevel();
// attach game to the stage and display it
Scene scene0 = splashPage.init(SIZE, SIZE);
Scene scene1 = cs.init(SIZE, SIZE, 0);
Scene scene2 = play.init(SIZE, SIZE, 0);
splashPage.setNextSceneHandler(() -> s.setScene(scene1));
cs.setNextSceneHandler(() -> s.setScene(scene2));
s.setScene(scene0);
s.show();
// sets the game's loop
KeyFrame frame = new KeyFrame(Duration.millis(MILLISECOND_DELAY),
e -> myGame.step(SECOND_DELAY));
Timeline animation = new Timeline();
animation.setCycleCount(Timeline.INDEFINITE);
animation.getKeyFrames().add(frame);
animation.play();
}
Your linked list idea should work too. You need a mechanism for passing the linked list instance (or perhaps an iterator from it) to each of the scene generating classes; their event handlers would execute code like
scene.getWindow().setScene(sceneIterator.next());
I kind of prefer setting the runnable on the objects, as it feels a little more flexible. Just a question of style though.

Java / JavaFX getting around final or effectively final, is this a good approach?

this feels like I am cheating or doing something wrong. I am a Java student working on a simple JavaFX project.
As I loop through and create buttons in a flowPane, I was having trouble using the loop counter i inside an inner class. It's the part where I assign event handlers. I have dealt with this issue before, I get the difference between "final" and "effectively final" so I don't believe I am asking that.
It's just that creating this copy of i by using "int thisI = i" just feels wrong, design-wise. Is there not a better way to do this? I looked into lambdas and they also have the "final or effectively final" requirement.
Here's my code, any level or criticism or suggestion for improvement is welcome, thanks!
private FlowPane addFlowPaneCenter() {
FlowPane flow = new FlowPane();
flow.setPadding(new Insets(0, 0, 0, 0));
flow.setVgap(0);
flow.setHgap(0);
flow.setPrefWrapLength(WIDTH_OF_CENTER); // width of function buttons
Button centerButtons[] = new Button[NUM_BUTTONS];
ImageView centerImages[] = new ImageView[NUM_BUTTONS];
for (int i=0; i < NUM_BUTTONS; i++) {
centerImages[i] = new ImageView(
new Image(Calculator.class.getResourceAsStream(
"images/button-"+(i)+".png")));
centerButtons[i] = new Button();
centerButtons[i].setGraphic(centerImages[i]);
centerButtons[i].setPadding(Insets.EMPTY);
centerButtons[i].setId("button-"+(i));
flow.getChildren().add(centerButtons[i]);
// add a drop shadow on mouseenter
DropShadow shadow = new DropShadow();
// ***** here's the workaround is this really a good approach
// to use this in the inner class instead of i? thanks *****
int thisI = i;
// set event handlers for click, mousein, mouseout
centerButtons[i].setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
// change graphic of button to down graphic
ImageView downImage = new ImageView(new
Image(Calculator.class.getResourceAsStream(
"images/button-"+(thisI)+"D.png")));
// call function to effect button press
System.out.println("Button click");
// change graphic back
centerButtons[thisI].setGraphic(centerImages[thisI]);
}});
centerButtons[i].addEventHandler(MouseEvent.MOUSE_ENTERED,
new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent e) {
centerButtons[thisI].setEffect(shadow);
}
});
centerButtons[i].addEventHandler(MouseEvent.MOUSE_EXITED,
new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent e) {
centerButtons[thisI].setEffect(null);
}
});
}
return flow;
}
You can remove the arrays centerButtons and centerImages completely. Instead create local variables for the image and the button within the loop and use those, e.g.
final ImageView image = new ImageView(...);
final Button button = new Button();
button.setGraphic(centerImages[i]);
...
You can use the local variables in your eventhandlers, e.g.
button.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
...
// change graphic back
button.setGraphic(image);
}});
Two minor improvements I noticed:
Try to avoid creating an Image more than once, because every time you create an Image, the actual data will be loaded again. Your handler will create a new Image for each click. I usually create Images in static final fields.
Event handlers are a nice opportunity to practice lambda expressions. :)

How to pass data from java program to javafx application chart?

I am new to JavaFX but I am not new to Java. I have a big complex system that produces some results in a loop. What I am trying to achieve is to plot the results of each iteration on a JavaFX chart. I was doing this with no problem with the java jFreeChart libraries, but now I am trying to switch to JavaFX. Charts looks more fancy and I like the way style is handled. Anyway, I am struggling in trying to understand how to add points to a XYChart.Series object in a JavaFX application. All tutorials on the oracle website start with some fixed points that the application knows a-priori and they are added using something like:
`series.getData().add(new XYChart.Data(1, 23));`
But what I am trying to achieve is a bit different. In my case my application produces some results and as soon as they are produced (random time) I want to plot them on a chart.
I launch a thread with the javafx.application.Application, but when I try to add some points to the Series object I get a
java.lang.IllegalStateException: Not on FX application thread; currentThread = main
exception.
What is the correct way to pass data points to a JavaFX chart? I thought that the closest way to do this is to override the Event type, Event object and create a whole Event handling structure... but this looks way too complicated for the simple thing I am trying to archive!
Can you please tell me, in your opinion, what is the best/simplest way for doing this?
EDIT:
Here is some code for you to have a look and give me some advice:
public class Chart extends Application {
private final static XYChart.Series series = new XYChart.Series();
public static void addValue(double gen, double val) {
series.getData().add(new XYChart.Data(gen, val));
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Chart");
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
final LineChart<Number,Number> lineChart =
new LineChart<Number,Number>(xAxis,yAxis);
//defining training set series
series.setName("Training");
Scene scene = new Scene(lineChart, 800, 600);
lineChart.getData().add(series);
primaryStage.setScene(scene);
primaryStage.show();
}
}
class Launcher extends Thread {
#Override
public void run() {
Application.launch(Chart.class);
}
public static void main(String[] args) throws InterruptedException {
new Launcher().start();
System.out.println("Now doing something else...");
for (int i = 0; i < 1000; i++) {
double trainValue = Math.random();
Chart.addValue(i, trainValue);
Thread.sleep(500);
}
}
}
to be sure that the code to add points to the chart is executed within the JavaFX thread
you could add it into a Runnable-Object which gets executed on the JavaFX thread:
Platform.runLater(new Runnable() {
#Override
public void run() {
// code to add points to the chart
}
});
Hope this helps.
Torsten

Categories