Javafx Canvas 未正确清除。

标签 java canvas javafx boids

我编写了 Craig Reynolds Boid 的 Java 实现。我最近更新了每个对象,使其由 .png 图像表示。从那时起我就一直遇到图像显示问题。

解决该问题的最佳方法是什么?

  • 我尝试过使用多边形,但当我的坐标之一为负数时,三角形无法正确显示。

Boids

主类:

public void paint(final GraphicsContext g) {
    new AnimationTimer() {
        @Override
        public void handle(long now) {
            flock.updateBoidsPostion();
            g.clearRect(0, 0, width, height);
            flock.drawBoids(g);
        }
    }.start();
}

@Override
public void start(Stage primaryStage) throws Exception {
    primaryStage.setTitle("Boids Flocking Algorithm");
    Group root = new Group();
    Canvas canvas = new Canvas(width, height);
    GraphicsContext gc = canvas.getGraphicsContext2D();

    root.getChildren().add(canvas);
    primaryStage.setScene(new Scene(root));
    primaryStage.show();

    paint(gc);

}

羊群:

/**
 * Paint each boid comprising the flock the canvas.
 * @param g
 */
public void drawBoids(GraphicsContext g) {
    for(Boid aBoid : boids) {
        aBoid.draw(g);
    }
}

body :

public void draw(GraphicsContext g) {
    //coordinates for the tip of the boid
    int x = (int)this.position.xPos;
    int y = (int)this.position.yPos;

    //Calculate a angle representing the direction of travel. 
    Rotate r = new Rotate(angle, x, y);
    g.setTransform(r.getMxx(), r.getMyx(), r.getMxy(), r.getMyy(), r.getTx(), r.getTy());

    g.drawImage(image, x, y);   
}

最佳答案

您的代码的问题是您旋转的是 GraphicsContext,而不是图像。或者至少在旋转 GraphicsContext 后不会将其旋转回去。

<小时/>

我对你提到的链接很好奇,i。 e. Boids Pseudocode .

这是一个快速实现。拖动矩形以使羊群跟随它。

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;

// Boids implementation in JavaFX
// Pseudo code by Conrad Parker: http://www.kfish.org/boids/pseudocode.html
public class Main extends Application {

    int numBoids = 50;
    double boidRadius = 10d;
    double boidMinDistance = boidRadius * 2d + 5; // +5 = arbitrary value
    double initialBaseVelocity = 1d;
    double velocityLimit = 3d;
    double movementToCenter = 0.01; // 1% towards center

    List<Boid> boids;

    static Random rnd = new Random();

    double sceneWidth = 1024;
    double sceneHeight = 768;

    Pane playfield;

    Rectangle rectangle;

    @Override
    public void start(Stage primaryStage) {

        BorderPane root = new BorderPane();

        playfield = new Pane();
        playfield.setPrefSize(sceneWidth, sceneHeight);

        Text infoText = new Text( "Drag the rectangle and have the flock follow it");
        root.setTop(infoText);

        root.setCenter(playfield);

        Scene scene = new Scene(root, sceneWidth, sceneHeight, Color.WHITE);
        //scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        primaryStage.setScene(scene);
        primaryStage.show();

        // create boids
        createBoids();

        // add boids to scene
        playfield.getChildren().addAll(boids);

        double w = 20;
        double h = 20;
        rectangle = new Rectangle( w, h);
        rectangle.relocate(sceneWidth / 2 - w/2, sceneHeight / 4 - h/2);
        playfield.getChildren().add(rectangle);

        MouseGestures mg = new MouseGestures();
        mg.makeDraggable(rectangle);

        // animation loop
        AnimationTimer loop = new AnimationTimer() {

            @Override
            public void handle(long now) {

                boids.forEach(Boid::move);
                boids.forEach(Boid::updateUI);

            }
        };

        loop.start();
    }

    private void createBoids() {

        boids = new ArrayList<>();

        // margin from top/left/bottom/right, so we have the boids initially more in the center
        double marginX = sceneWidth / 4;
        double marginY = sceneHeight / 4;

        for (int i = 0; i < numBoids; i++) {

            // random position around the center
            double x = rnd.nextDouble() * (sceneWidth - marginX * 2) + marginX;
            double y = rnd.nextDouble() * (sceneHeight - marginY * 2) + marginY;

            // initial random velocity depending on speed
            double v = Math.random() * 4 + initialBaseVelocity;

            Boid boid = new Boid(i, x, y, v);

            boids.add(boid);

        }

    }


    // Rule 1: Boids try to fly towards the centre of mass of neighbouring boids. 
    public Point2D rule1(Boid boid) {

        Point2D pcj = new Point2D(0, 0);

        for( Boid neighbor: boids)  {

            if( boid == neighbor)
                continue;

            pcj = pcj.add( neighbor.position);

        }

        if( boids.size() > 1) {
            double div = 1d / (boids.size() - 1);
            pcj = pcj.multiply( div);
        }

        pcj = (pcj.subtract(boid.position)).multiply( movementToCenter);

        return pcj;
    }

    // Rule 2: Boids try to keep a small distance away from other objects (including other boids). 
    public Point2D rule2(Boid boid) {

        Point2D c = new Point2D(0, 0);

        for( Boid neighbor: boids)  {

            if( boid == neighbor)
                continue;

            double distance = (neighbor.position.subtract(boid.position)).magnitude();

            if( distance < boidMinDistance) {
                c = c.subtract(neighbor.position.subtract(boid.position));
            }

        }

        return c;
    }

