iOS开发:检测多边形线段是否被橡皮擦路径完全覆盖
iOS 多边形线段被橡皮擦路径完全覆盖的可靠检测方案
需求回顾
现有n边形模型存储所有线段坐标,用户在橡皮擦模式下手绘宽线浅蓝色路径,需检测并高亮所有被该路径完全覆盖的多边形线段(高亮为绿色),后续可删除;无覆盖线段时不选中任何线段。仅通过线段起点终点无法可靠判断覆盖情况,需实现精准检测逻辑。
核心实现思路
橡皮擦的宽线路径实际覆盖区域是沿线扩展的填充轮廓,不能仅验证端点,需确保线段全程处于橡皮擦的填充区域内。通过以下步骤实现可靠检测:
1. 生成橡皮擦路径的实际碰撞区域
利用Core Graphics的CGPathCreateCopyByStrokingPath,将手绘的橡皮擦路径转换为填充式的轮廓路径,这个路径就是橡皮擦宽线实际覆盖的区域:
// eraserPath:用户手绘的CGPathRef;eraserLineWidth:橡皮擦的线宽 CGPathRef eraserCollisionPath = CGPathCreateCopyByStrokingPath(eraserPath, NULL, eraserLineWidth, kCGLineCapRound, kCGLineJoinRound, 0);
2. 精准验证线段的完全覆盖
对每条多边形线段,通过包围盒快速过滤+密集采样点验证的方式判断是否被完全覆盖:
- 快速过滤:先判断线段的包围盒是否被橡皮擦碰撞区域的包围盒包含,直接排除不可能的线段,提升性能
- 密集采样验证:按橡皮擦线宽的比例在线段上采样多个点(至少包含端点、中点),确保每个采样点都在橡皮擦碰撞区域内
示例代码:
BOOL isSegmentFullyCovered(CGPoint start, CGPoint end, CGPathRef eraserCollisionPath, CGFloat eraserLineWidth) { // 计算线段包围盒 CGRect segmentBounds = CGRectMake(MIN(start.x, end.x), MIN(start.y, end.y), ABS(end.x - start.x), ABS(end.y - start.y)); // 计算橡皮擦碰撞区域包围盒 CGRect eraserBounds = CGPathGetBoundingBox(eraserCollisionPath); // 快速排除:线段包围盒不在橡皮擦区域内则直接返回NO if (!CGRectContainsRect(eraserBounds, segmentBounds)) { return NO; } // 计算采样点数量:根据线段长度和橡皮擦线宽,确保采样密度足够覆盖所有区域 CGFloat segmentLength = hypot(end.x - start.x, end.y - start.y); NSInteger sampleCount = MAX(3, (NSInteger)(segmentLength / (eraserLineWidth * 0.5)) + 1); // 遍历所有采样点,只要有一个点不在碰撞区域内,就说明未完全覆盖 for (NSInteger i = 0; i < sampleCount; i++) { CGFloat progress = (CGFloat)i / (sampleCount - 1); CGPoint samplePoint = CGPointMake(start.x + progress * (end.x - start.x), start.y + progress * (end.y - start.y)); if (!CGPathContainsPoint(eraserCollisionPath, NULL, samplePoint, NO)) { return NO; } } return YES; }
3. 边界与性能优化
- 线宽适配:确保橡皮擦线宽大于多边形线段的线宽,避免因线宽过小导致覆盖检测失效
- 圆角兼容:如果橡皮擦路径使用了圆角线帽/线接,
CGPathCreateCopyByStrokingPath会自动生成带圆角的碰撞区域,无需额外处理 - 批量处理:对大量线段,先通过包围盒过滤减少需要精细验证的线段数量,提升检测效率
后续流程
- 遍历所有多边形线段,调用上述方法检测是否被完全覆盖
- 标记所有返回YES的线段为选中状态,绘制时用绿色高亮显示
- 删除操作时,直接从模型中移除标记的线段即可
内容的提问来源于stack exchange,提问作者iOS
相关产品推荐
相关产品推荐

