线段交点(交点)

时间:2015-04-24 17:41:16

标签: java geometry line line-intersection

我创建了一个计算两个线段交点的函数。

不幸的是,如果其中一个段是verticale

,则下面的代码不起作用
    public static Point intersection(Segment s1, Segment s2) {
    double x1 = s1.getP1().getX();
    double y1 = s1.getP1().getY() ;
    double x2 = s1.getP2().getX();
    double y2 = s1.getP2().getY() ;
    double x3 = s2.getP1().getX();
    double y3 = s2.getP1().getY();
    double x4 = s2.getP2().getX();
    double y4 = s2.getP2().getY();

    double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
    if (d == 0) {
        return null;
    }
    double xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
    double yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
    Point p = new Point(xi, yi);
    if (xi < Math.min(x1, x2) || xi > Math.max(x1, x2)) {
        return null;
    }
    if (xi < Math.min(x3, x4) || xi > Math.max(x3, x4)) {
        return null;
    }
    return p;
}

当我有一个垂直线段的问题时,这个公式

double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);

等于0,方法返回null。

如何处理此异常。

谢谢

3 个答案:

答案 0 :(得分:1)

没有特殊情况的线交叉

来自投影几何的背景,我用均匀坐标写出点:

v1 = [x1, y1, 1]
v2 = [x2, y2, 1]
v3 = [x3, y3, 1]
v4 = [x4, y4, 1]

然后,使用叉积表示连接两个点和两条线的交点的线:

[x5, y5, z5] = (v1 × v2) × (v3 × v4)

你可以将其去均匀化以找到结果点

[x5/z5, y5/z5]

无需处理任何特殊情况。如果你的线是平行的,那么最后一点会导致除零,所以你可能想要抓住这种情况。

对细分的限制

以上是无限线。如果交叉点落在边界框之外,您可能希望保留返回null的代码。但是如果你想要真正的段,那么代码是不正确的:你可以有一个交叉点,它位于一个段之外,但仍然在边界框内。

可以使用方向检查谓词来实现正确的检查。如果它们形成的三角形具有一个方向,则上面给出的三个矢量vi的行列式将具有正号,而相反方向的负号则具有负号。因此,v3v4点位于s1的不同侧,如果

det(v1, v2, v3) * det(v1, v2, v4) < 0

并以类似的方式v1v2位于s2的不同方面,如果

det(v3, v4, v1) * det(v3, v4, v2) < 0

因此,如果两者都满足,则段之间会有交集。如果您想要包含细分受端群,请在这些不等式中将<更改为

答案 1 :(得分:0)

我已经尝试过代码而不进行测试......我希望它有效! ^^

GUID

答案 2 :(得分:0)

这是我的答案。我已经通过创建一个循环来测试它的准确性,该循环检查它给出的答案是否与 Boost 几何库给出的答案相同,并且他们在每个测试上都同意,尽管我在下面写的那个要快得多Boost 中的那个。对于所有可能的线段对,测试使每个可能的线段,其中 x 是 [-3,2] 中的整数,y 是 [-3,2] 中的整数。

下面的代码认为在端点处相交的线段是相交的。 T形交叉点也被认为是相交的。代码是用 C++ 编写的,但很容易适应任何语言。它基于不同的 stackoverflow 答案,但该答案没有正确处理端点。

它使用叉积方法,该方法可以报告一个点是在给定光线的左侧还是右侧。

在数学上有一些优化需要进行,但与使用 g++ -O2 编译相比,执行这些优化并没有显示性能改进,有时甚至性能下降!编译器能够进行这些优化,所以我更愿意让代码保持可读性。

// is_left(): tests if a point is Left|On|Right of an infinite line.
//    Input:  three points p0, p1, and p2
//    Return: >0 for p2 left of the line through p0 and p1
//            =0 for p2 on the line
//            <0 for p2 right of the line
//    See: Algorithm 1 "Area of Triangles and Polygons"
//    This is p0p1 cross p0p2.
extern inline coordinate_type_fp is_left(point_type_fp p0, point_type_fp p1, point_type_fp p2) {
  return ((p1.x() - p0.x()) * (p2.y() - p0.y()) -
          (p2.x() - p0.x()) * (p1.y() - p0.y()));
}

// Is x between a and b, where a can be lesser or greater than b.  If
// x == a or x == b, also returns true. */
extern inline coordinate_type_fp is_between(coordinate_type_fp a,
                                            coordinate_type_fp x,
                                            coordinate_type_fp b) {
  return x == a || x == b || (a-x>0) == (x-b>0);
}

// https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
extern inline bool is_intersecting(const point_type_fp& p0, const point_type_fp& p1,
                                   const point_type_fp& p2, const point_type_fp& p3) {
  const coordinate_type_fp left012 = is_left(p0, p1, p2);
  const coordinate_type_fp left013 = is_left(p0, p1, p3);
  const coordinate_type_fp left230 = is_left(p2, p3, p0);
  const coordinate_type_fp left231 = is_left(p2, p3, p1);

  if (p0 != p1) {
    if (left012 == 0) {
      if (is_between(p0.x(), p2.x(), p1.x()) &&
          is_between(p0.y(), p2.y(), p1.y())) {
        return true; // p2 is on the line p0 to p1
      }
    }
    if (left013 == 0) {
      if (is_between(p0.x(), p3.x(), p1.x()) &&
          is_between(p0.y(), p3.y(), p1.y())) {
        return true; // p3 is on the line p0 to p1
      }
    }
  }
  if (p2 != p3) {
    if (left230 == 0) {
      if (is_between(p2.x(), p0.x(), p3.x()) &&
          is_between(p2.y(), p0.y(), p3.y())) {
        return true; // p0 is on the line p2 to p3
      }
    }
    if (left231 == 0) {
      if (is_between(p2.x(), p1.x(), p3.x()) &&
          is_between(p2.y(), p1.y(), p3.y())) {
        return true; // p1 is on the line p2 to p3
      }
    }
  }
  if ((left012 > 0) == (left013 > 0) ||
      (left230 > 0) == (left231 > 0)) {
    if (p1 == p2) {
      return true;
    }
    return false;
  } else {
    return true;
  }
}

测试代码:

BOOST_AUTO_TEST_CASE(small, *boost::unit_test::disabled()) {
  for (double x0 = -3; x0 < 3; x0++) {
    for (double y0 = -3; y0 < 3; y0++) {
      for (double x1 = -3; x1 < 3; x1++) {
        for (double y1 = -3; y1 < 3; y1++) {
          for (double x2 = -3; x2 < 3; x2++) {
            for (double y2 = -3; y2 < 3; y2++) {
              for (double x3 = -3; x3 < 3; x3++) {
                for (double y3 = -3; y3 < 3; y3++) {
                  point_type_fp p0{x0, y0};
                  point_type_fp p1{x1, y1};
                  point_type_fp p2{x2, y2};
                  point_type_fp p3{x3, y3};
                  linestring_type_fp ls0{p0,p1};
                  linestring_type_fp ls1{p2,p3};
                  BOOST_TEST_INFO("intersection: " << bg::wkt(ls0) << " " << bg::wkt(ls1));
                  BOOST_CHECK_EQUAL(
                      path_finding::is_intersecting(p0, p1, p2, p3),
                      bg::intersects(ls0, ls1));
                }
              }
            }
          }
        }
      }
    }
  }
}