如何判断CGPoint是否在线段上?Swift有无苹果官方内置方法支持?
苹果官方没有提供直接判断CGPoint是否落在指定线段上的一步调用API,但可以通过官方框架的现有能力组合实现,完全不需要手动处理浮点数比较、边界场景兼容等问题,以下是两种主流的官方实现方案:
方案1:使用UIBezierPath/CGPath实现(兼容性最好、容差可控)
基于CoreGraphics框架的路径命中检测能力实现,所有几何计算逻辑都由官方底层处理,还可以自定义判断容差适配不同场景:
import UIKit // macOS项目替换为AppKit,使用NSBezierPath逻辑一致 extension CGPoint { func isOnLineSegment(start: CGPoint, end: CGPoint, tolerance: CGFloat = 1.0) -> Bool { // 构造线段路径 let linePath = UIBezierPath() linePath.move(to: start) linePath.addLine(to: end) // 生成指定线宽的描边路径,线宽对应判断容差 let strokedPath = linePath.cgPath.copy( strokingWithWidth: tolerance, lineCap: .round, lineJoin: .round, miterLimit: 0 ) // 官方提供的路径命中检测 return strokedPath.contains(self) } }
- 无需手动处理竖线除零、浮点数精度对齐等边界问题,运算精度由官方保障
- 可通过
tolerance参数灵活调整容差,比如交互场景可将容差设为2-3,方便用户点击细线段 - 兼容iOS 10+/macOS 10.12+,覆盖绝大多数现网设备
方案2:使用SIMD向量运算实现(性能更高、适合批量计算)
如果是高频调用场景(比如游戏、图形渲染每帧做大量判断),可以用苹果官方SIMD库的向量运算实现,性能远高于路径检测方案:
import simd extension CGPoint { func isOnLineSegment(start: CGPoint, end: CGPoint, epsilon: CGFloat = 1e-5) -> Bool { let pointVec = SIMD2<Float>(Float(x), Float(y)) let startVec = SIMD2<Float>(Float(start.x), Float(start.y)) let endVec = SIMD2<Float>(Float(end.x), Float(end.y)) // 官方SIMD叉乘计算,判断点是否和线段共线 let crossValue = cross(endVec - startVec, pointVec - startVec) guard abs(crossValue) <= Float(epsilon) else { return false } // 官方SIMD点乘计算,判断点是否在线段区间范围内 let dotValue = dot(pointVec - startVec, pointVec - endVec) return dotValue <= Float(epsilon) } }
- 纯CPU向量指令运算,性能是路径检测方案的数倍,适合批量计算场景
- 完全规避手动计算斜率的除零、精度丢失问题,运算逻辑经过官方优化
- 兼容所有支持SIMD的苹果系统,覆盖所有现役设备
内容的提问来源于stack exchange,提问作者Senseful
相关产品推荐
相关产品推荐

