ios - 计算CGPoint是否在线段上?

标签 ios swift geometry

我正在寻找一种方法来计算 CGPoint 是否在线段上。 Swift 或 Apple 的任何库中是否有内置方法?

我不介意转换为不同的类型,只要它是 Apple 代码的一部分(例如类似于 instead of performing complex calculations on double4x4 matrix, you can convert it to an SCNNode )。


我很快就想出了强力解决方案,但它既不优雅,也不准确(考虑到到处都是浮点比较):

extension CGPoint {
  func isOnLineSegment(start: CGPoint, end: CGPoint) -> Bool {
    func value(_ value: CGFloat, isBetween one: CGFloat, and two: CGFloat) -> Bool {
      let a = [one, two].sorted()
      return a[0] <= value && value <= a[1]
    }

    let slope = (end.y - start.y) / (end.x - start.x)
    if slope.isInfinite {
      let isOnLine = x.isNearlyEqual(to: start.x)
      let isWithinYValues = value(y, isBetween: start.y, and: end.y)
      return isOnLine && isWithinYValues
    }

    let isOnLine = y.isNearlyEqual(to: slope * (x - start.x) + start.y) // y = m * (x - x1) + y1
    let isWithinXValues = value(x, isBetween: start.x, and: end.x)

    return isOnLine && isWithinXValues
  }
}

最佳答案

一种可能的方法是变换整个配置,使得线段的起点变换为原点,终点变换为点(1, 0)。这可以通过 CGAffineTransformation 来完成。然后,问题就简化为确定给定点是否位于 (0, 0) 和 (1,0) 之间的线段上。这是一个可能的实现:

extension CGPoint {
    func isOnLineSegment(start: CGPoint, end: CGPoint) -> Bool {
        // Transformation which maps (0,0) to start and (1, 0) to end:
        let t = CGAffineTransform(a: end.x - start.x, b: end.y - start.y,
                                  c: start.y - end.y, d: end.x - start.x,
                                  tx: start.x, ty: start.y)
        // Apply the inverse transformation to our point:
        let q = self.applying(t.inverted())
        // Check (with some tolerance) if q is on the segment from (0, 0) to (1, 0):
        let eps = CGFloat.ulpOfOne.squareRoot()
        return q.x > -eps && q.x < 1.0 + eps && q.y.magnitude < eps 
    }
}

作为容差,我选择 .ulpOfOne 的平方根(如建议的 here ),但您可以根据您的需要进行调整。

另一种方法是利用点 P 正好位于从 P1 到 P2 的线段上这一事实

| P - P1 | + | P - P2 | = | P1 - P2 |

其中| 。 | 表示(欧几里德)距离:

extension CGPoint {
    func distance(to p: CGPoint) -> CGFloat {
        return hypot(p.x - self.x, p.y - self.y)
    }
    func isOnLineSegment(start: CGPoint, end: CGPoint) -> Bool {
        let eps = CGFloat.ulpOfOne.squareRoot()
        return self.distance(to: start) + self.distance(to: end) <= start.distance(to: end) + eps
    }
}

关于ios - 计算CGPoint是否在线段上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70105593/

相关文章:

ios - 表达式类型 '(_) -> _ ' 在没有更多上下文的情况下不明确

geometry - 如何使用 OSMDROID 制作 161 米/528 英尺的圆圈?

ios - 如何在 objective-c 中解析数组 JSON

android - 如何在 React Native 中创建自定义底部标签栏?

ios - 可以创建 UIWindow 来掩盖推送通知的自动转场吗?

swift - 如何在不使项目变灰的情况下禁用 UITabBarItem

ruby - 旋转一组物体坐标

java - 如何找到椭圆和线之间的交点?

ios - 在 Core Data 中存储时间间隔的正确方法是什么?

ios - 过渡动画中的闪烁(模拟器?)