I have a JavaFX application that animates robots (black dots) on the screen and draws a small white line against a lighgray background, wherever they are (think Throne). For this, I save all the coordinates of the robots and all white pixels. The behavior of the robots is controlled by another thread that implements Runnable and can be changed during the simulation. The coordinates of the robot are stored in HashMap with the coordinates being a class that extends Point and uses doubles for higher internal calculation accuracy for x and y values. For white points, I use HashMap, because integer accuracy is sufficient for them, since they do not move and remain indefinitely on these x and y coordinates on the screen.
Now the program works very well, but when the HashMap saves dots for white dots, it becomes more likely to destroy the flow of the JavaFX application (think about it, more precisely, this is just the canvas where the robots are drawn.) The sliders of the controls remain responsive, and the text fields for iterations and HashMap sizes continue to be updated. But nothing comes to life, and after a few seconds the canvas turns white. Increasing ms for Thread.sleep (ms) makes the program more stable, but it is painfully slow as it already is. It also happens more often and faster on my slow school netbook (running Win XP) than on my home desktop PC (running Win7 64Bit). An exception is also literal. For desktop, this is the following:
java.lang.InternalError: Unrecognized PGCanvas token: 68
at com.sun.javafx.sg.prism.NGCanvas.renderStream(NGCanvas.java:651)
at com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:320)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:187)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:39)
at com.sun.javafx.sg.BaseNode.render(BaseNode.java:1145)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:204)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:420)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:187)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:39)
at com.sun.javafx.sg.BaseNode.render(BaseNode.java:1145)
at com.sun.javafx.tk.quantum.ViewPainter.doPaint(ViewPainter.java:117)
at com.sun.javafx.tk.quantum.AbstractPainter.paintImpl(AbstractPainter.java:175)
at com.sun.javafx.tk.quantum.PresentingPainter.run(PresentingPainter.java:73)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:351)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:178)
at com.sun.prism.render.RenderJob.run(RenderJob.java:37)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:98)
at java.lang.Thread.run(Thread.java:724)
For a netbook, this is:
java.lang.IllegalArgumentException: alpha value out of range
at java.awt.AlphaComposite.<init>(AlphaComposite.java:624)
at java.awt.AlphaComposite.getInstance(AlphaComposite.java:689)
at java.awt.AlphaComposite.derive(AlphaComposite.java:761)
at com.sun.prism.j2d.J2DPrismGraphics.setExtraAlpha(J2DPrismGraphics.java:569)
at com.sun.javafx.sg.prism.NGCanvas.renderStream(NGCanvas.java:739)
at com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:389)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:201)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:40)
at com.sun.javafx.sg.BaseNode.render(BaseNode.java:1145)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:204)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:420)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:201)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:40)
at com.sun.javafx.sg.BaseNode.render(BaseNode.java:1145)
at com.sun.javafx.tk.quantum.ViewPainter.doPaint(ViewPainter.java:117)
at com.sun.javafx.tk.quantum.AbstractPainter.paintImpl(AbstractPainter.java:182)
at com.sun.javafx.tk.quantum.PresentingPainter.run(PresentingPainter.java:73)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:351)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:178)
at com.sun.prism.render.RenderJob.run(RenderJob.java:37)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:108)
at java.lang.Thread.run(Thread.java:722)
Any help to correct or narrow this problem would be greatly appreciated.
:
, , . Visualizer,
import java.awt.Point;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class Visualizer extends Application {
private static GraphicsContext gc;
private static Canvas canvas;
private static BorderPane pane;
private static Scene scene;
private static Thread thread;
private static Simulator sim = new Simulator();
private static int optionsWidth = 200;
private static HashSet<Point> manganCollected = new HashSet<Point>();
private static HashMap<Integer, Coordinates> coordinates = new HashMap<Integer, Coordinates>();
private static final int zoom = 4;
private static Point cmpP;
public static double simHeight;
public static double simWidth;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
Screen screen = Screen.getPrimary();
Rectangle2D bounds = screen.getVisualBounds();
double screenHeight = bounds.getHeight();
double screenWidth = bounds.getWidth();
stage.setFullScreen(true);
stage.setHeight(screenHeight);
stage.setWidth(screenWidth);
stage.setTitle("Manganernte");
canvas = new Canvas();
simHeight = screenHeight;
simWidth = screenWidth - optionsWidth;
canvas.setHeight(simHeight);
canvas.setWidth(simWidth);
gc = canvas.getGraphicsContext2D();
gc = canvas.getGraphicsContext2D();
gc.setFill(Color.LIGHTGRAY);
gc.fillRect(0, 0, simWidth, simHeight);
gc.setStroke(Color.BLACK);
gc.setLineWidth(3);
gc.strokeRect(0, 0, simWidth, simHeight);
pane = new BorderPane();
pane.setCenter(canvas);
scene = new Scene(pane);
stage.setScene(scene);
stage.show();
thread = new Thread(sim);
thread.start();
}
private static Coordinates transform(Coordinates coordinates) {
return new Coordinates(Math.round((simWidth / 2) + (zoom * coordinates.getX())), Math.round((simHeight / 2) + (zoom * coordinates.getY())));
}
private static Point transform(Point point) {
return new Point((int)Math.round((simWidth / 2) + (zoom * point.getX())), (int)Math.round(((simHeight / 2) + (zoom * point.getY()))));
}
private static void clear () {
gc.setFill(Color.LIGHTGRAY);
gc.fillRect(0, 0, simWidth, simHeight);
gc.setStroke(Color.BLACK);
gc.setLineWidth(3);
gc.strokeRect(0, 0, simWidth, simHeight);
}
public static void DrawRobots() {
coordinates = Simulator.coordinates;
manganCollected = Simulator.manganCollected;
clear();
gc.setFill(Color.WHITE);
Iterator<Point> it = manganCollected.iterator();
while(it.hasNext()) {
cmpP = it.next().getLocation();
double x = transform(cmpP).getX();
double y = transform(cmpP).getY();
gc.fillRect(x, y, zoom, zoom);
}
gc.setFill(Color.BLACK);
for(int i = 1; i <= coordinates.size(); i++) {
double x = transform(coordinates.get(i)).getX();
double y = transform(coordinates.get(i)).getY();
gc.fillOval(x, y, zoom, zoom);
}
}
}
:
import java.awt.Point;
import java.util.HashMap;
import java.util.HashSet;
public class Simulator implements Runnable {
private static HashMap<Integer, Robot> robots = new HashMap<Integer, Robot>();
public static HashMap<Integer, Coordinates> coordinates = new HashMap<Integer, Coordinates>();
public static HashSet<Point> manganCollected = new HashSet<Point>();
public static int processSpeed = 100;
public static void createRobot(int x, int y) {
coordinates.put(coordinates.size() + 1, new Coordinates(x, y));
robots.put(robots.size() + 1, new Robot());
}
@Override
public void run() {
for(int i = 0; i < 100; i++) createRobot(i - 50, 0);
Visualizer.DrawRobots();
while(true) {
for(int i = 1; i <= robots.size(); i++) robots.get(i).think();
for(int i = 1; i <= robots.size(); i++) {
coordinates.get(i).add(robots.get(i).move());
manganCollected.add(new Point((int)Math.round(coordinates.get(i).getX()), (int)Math.round(coordinates.get(i).getY())));
}
Visualizer.DrawRobots();
try {
Thread.sleep(processSpeed);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
, , , :
public class Robot {
Coordinates future = new Coordinates(0, 0);
public void think () {
future.setY(1.0);
}
public Coordinates move() {
return future;
}
}
UPDATE
JDK 8, :
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
, Platform.runLater(), . , .
, :
Class Visualizer, , main() , , GraphicsContext. Simulator , Runnable. run() Simulator while (true). , , thread.sleep(). "", , Visualizer . , , , JavaFX.
runLater(), , , ?
2
, ( ), , - UI/Canvas runLater, .
public static void drawStuff() {
Platform.runLater(new Runnable() {
@Override
public void run() {
}
});
}