java 将斜线拉伸到极限

ewm0tg9j  于 2023-02-28  发布在  Java
关注(0)|答案(1)|浏览(123)

假设有一条线形成(A)=x1,y1和(B)=x2,y2这条线画在屏幕上,现在我需要这条线到达屏幕的两端。
有没有什么公式可以使直线在保持倾斜角的情况下继续延伸?
我知道JavaFX有很多资源,我接受建议

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

  

public class LineContinue extends Application {

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

    @Override
    public void start(Stage primaryStage) {
        
        BorderPane bp = new BorderPane();

        Line line = new Line();
        line.setStartX(0);
        line.setStartY(150);
        
        line.setEndX(170);
        line.setEndY(50);
        
        line.setLayoutX(10);
        line.setLayoutY(10);
        
            
        Pane pane = new Pane();
        pane.getChildren().add(line);
        
        line.setLayoutX(100);
        line.setLayoutY(200);
        
        
        Button b = new Button("Extreme Line");
        b.setOnAction( (e) -> {
             
             // formula continue line until extreme
             
        
        });
        
        
        bp.setCenter(pane);
        bp.setBottom(b);
  
        primaryStage.setWidth(600);
        primaryStage.setHeight(500);
        primaryStage.setScene(new Scene(bp));
        primaryStage.setTitle("Extreme Line");
        primaryStage.show();
        
    }    
    
}
w8biq8rn

w8biq8rn1#

这基本上是一道数学题。
您需要找到(x1,y1)(x2,y2)之间的直线与x=0y=0x=wy=h定义的直线(其中wh是窗口的宽度和高度)的交点。
最简单的方法是使用齐次坐标,将点和线表示为3-向量,可以使用Point3D类方便地对它们进行建模。
齐次坐标的主要优点是它可以以相同的方式处理所有“倾斜”:例如,可以使用二维坐标,即求出y = mx + c形式的斜线方程,重新排列求交点等;但这对垂直线不起作用(其中m是无穷大),你可以绕过它,但它会变得有点混乱,而且在数值上条件不太好。
在齐次坐标中,将直线ax + by + c = 0表示为向量[a b c]'
您可以通过表示直线的两个向量的叉积来计算两条直线的交点:

[ a1 ]   [ a2 ]    [   b1 * c2 - c1 * b2 ]
[ b1 ] x [ b2 ]  = [ - a1 * c2 + c1 * a2 ]
[ c1 ]   [ c2 ]    [   a1 * b2 - b1 * a2 ]

考虑点(X, Y)位于直线x = Xy = Y的交点处;因此,由上式可知,(X, Y)的齐次坐标为:

[ 1  ]   [ 0  ]   [ X ]
[ 0  ] x [ 1  ] = [ Y ]
[ -X ]   [ -Y ]   [ 1 ]

同样,齐次记法中两点之间的直线由两点的叉积给出

[ x1 ]   [ x2 ]   [ y1 - y2       ]
[ y1 ] x [ y2 ] = [ -x1 + x2      ]
[  1 ]   [  1 ]   [ x1*y2 - x2*y1 ]

(Don不用担心这些复杂的表达方式:它们很容易打错,我确信我打错了;但是您不必手动实现它们,因为方法存在于Point3D类中)。
另一件需要注意的重要事情是,您可以缩放齐次坐标,而无需更改点或线。
例如,[ka kb kc][a b c]完全相同:所有坐标都乘以相同的因子k,这使得点/线完全不变。
因此,我们可以将斜线表示为Point3D

Point3D p1 = new Point3D(x1, y1, 1);
Point3D p2 = new Point3D(x2, y2, 1);
Point3D slantedLine = p1.crossProduct(p2);

我们可以用方程x=0x=wy=0y=h来表示视口的四条边:

Point3D lineX_equals_0 = new Point3D(1, 0, 0);
Point3D lineX_equals_w = new Point3D(1, 0, -w);
Point3D lineY_equals_0 = new Point3D(0, 1, 0);
Point3D lineY_equals_h = new Point3D(0, 1, -h);

我们可以找到这些线与斜线的交点

Point3D intersectionX_equals_0 = slantedLine.crossProduct(lineX_equals_0);
Point3D intersectionX_equals_w = slantedLine.crossProduct(lineX_equals_w);
Point3D intersectionY_equals_0 = slantedLine.crossProduct(lineY_equals_0);
Point3D intersectionY_equals_h = slantedLine.crossProduct(lineY_equals_h);

现在你有了4个相同的点,斜线与这些点相交,为了延长斜线使之相交,你需要将它们转换回二维坐标。
请记住,二维点(x, y)表示为三维点(x, y, 1):我们可以通过将切入点除以它们的z分量来转换切入点:

intersectionX_equals_0 = intersectionX_equals_0.multiply(1 / intersectionX_equals_0.getZ());
intersectionX_equals_w = intersectionX_equals_w.multiply(1 / intersectionX_equals_w.getZ());

intersectionY_equals_0 = intersectionY_equals_0.multiply(1 / intersectionY_equals_0.getZ());
intersectionY_equals_h = intersectionY_equals_h.multiply(1 / intersectionY_equals_h.getZ());

现在,计算出这些点中的哪些位于窗口的可绘制部分:如果0 <= x <= w0 <= y <= h,则点是可绘制的。
您可以选择2个可绘制的点,并在它们之间绘制线。
让我们把所有这些放在一起:

Point3D p1 = new Point3D(x1, y1, 1);
Point3D p2 = new Point3D(x2, y2, 1);
Point3D slantedLine = p1.crossProduct(p2);

Set<Point2D> intersectionPoints =
    Stream.of(
        new Point3D(1, 0, 0),
        new Point3D(1, 0, -w),
        new Point3D(0, 1, 0),
        new Point3D(0, 1, -h))
      .map(slantedLine::crossProduct)
      .filter(p -> p.getZ() != 0)  // Remove edges parallel to slanted line
      .map(p -> p.multiply(1 / p.getZ()))
      .filter(p -> p.getX() >= 0 && p.getX() <= w)
      .filter(p -> p.getY() >= 0 && p.getY() <= h)
      .map(p -> new Point2D(p.getX(), p.getY()))
      .collect(toSet());

这给了你以下几点:

  • 如果有两个点,就在这两点之间画一条线
  • 如果有三个点,则线与视口相交于一个角
  • 如果有四个点,则线与视口相交于两个角
  • 如果少于两个点,则问题退化(例如w或h为0,或x1=x2且y1=y2)

您需要考虑如何处理非两点情况。

相关问题