简体   繁体   English

如何在JavaFX(逐像素)画布中创建填充动画?

[英]How to create filling animation in JavaFX(pixel by pixel) canvas?

I am struggling with my school-project on Topology of Digital Images. 我正在为我的数字图像拓扑学学校项目而苦苦挣扎。 The goal is to write an app that will have a rectangle, with a line crossing through it and the app needs to fill the field below the line. 我们的目标是编写一个具有矩形的应用程序,该应用程序具有一条穿过该矩形的线,并且该应用程序需要填充该线下方的字段。 It needs to fill it pixel by pixel using a certain type of neigbourhood of the pixel. 它需要使用某种类型的像素邻域来逐像素填充它。

I managed to write some app that fills the field below the line, but it fills it all in a sec. 我设法编写了一些应用程序,该应用程序填充了该行下方的字段,但它很快就填充了所有内容。 I can't manage to have it done pixel by pixel. 我无法逐个像素地完成它。 I tried stopping the thread, but you can't stop the application thread, you cannot update the UI from the thread other than application. 我尝试停止线程,但无法停止应用程序线程,无法从应用程序以外的线程更新UI。 Platform.runLater or Task doesn't work either. Platform.runLater或Task也不起作用。 I want the canvas to refresh everytime new pixels are added, so I has this animation showing of how this algorithm works. 我希望画布在每次添加新像素时刷新,因此我有此动画演示此算法的工作原理。

I hope my explanation will be sufficient. 我希望我的解释就足够了。 You don't have to focus on my project, just can someone explain to me how to do this pixel by pixel filling in canvas? 您不必专注于我的项目,只是有人可以向我解释如何在画布中逐像素填充此像素吗?

Here's a code of a controller: 这是控制器的代码:

@FXML
private Canvas canvas;

@FXML
private ComboBox<String> optionsType;

@FXML
private Button drawBtn;

@FXML
private ComboBox<String> optionsNeighbourhood;

WritableImage wi;
PixelReader pr;
GraphicsContext gc;

(...) (...)

public void drawLine() {
    gc.strokeLine(0, 0, canvas.getWidth(), canvas.getHeight());
}

private void fillShape(Neighbourhood type, int startX, int startY) {

    int width = (int) canvas.getWidth();
    int height = (int) canvas.getHeight();

    LinkedList<Point> list = new LinkedList<>();
    list.addFirst(new Point(startX, startY));
    while (!list.isEmpty()) {
        Point point = list.removeLast();
        int x = point.getX();
        int y = point.getY();

        if (x >= 0 && x < width && y >= 0 && y < height && isEmpty(x, y)) {
            gc.fillRect(x, y, 1, 1);
            gc.fill();
            canvas.snapshot(null, wi);

            list.addFirst(new Point(x + 1, y));
            list.addFirst(new Point(x, y + 1));
            list.addFirst(new Point(x, y - 1));
            list.addFirst(new Point(x - 1, y));
            if (type == Neighbourhood.SIX || type == Neighbourhood.EIGHT) {
                if (isEmpty(x - 1, y) || isEmpty(x, y - 1)) {
                    list.add(new Point(x - 1, y - 1));
                }

                if (isEmpty(x + 1, y) || isEmpty(x, y + 1)) {
                    list.add(new Point(x + 1, y + 1));
                }
            }

            if (type == Neighbourhood.EIGHT) {
                if (isEmpty(x - 1, y) || isEmpty(x, y + 1)) {
                    list.add(new Point(x - 1, y + 1));
                }

                if (isEmpty(x + 1, y) || isEmpty(x, y + 1)) {
                    list.add(new Point(x + 1, y - 1));
                }
            }
        }

    }
}

private boolean isEmpty(int x, int y) {

    if (x < 0 || x >= canvas.getWidth() || y < 0 || y >= canvas.getHeight()) {
        return false;
    }

    if (!pr.getColor(x, y).toString().equals("0xffffffff")) {
        return false;
    }

    return true;
}

} }

Now I know the code may doesn't seem pretty obvious at first. 现在我知道代码乍一看似乎不太明显。 The algorithm doesn't matter, because it does seem to work, but the main question is: how do I get this field filled pixel by pixel(like an animation(yes, I tried AnimationTimer as well). 该算法无关紧要,因为它似乎确实起作用,但是主要问题是:如何使该字段逐个像素填充(就像动画一样(是的,我也尝试过AnimationTimer)。

There are several ways, one would be to use an AnimationTimer like this: 有几种方法,一种方法是使用像这样的AnimationTimer:

import java.util.Random;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class Main extends Application {

    private static double SCENE_WIDTH = 800;
    private static double SCENE_HEIGHT = 600;

    static Random random = new Random();

    Canvas canvas;
    GraphicsContext graphicsContext;

    AnimationTimer loop;

    Scene scene;

    @Override
    public void start(Stage primaryStage) {

        BorderPane root = new BorderPane();

        canvas = new Canvas(SCENE_WIDTH, SCENE_HEIGHT);

        graphicsContext = canvas.getGraphicsContext2D();

        Pane layerPane = new Pane();

        layerPane.getChildren().addAll(canvas);

        root.setCenter(layerPane);

        scene = new Scene(root, SCENE_WIDTH, SCENE_HEIGHT);

        primaryStage.setScene(scene);
        primaryStage.show();

        startAnimation();

    }

    private void startAnimation() {

        loop = new AnimationTimer() {

            double startX = 100;
            double endX = 200;
            double y = 100;
            double x = startX;
            double speed = 0.2;

            @Override
            public void handle(long now) {

                graphicsContext.fillOval(x, y, 5,5);

                x+=speed;

                if( x >= endX) {
                    loop.stop();
                }
            }
        };

        loop.start();

    }

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM