java - 为什么 Shape.subtract() 生成的形状出现在错误的位置?

标签 java javafx

我的程序是这样的,您可以在其中放置 FRC 比赛中的现场绘图点,然后您可以看到使用这些点定义的路径。

程序的第一步是校准(找到真实距离比例的像素),我目前正在添加定义场的功能(场墙、障碍物等......)。我首先尝试定义字段边界。为此,我正在考虑在绘图上选择点,然后将它们添加到多边形中,然后从与字段图像大小相同的矩形中减去多边形。

问题是,在测试时,由于某种原因,它显示了我减去的形状偏移。我看过StackOverflow question然而,关于Shape.intersect,它没有帮助。我已经检查了 AnchorPane 及其放置在其中的子项的所有布局位置、比例等,但它都返回 0 或 1。

下面是它返回的内容的图:enter image description here 红色矩形是图像大小的矩形。
紫色是矩形和所选多边形之间的重叠部分。
灰色是 Shape.subtract() 结果。

我想要的是 Shape.substract (灰色)在紫色顶部有一个空白点并且完美贴合。但正如您在该图像中看到的,紫色与灰色重叠。

这就是我想要的样子: enter image description here

这是我创建的用于重现该现象的测试环境:

Main.java

import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {


    public Main() {
    }

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

    @Override
    public final void start(Stage primaryStage) throws IOException {

        Parent root = FXMLLoader.load(getClass().getResource("recreationFieldSelection.fxml"));

        primaryStage.setTitle("Path Planner");
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
}

RecreationFieldSelection.java

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Path;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;

public class RecreationFieldSelection implements Initializable {

    public AnchorPane pointPlacement;
    public ImageView fieldImage;
    public TextField distanceViewer;
    public HBox infoPane;

    private Polygon polygon = new Polygon();
    private Color gray = Color.gray(.5, .6);
    private Color blue = Color.rgb(0, 0, 255, .5);
    private boolean now = true;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        fieldImage.setFitHeight(1000);
        fieldImage.setFitWidth(1000);

        polygon.setFill(Color.TRANSPARENT);
        polygon.setStroke(Color.RED);
        polygon.setStrokeWidth(1);

        pointPlacement.getChildren().add(polygon);
    }

    @FXML
    public void handleMouseClicked(MouseEvent mouseEvent) {
        outlineField(mouseEvent);
    }

    @FXML
    private void outlineField(MouseEvent mouseEvent) {

        polygon.getPoints().addAll(mouseEvent.getX(), mouseEvent.getY());

//      If the polygon has 8 defining points
        if (polygon.getPoints().size() / 2 >= 8 && now) {
            now = false;

            Rectangle rectangle = new Rectangle(fieldImage.getFitWidth(), fieldImage.getFitHeight());
            rectangle.setFill(Color.color(1, 0, 0, .3));

            Path subtract = (Path) Polygon.subtract(rectangle, polygon);
            subtract.setFill(gray);
            subtract.setStroke(gray);
            subtract.setStrokeWidth(1);

            polygon.setFill(blue);

            pointPlacement.getChildren().addAll(subtract);

        }
    }
}

recreationFieldSelection.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns:fx="http://javafx.com/fxml/1" spacing="5.0" xmlns="http://javafx.com/javafx/8"
  fx:controller="testing.RecreationFieldSelection">
  <padding>
    <Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
  </padding>
  <ScrollPane VBox.vgrow="ALWAYS" prefViewportHeight="150.0" prefViewportWidth="200.0">
    <AnchorPane fx:id="pointPlacement">
      <ImageView onMouseClicked="#handleMouseClicked" pickOnBounds="true" preserveRatio="true" fx:id="fieldImage"/>
    </AnchorPane>
  </ScrollPane>
</VBox>

其余代码为here .

最佳答案

该问题是两个因素综合作用的结果:填充和节点层次结构;并且可能是一个错误。需要注意的重要一点来自 subtract 的文档:

Before the final operation the areas of the input shapes are transformed to the parent coordinate space of their respective topmost parent nodes.

这意味着进行减法时节点在层次结构中的位置很重要。如果节点位于具有填充的父级中,则在坐标转换期间会考虑到它,这会导致您看到的移位。

简单的解决方法是删除有问题的一侧的填充(取决于节点中的对齐方式),将移位的节点平移填充量(取消填充移位) ,或者在减去操作之后将多边形添加到层次结构中,其中添加减去:

pointPlacement.getChildren().addAll(subtract, polygon);

为了进行更深入的分析,我创建了以下 MCVE:

public class Main2 extends Application {

    private final double size = 400;

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

    @Override
    public final void start(Stage stage) throws IOException {
        AnchorPane pane = new AnchorPane();
        pane.setStyle("-fx-background-color: lightgray;");

        VBox root = new VBox(pane);
        VBox.setVgrow(pane, Priority.ALWAYS);
        root.setPadding(new Insets(10, 10, 10, 10));

        stage.setTitle("add none");
        stage.setScene(new Scene(root));
        stage.setWidth(size + 50);
        stage.setHeight(size + 70);

        draw(pane);
        stage.show();
//      stage.hide();
//      draw(pane);
//      stage.show();
    }

    private void draw(AnchorPane pane) {
        Rectangle big = new Rectangle(size, size);
        big.setFill(Color.RED.deriveColor(0, 1, 1, 0.3));
        big.setStroke(Color.RED);
        pane.getChildren().add(big);

        Rectangle small = new Rectangle(size/2, size/2);
        small.setFill(Color.BLUE.deriveColor(0, 1, 1, 0.3));
        small.setStroke(Color.BLUE);
        pane.getChildren().add(small);

        Shape subtract = Shape.subtract(big, small);
        subtract.setFill(Color.LIME.deriveColor(0, 1, 1, 0.3));
        subtract.setStroke(Color.LIME);

//      pane.getChildren().add(big);
//      pane.getChildren().add(small);
        pane.getChildren().add(subtract);
    }
}

有几种情况需要注意:

  • draw 在舞台可见之前调用。在这种情况下,不考虑填充,结果将始终是正确的。
  • draw 在舞台第一次可见后调用(隐藏它、调用 draw 和显示它没有区别)。在那里我们可以做出 4 个区别:
    • 在相减之前,不会将任何形状添加到层次结构中。两者都不受填充的影响,因此结果是正确的:
      enter image description here
    • 在减去之前仅将小形状添加到层次结构中。这类似于将填充应用于小(蓝色)形状、剪切并将其移回:
      enter image description here
    • 在减去之前,仅将大形状添加到层次结构中。这类似于将填充应用到大(红色)形状、剪切并将其移回:
      enter image description here
    • 两个形状都会在相减之前添加到层次结构中。上述2的组合:
      enter image description here

关于java - 为什么 Shape.subtract() 生成的形状出现在错误的位置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51599995/

相关文章:

带 Spring Boot 的 Javafx 启动屏幕

java - 低性能计算机上的 Spring bean 单例复制

c++ - 什么是等同于 JavaFx 和 AnimationTimer 类的 C++?

java - WebView 加载 url 不起作用

JavaFX 项目在导出到可运行的 .jar 时中断? FXMLLoader 相关吗?

java - 迭代时删除元素。 removeIf 导致 ConcurrentModificationException

java - 当前用户的DataSnapshot值为NULL

java - 如何避免代码重复?

java - 如何在 weblogic 中共享 session

java - Docker Compose OpenJDK - 卷映射不起作用