【问题标题】:Java - Proper line intersection checkJava - 正确的线交叉检查
【发布时间】:2014-04-21 07:05:05
【问题描述】:

我需要确定两条线段是否相交,但使用 line2D.linesIntersect 方法存在问题。即使这些行只共享一个端点,该方法也会返回一个真实的结果,如下所示:

Point2D.Double temp1 = new Point2D.Double(0, 0);
Point2D.Double temp2 = new Point2D.Double(0, 1);
Point2D.Double temp3 = new Point2D.Double(1, 0);

if(Line2D.linesIntersect(temp1.x, temp1.y, temp2.x, temp2.y, temp1.x, temp1.y, temp3.x, temp3.y){
    System.out.println("Lines share an endpoint.");
}
else{
    System.out.println("Lines don't intersect.");
}

在这种情况下,我总是会收到“线路共享端点”消息。当然,在某些情况下,线确实共享一个端点,它们有可能无限次相交((0,0)到(0,1)与(0,0)到(0,2)相交) ,这显然应该返回一个真实的结果。但是,在仅共享端点且不发生其他交叉点的其他情况下,程序将无法正常工作。有没有办法防止这个问题?

【问题讨论】:

    标签: java line point


    【解决方案1】:

    这是我可以用我的基本数学知识得出的答案。希望它可以帮助你。给定 4 个点,它会告诉您两条线(来自这四个点)是否相交、共享一个端点或两者都不相交。

            //starting point of line 1
            Point2D.Double temp1 = new Point2D.Double(0 , 1);
            //ending point of line 1
            Point2D.Double temp2 = new Point2D.Double(0, -1);
            //starting point of line 2
            Point2D.Double temp3 = new Point2D.Double(-1, 0);
            //ending point of line 2
            Point2D.Double temp4 = new Point2D.Double(1, 0);
    
            //determine if the lines intersect
            boolean intersects = Line2D.linesIntersect(temp1.x, temp1.y, temp2.x, temp2.y, temp3.x, temp3.y, temp4.x, temp4.y);
    
            //determines if the lines share an endpoint
            boolean shareAnyPoint = shareAnyPoint(temp1, temp2, temp3, temp4);
    
            if (intersects && shareAnyPoint) {
                System.out.println("Lines share an endpoint.");
            } else if (intersects && !shareAnyPoint) {
                System.out.println("Lines intersect.");
            } else {
                System.out.println("Lines neither intersect nor share a share an endpoint.");
            }
    

    这里是 shareAnyPoint(StartPointA, EndPointA, StartPointB, EndPointB) 函数,它检查是否开始/任何一条线的端点都在另一条线上。

    public static boolean shareAnyPoint(Point2D.Double A, Point2D.Double B, Point2D.Double C, Point2D.Double D) {
        if (isPointOnTheLine(A, B, C)) return true;
        else if (isPointOnTheLine(A, B, D)) return true;
        else if (isPointOnTheLine(C, D, A)) return true;
        else if (isPointOnTheLine(C, D, B)) return true;
        else return false;
    }
    

    这里是 isPointOnTheLine(StartPoint, EndPoint, MyPoint) 函数,用于确定一个点是否在线(由其他 2分)

    public static boolean isPointOnTheLine(Point2D.Double A, Point2D.Double B, Point2D.Double P) {  
        double m = (B.y - A.y) / (B.x - A.x);
    
        //handle special case where the line is vertical
        if (Double.isInfinite(m)) {
            if(A.x == P.x) return true;
            else return false;
        }
    
        if ((P.y - A.y) == m * (P.x - A.x)) return true;
        else return false;
    }
    

    试一试,让我知道结果。

    【讨论】:

    • 它适用于初始情况(合法相交),似乎也适用于其他情况(A 线从 (0, 0) 到 (0, 1),B 线是从 (0, 0) 到 (-1, 0))。我问这个的主要原因是因为我必须检查无数可能的线交叉点才能生成邻接矩阵。它的结构应该不会有两条线相交。在得知 Line2D.linesIntersect 函数无法正常工作之前,我对它的工作原理有所了解。
    【解决方案2】:

    如果您不限于 Point2DLine2D 对象,您可以使用 JTS(Java 拓扑套件)。

    • 将线条创建为 LineString 对象
    • 使用intersects方法

    简单代码示例:

    LineString lineA = new GeometryFactory().createLineString(new Coordinate[]{new Coordinate(0, 0), new Coordinate(0, 10)});
    LineString lineB = new GeometryFactory().createLineString(new Coordinate[]{new Coordinate(-5,5), new Coordinate(5,5)});
    boolean intersect = lineA.intersects(lineB);
    

    【讨论】:

      【解决方案3】:

      我在网上找到了一个解决方案,我已经对其进行了调整,并进行了手动测试。 自带的,只需要import java.awt.geom.Point2D

      /**
       * Test if the first point lies in the bounding box denote by the other two
       * points.
       */
      public static boolean isBetween(Point2D pToTest, Point2D p1, Point2D p2) {
          return isBetween(pToTest.getX(), pToTest.getY(), p1.getX(), p1.getY(), p2.getX(), p2.getY());
      }
      
      /**
       * Called {@link #isBetween(Point2D, Point2D, Point2D)} passing those points
       * coordinates.
       */
      public static boolean isBetween(double pxToTest, double pyToTest, double px1, double py1, double px2, double py2) {
          double w, h;
          // taking inspiration from Rectangle's class
          w = px1 - px2;
          if (w < 0)
              w = -w;
          h = py1 - py2;
          if (h < 0)
              h = -h;
          // use p1 as the left-top corner of the rectangle
          // (the left-top corner is considered as the origin (0;0))
          if (px1 > px2)
              px1 = px2;//
          if (py1 > py2)
              py1 = py2;//
          if (pxToTest < px1 || pyToTest < py1) {
              return false;
          }
          w += px1;
          h += py1;
          // overflow || intersect
          return ((w < px1 || w > pxToTest) && (h < py1 || h > pyToTest));
      }
      
      public static double slope(double xa, double ya, double xb, double yb) {
          if (xb == xa)
              return Double.POSITIVE_INFINITY;
          return (yb == ya) ? 0.0 : ((yb - ya) / (xb - xa));
      }
      
      /**
       * Called by {@link #areLinesIntersecting(Point2D, Point2D, Point2D, Point2D)}
       * by providing each of those point's coordinates.
       */
      public static Point2D areLinesIntersecting(double pxStart1, double pyStart1, double pxEnd1, double pyEnd1,
              double pxStart2, double pyStart2, double pxEnd2, double pyEnd2) {
          double slope_ab, slope_cd, numerator, denominator, q_ab, q_cd, x, y;
          Point2D p;
          slope_ab = slope(pxStart1, pyStart1, pxEnd1, pyEnd1);
          slope_cd = slope(pxStart2, pyStart2, pxEnd2, pyEnd2);
          q_ab = (pyStart1 - slope_ab * pxStart1);
          q_cd = (pyStart2 - slope_cd * pxStart2);
          if ((slope_ab == slope_cd) // parallel?
                  && (
                  // overlapping?
                  ((slope_ab == Double.POSITIVE_INFINITY || slope_ab == Double.NaN) && pxStart1 == pxStart2) //
                          || //
                              // overlapping?
                          (slope_ab == 0.0 && pyStart1 == pyStart2) //
                          || //
                              // if different costant parts of lines, then parallel BUT not overlapping
                          (q_ab == q_cd)//
                  )) {
              if (isBetween(pxStart2, pyStart2, pxStart1, pyStart1, pxEnd1, pyEnd1))
                  return new Point2D.Double(pxStart2, pyStart2);
              if (isBetween(pxEnd2, pyEnd2, pxStart1, pyStart1, pxEnd1, pyEnd1))
                  return new Point2D.Double(pxEnd2, pyEnd2);
              if (isBetween(pxStart1, pyStart1, pxStart2, pyStart2, pxEnd2, pyEnd2))
                  return new Point2D.Double(pxStart1, pyStart1);
              if (isBetween(pxEnd1, pyEnd1, pxStart2, pyStart2, pxEnd2, pyEnd2))
                  return new Point2D.Double(pxEnd1, pyEnd1);
          }
          if (slope_ab == Double.POSITIVE_INFINITY) {
              // ab vertical line: all a&b's x-es are equals
              x = pxStart1;
              y = (q_cd + (slope_cd * x));
              // it's a cross
              if ((pyStart1 <= pyEnd1) ? (y < pyStart1 || y > pyEnd1)
                      // point are reversed
                      : (y > pyStart1 || y < pyEnd1))
                  return null;
              if ((pxStart2 < pxEnd2) ? (pxStart2 <= x && x <= pxEnd2)//
                      : (pxEnd2 <= x && x <= pxStart2))
                  return new Point2D.Double(x, y);
              else
                  return null;
          }
          if (slope_cd == Double.POSITIVE_INFINITY) {
              // cd vertical line: all c&d's x-es are equals
              x = pxStart2;
              y = (q_ab + (slope_ab * x));
              // it's a cross
              if ((pyStart2 <= pyEnd2) ? (y < pyStart2 || y > pyEnd2)
                      // point are reversed
                      : (y > pyStart2 || y < pyEnd2))
                  return null;
              // if the y lies inside the line a-b, then intersection
              if ((pxStart1 < pxEnd1) ? (pxStart1 <= x && x <= pxEnd1)//
                      : (pxEnd1 <= x && x <= pxStart1))
                  return new Point2D.Double(x, y);
              else
                  return null;
      
          }
          // slopes cannot be infinity
          if (slope_ab == 0.0) {
              y = pyStart1;
              // slope_cd cannot be Infinity (second group of checks) and zero (first ones)
              x = (y - q_cd) / slope_cd;
      
              if ((pxStart1 <= pxEnd1) ? (x < pxStart1 || x > pxEnd1)
                      // point are reversed
                      : (x > pxStart1 || x < pxEnd1))
                  return null;
              if ((pxStart2 <= pxEnd2) ? (x < pxStart2 || x > pxEnd2)
                      // point are reversed
                      : (x > pxStart2 || x < pxEnd2))
                  return null;
              if ((pyStart2 < pyEnd2) ? (pyStart2 <= y && y <= pyEnd2)//
                      : (pyEnd2 <= y && y <= pyStart2))
                  return new Point2D.Double(x, y);
              else
                  return null;
          }
          if (slope_cd == 0.0) {
              y = pyStart2;
              // slope_ab cannot be Infinity (second group of checks) and zero (first ones)
              x = (y - q_ab) / slope_ab;
      
              if ((pxStart2 <= pxEnd2) ? (x < pxStart2 || x > pxEnd2)
                      // point are reversed
                      : (x > pxStart2 || x < pxEnd2))
                  return null;
      
              if ((pxStart1 <= pxEnd1) ? (x < pxStart1 || x > pxEnd1)
                      // point are reversed
                      : (x > pxStart1 || x < pxEnd1))
                  return null;
              if ((pyStart1 < pyEnd1) ? (pyStart1 <= y && y <= pyEnd1)//
                      : (pyEnd1 <= y && y <= pyStart1))
                  return new Point2D.Double(x, y);
              else
                  return null;
          }
          denominator = slope_cd - slope_ab;
          numerator = q_ab - q_cd;
          x = (numerator / denominator);
          y = (q_ab + (slope_ab * x));
          p = new Point2D.Double(x, y);
          if (isBetween(p.getX(), p.getY(), pxStart1, pyStart1, pxEnd1, pyEnd1)
                  && isBetween(p.getX(), p.getY(), pxStart2, pyStart2, pxEnd2, pyEnd2))
              return p;
          y = (q_cd + (slope_cd * x));
          p = new Point2D.Double(x, y);
          if ((isBetween(p.getX(), p.getY(), pxStart1, pyStart1, pxEnd1, pyEnd1)
                  && isBetween(p.getX(), p.getY(), pxStart2, pyStart2, pxEnd2, pyEnd2)))
              return p;
      
          return null;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-05
        • 2015-03-15
        • 2012-10-08
        • 1970-01-01
        相关资源
        最近更新 更多