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

时间:2018-07-30 18:14:21

标签: java javafx

我的程序是一个程序,您可以在其中放置FRC比赛中的工程图的点,然后可以查看使用这些点定义的路径。

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

问题是,进行测试时,由于某种原因,它会显示我减去的形状偏移。我看过关于Shape.intersect的{​​{3}},但这无济于事。我已经检查了AnchorPane及其放置在其中的所有子元素的所有布局位置,比例等,但是它们都返回0或1s。

以下是返回的图片:enter image description here 红色矩形是作为图像大小的矩形。
紫色是矩形和所选多边形之间的重叠。
灰色是Shape.subtract()的结果。

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

这就是我想要的样子: StackOverflow question

这是我为重现此现象而创建的测试环境:

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>

其余代码为enter image description here

1 个答案:

答案 0 :(得分:2)

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

  

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

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

您的简单解决方法是删除有问题的边上的填充(取决于节点中的对齐方式),将移位后的节点平移填充量(取消填充移位) ,或在添加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个区分:
    • 在减去之前,不会将任何形状添加到层次结构中。两者都不受填充的影响,因此结果是正确的:
      enter image description here
    • 在减去之前,只有较小的形状被添加到层次结构中。这类似于将填充物应用于小(蓝色)形状,然后将其裁剪并移回:
      enter image description here
    • 在减去之前,仅将大形状添加到层次结构中。这类似于将填充物应用于大(红色)形状,然后将其裁剪并移回:
      enter image description here
    • 两个形状都将在减去之前添加到层次结构中。上述2的组合:
      enter image description here