    // Rule 3: Boids try to match velocity with near boids. 
    public Point2D rule3(Boid boid) {

        Point2D pvj = new Point2D(0, 0);

        for( Boid neighbor: boids)  {

            if( boid == neighbor)
                continue;

            pvj = pvj.add( neighbor.velocity);

        }

        if( boids.size() > 1) {
            double div = 1d / (boids.size() - 1);
            pvj = pvj.multiply( div);
        }

        pvj = (pvj.subtract(boid.velocity)).multiply(0.125); // 0.125 = 1/8

        return pvj;
    }


    // tend towards the rectangle
    public Point2D tendToPlace( Boid boid) {

        Point2D place = new Point2D( rectangle.getBoundsInParent().getMinX() + rectangle.getBoundsInParent().getWidth() / 2, rectangle.getBoundsInParent().getMinY() + rectangle.getBoundsInParent().getHeight() / 2);

        return (place.subtract(boid.position)).multiply( 0.01);
    }

    public class Boid extends Circle {

        int id;

        Point2D position;
        Point2D velocity;

        double v;

        // random color
        Color color = randomColor();

        public Boid(int id, double x, double y, double v) {

            this.id = id;
            this.v = v;

            position = new Point2D( x, y);
            velocity = new Point2D( v, v);

            setRadius( boidRadius);

            setStroke(color);
            setFill(color.deriveColor(1, 1, 1, 0.2));

        }

        public void move() {

            Point2D v1 = rule1(this);
            Point2D v2 = rule2(this);
            Point2D v3 = rule3(this);
            Point2D v4 = tendToPlace(this);

            velocity = velocity
                    .add(v1)
                    .add(v2)
                    .add(v3)
                    .add(v4)
                    ;

            limitVelocity();

            position = position.add(velocity);

            constrainPosition();
        }

        private void limitVelocity() {

            double vlim = velocityLimit;

            if( velocity.magnitude() > vlim) {
                 velocity = (velocity.multiply(1d/velocity.magnitude())).multiply( vlim);
            }

        }


        // limit position to screen dimensions
        public void constrainPosition() {

            double xMin = boidRadius;
            double xMax = sceneWidth - boidRadius;
            double yMin = boidRadius;
            double yMax = sceneHeight - boidRadius;

            double x = position.getX();
            double y = position.getY();
            double vx = velocity.getX();
            double vy = velocity.getY();

            if( x < xMin) {
                x = xMin;
                vx = v;
            }
            else if( x > xMax) {
                x = xMax;
                vx = -v;
            }

            if( y < yMin) {
                y = yMin;
                vy = v;
            }
            else if( y > yMax) {
                y = yMax;
                vy = -v;
            }

            // TODO: modification would be less performance consuming => find out how to modify the vector directly or create own Poin2D class
            position = new Point2D( x, y);
            velocity = new Point2D( vx, vy);

        }


        public void updateUI() {

            setCenterX(position.getX());
            setCenterY(position.getY());
        }
    }


    public static Color randomColor() {
        int range = 220;
        return Color.rgb((int) (rnd.nextDouble() * range), (int) (rnd.nextDouble() * range), (int) (rnd.nextDouble() * range));
    }


    public static class MouseGestures {

        class DragContext {
            double x;
            double y;
        }

        DragContext dragContext = new DragContext();

        public void makeDraggable( Node node) {
            node.setOnMousePressed( onMousePressedEventHandler);
            node.setOnMouseDragged( onMouseDraggedEventHandler);
            node.setOnMouseReleased( onMouseReleasedEventHandler);
        }

        EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent event) {

                if( event.getSource() instanceof Circle) {

                    Circle circle = ((Circle) (event.getSource()));

                    dragContext.x = circle.getCenterX() - event.getSceneX();
                    dragContext.y = circle.getCenterY() - event.getSceneY();

                } else {

                    Node node = ((Node) (event.getSource()));

                    dragContext.x = node.getTranslateX() - event.getSceneX();
                    dragContext.y = node.getTranslateY() - event.getSceneY();

                }
            }
        };

        EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent event) {

                if( event.getSource() instanceof Circle) {

                    Circle circle = ((Circle) (event.getSource()));

                    circle.setCenterX( dragContext.x + event.getSceneX());
                    circle.setCenterY( dragContext.y + event.getSceneY());

                } else {

                    Node node = ((Node) (event.getSource()));

                    node.setTranslateX( dragContext.x + event.getSceneX());
                    node.setTranslateY( dragContext.y + event.getSceneY());

                }

            }
        };

        EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent event) {

            }
        };

    }

    public static void main(String[] args) {
        launch(args);
    }

}

enter image description here

3D 版本只是使用 Points3D 代替 Points2D,使用球体和盒子代替圆形和矩形。

enter image description here

我还建议您阅读这本优秀的书The Nature of Code作者:Daniel Shiffman,特别是章节 Autonomous Agents 。它详细介绍了 Boids。

关于Javafx Canvas 未正确清除。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30401313/

相关文章:

java - MongoDB 使用大量内存

java - 如何填充动态列表 Java

javafx - 使用 javafx 图表的极坐标图

java - 关于 Java 子接口(interface)子类型

java - 支持 JAX-RS 中的查询参数

CSS 定位的 Canvas 大小不同于其他 block 元素(左上角右下角属性的约束)

android - ViewFlipper:在 View 上绘制 Canvas

javascript - 在 Canvas 中填充更多类型的相似对象

java - 有没有办法在javafx WebView中播放flash?

java - 单击一行时使用表格 View 数据填充文本字段