I um JavaFX zu üben, baute ich eine einfache App, die Sierpinski Triangles zeichnet.Timing JavaFX Canvas-Anwendung
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class SierpinskiTriangles extends Application {
private final int PADDING = 5;
private static int numberOfLevels;
public static void launch(String... args){
numberOfLevels = 8;
if((args != null) && (args.length > 0)) {
int num = -1;
try {
num = Integer.parseInt(args[0]);
} catch (NumberFormatException ex) {
ex.printStackTrace();
return;
}
numberOfLevels = (num > 0) ? num : numberOfLevels;
}
Application.launch(args);
}
@Override
public void start(Stage stage) {
stage.setOnCloseRequest((ae) -> {
Platform.exit();
System.exit(0);
});
stage.setTitle("Sierpinski Triangles (fx)");
BorderPane mainPane = new BorderPane();
mainPane.setPadding(new Insets(PADDING));
Pane triPanel = new Triangles();
BorderPane.setAlignment(triPanel, Pos.CENTER);
mainPane.setCenter(triPanel);
Scene scene = new Scene(mainPane);
stage.setScene(scene);
stage.centerOnScreen();
stage.setResizable(false);
stage.show();
}
class Triangles extends AnchorPane{
private static final int PANEL_WIDTH =600, PANEL_HEIGHT = 600;
private static final int TRI_WIDTH= 500, TRI_HEIGHT= 500;
private static final int SIDE_GAP = (PANEL_WIDTH - TRI_WIDTH)/2;
private static final int TOP_GAP = (PANEL_HEIGHT - TRI_HEIGHT)/2;
private int countTriangles;
private long startTime;
private Point2D top, left, right;
private Canvas canvas;
private GraphicsContext gc;
Triangles(){
setPrefSize(PANEL_WIDTH, PANEL_HEIGHT);
canvas = getCanvas();
gc = canvas.getGraphicsContext2D();
getChildren().add(canvas);
draw(numberOfLevels);
}
void draw(int numberLevels) {
Platform.runLater(new Runnable() {
@Override
public void run() {
clearCanvas();
setStartPoints();
startTime = System.currentTimeMillis();
countTriangles = 0;
RunTask task = new RunTask(numberLevels, top, left, right);
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
});
}
private void drawTriangle(int levels, Point2D top, Point2D left, Point2D right) {
if(levels < 0) {//add stop criteria
return ;
}
gc.strokePolygon(//implementing with strokeLine did not make much difference
new double[]{
top.getX(),left.getX(),right.getX()
},
new double[]{
top.getY(),left.getY(), right.getY()
},3
);
countTriangles++;
//Get the midpoint on each edge in the triangle
Point2D p12 = midpoint(top, left);
Point2D p23 = midpoint(left, right);
Point2D p31 = midpoint(right, top);
// recurse on 3 triangular areas
drawTriangle(levels - 1, top, p12, p31);
drawTriangle(levels - 1, p12, left, p23);
drawTriangle(levels - 1, p31, p23, right);
}
private void setStartPoints() {
top = new Point2D(getPrefWidth()/2, TOP_GAP);
left = new Point2D(SIDE_GAP, TOP_GAP + TRI_HEIGHT);
right = new Point2D(SIDE_GAP + TRI_WIDTH, TOP_GAP + TRI_WIDTH);
}
private Point2D midpoint(Point2D p1, Point2D p2) {
return new Point2D((p1.getX() + p2.getX())/
2, (p1.getY() + p2.getY())/2);
}
private void updateGraphics(boolean success){
if(success) {
gc.fillText("Number of triangles: "+ countTriangles,5,15);
gc.fillText("Time : "+ (System.currentTimeMillis()- startTime)+ " mili seconds", 5,35);
gc.fillText("Levels: "+ numberOfLevels,5,55);
}
System.out.println("Completed after: "+
(System.currentTimeMillis()- startTime)+ " mili seconds"
+" Triangles: " + countTriangles +" Failed: "+ !success);
}
private Canvas getCanvas() {
Canvas canvas = new Canvas();
canvas.widthProperty().bind(widthProperty());
canvas.heightProperty().bind(heightProperty());
canvas.getGraphicsContext2D().setStroke(Color.RED);
canvas.getGraphicsContext2D().setLineWidth(0.3f);
return canvas;
}
private void clearCanvas() {
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
}
class RunTask extends Task<Void>{
private int levels;
private Point2D top, left;
private Point2D right;
RunTask(int levels, Point2D top, Point2D left, Point2D right){
this.levels = levels;
this.top = top;
this.left = left;
this.right = right;
startTime = System.currentTimeMillis();
countTriangles = 0;
}
@Override public Void call() {
drawTriangle(levels,top, left, right);
return null;
}
@Override
protected void succeeded() {
updateGraphics(true);
super.succeeded();
}
@Override
protected void failed() {
updateGraphics(false);
}
}
}
public static void main(String[] args) {
launch("13");
}
}
Die Probleme, die ich habe:
ein. Der Zeitausdruck bei updateGraphics()
zeigt lang vor (8 Sekunden auf meiner Maschine) ist das Zeichnen der Dreiecke abgeschlossen, daher misst es nicht den kompletten Prozess. Wie verbessere ich es?
b. Auf meinem Gerät dauert es 30-35 Sekunden, bis das Panel vollständig gezeichnet ist. Eine ähnliche Swing-Anwendung dauert 4 Sekunden. Es könnte bedeuten, dass etwas grundsätzlich falsch mit meiner Javafx-Implementierung ist.
Danke, dass Sie sich die Zeit nehmen sie im Detail zu erklären. Ich werde es studieren und versuchen zu implementieren. – c0der
Leider löst der Snapshot " ' IllegalStateException "aus, wenn diese Methode für einen anderen Thread als den JavaFX-Anwendungsthread aufgerufen wird." Sehen wir uns einen alternativen Ansatz an. – trashgod
Zunächst habe ich wie gesagt ohne Hintergrundupdates eine freistehende "Hintergrund" -Leinwand aufgetragen. Wenn das Zeichnen fertig ist, kopiere ich den Hintergrundbereich auf die angezeigte Leinwand. Das funktioniert gut und vielleicht hat es eine bessere Gesamtstruktur. Es hilft mir immer noch nicht, den Unterschied zwischen der berechneten Zeit und der tatsächlichen Zeit zu lösen. – c0der