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

Java 为什么shape.subtract()生成的形状出现在错误的位置?,java,javafx,Java,Javafx,在我的程序中,你可以放置FRC比赛中的场地图点,然后你可以看到使用这些点定义的路径 程序的第一步是校准(找到像素到实际距离的比例),我目前正在添加定义场的功能(场墙、障碍物等)。我首先尝试定义字段边界。为此,我考虑在图形上选择点,然后将它们添加到多边形中,然后从与场图像大小相同的矩形中减去多边形 问题是,当测试它时,它会出于某种原因显示减去的形状偏移。我已经查看了about形状.intersect,但是,它没有帮助。我已经检查了AnchorPane的所有布局位置、比例等,以及我放置在其中的子对象

在我的程序中,你可以放置FRC比赛中的场地图点,然后你可以看到使用这些点定义的路径

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

问题是,当测试它时,它会出于某种原因显示减去的形状偏移。我已经查看了about
形状.intersect
,但是,它没有帮助。我已经检查了
AnchorPane
的所有布局位置、比例等,以及我放置在其中的子对象,但它都返回0或1s

下面是它返回的内容的图形: 红色矩形是表示图像大小的矩形。
紫色是矩形和选定多边形之间的重叠。
灰色是
形状。subtract()
结果

我想要的是
Shape.substract
(灰色)在紫色上方有一个空白点,非常适合。但正如你在这张图片中看到的,紫色与灰色重叠

这就是我希望它看起来的样子:

我创建了一个测试环境来重现这种现象:

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>


.

问题是两个因素组合的结果:填充和节点层次结构;可能是个虫子。需要注意的要点是
减去
的文档:

在最终操作之前,输入形状的区域将转换为其各自最顶端父节点的父坐标空间

这意味着在进行减法运算时,节点在层次中的位置很重要。如果节点位于具有填充的父节点中,则在坐标变换过程中会将其考虑在内,从而导致您看到的偏移

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

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个区别:
    • 在减法之前,不会将任何形状添加到层次中。两者都不受填充的影响,因此结果是正确的:
    • 在减法之前,仅将小形状添加到层次中。这类似于将填充应用于小(蓝色)形状、剪裁并向后移动:
    • 在减法之前,仅将大形状添加到层次中。这类似于将填充应用于大(红色)形状、剪裁并向后移动:
    • 两个形状在减法之前都添加到层次中。上述两种方法的组合:

寻求调试帮助的问题(“为什么此代码不起作用?”)必须包括所需的行为、特定的问题或错误以及在问题本身中重现这些问题所需的最短代码。没有明确问题陈述的问题对其他读者没有用处。请参阅:如何创建。我们应该能够将发布的代码粘贴到IDE中并运行它,然后看到问题所在。您发布的代码不是一个,请重新阅读链接内容。这样您就离发现问题又近了一步。但是如果你不能复制它,我们也不能。菜单、文本字段和按钮是用来做什么的?移除任何不需要的东西。