在CAD二次开发中,SamplePolyline(多段线离散)与样条曲线(Spline)的离散处理,虽然在核心目标上相似——即用离散的点集来逼近连续的几何形状,但在处理对象、几何特性、离散策略、实现复杂度和应用场景上存在显著差异。理解这些区别对于选择正确的算法和优化性能至关重要。
核心差异对比
下表从多个维度对两者进行了系统性对比:
| 对比维度 | 多段线 (Polyline) 离散 | 样条曲线 (Spline) 离散 |
|---|---|---|
| 几何构成 | 由线性段(Line)和圆弧段(Arc)顺序连接而成。每个段有明确的解析几何定义(起点、终点、圆心、半径等)。 | 由控制点、阶数、节点向量等参数定义的自由曲线(如NURBS)。没有直接的“圆弧”概念,形状由数学公式插值或逼近控制点生成。 |
| 参数空间 | 参数t通常与顶点索引和段内位置相关。对于直线段,参数与长度线性相关;对于圆弧段,参数与角度线性相关。 | 参数u定义在节点向量区间内,与曲线长度非线性关系更复杂,需要通过积分计算弧长。 |
| 离散策略重点 | 分段处理。可以针对直线段(简单)和圆弧段(需要特殊处理)采用不同的、高效的离散逻辑。 | 全局自适应。由于曲线处处可导,通常采用统一的递归细分或弦高误差法,沿整个参数区间进行采样。 |
| 获取点的方法 | 对于直线段:线性插值。对于圆弧段:CircularArc3d.EvaluatePoint(angle)。 | Spline.EvaluatePoint(u),根据参数u计算曲线上对应的Point3d。 |
| 精度控制 | 对圆弧段,弦高误差法是精确且高效的标准方法。可以显式计算给定圆弧段的弦高。 | 同样采用弦高误差法,但需要数值计算当前弦对应的近似弦高,通常通过评估曲线段中点与弦中点的距离来估算。 |
| 实现复杂度 | 相对较低。逻辑清晰,可以分而治之。 | 较高。需要理解样条曲线的参数化、阶数、节点向量等概念,且弧长计算、等弦长采样等操作需要数值积分,计算开销大。 |
应用场景与实现代码示例
1. 多段线离散(基于弦高误差的自适应采样 - 优化版)
此方法优先识别多段线的段类型,并对圆弧段进行递归细分,精度高且效率好。
/// <summary> /// 针对多段线的自适应离散(支持直线和圆弧段) /// </summary> public List<Point3d> SamplePolylineAdaptive(Polyline pl, double maxChordHeight) { List<Point3d> sampledPoints = new List<Point3d>(); if (pl == null || pl.NumberOfVertices < 2) return sampledPoints; int numVertices = pl.NumberOfVertices; bool isClosed = pl.Closed; // 添加起点 sampledPoints.Add(pl.GetPoint3dAt(0)); for (int i = 0; i < numVertices; i++) { // 获取当前段的类型 SegmentType segType = pl.GetSegmentType(i); // 获取当前段的曲线对象(可能为null,需判断) Curve segCurve = pl.GetSegmentAt(i); if (segCurve == null) continue; Point3d segmentStart = sampledPoints.Last(); if (segType == SegmentType.Line) { // 直线段:直接添加终点即可 Point3d segmentEnd = segCurve.EndPoint; sampledPoints.Add(segmentEnd); } else if (segType == SegmentType.Arc) { // 圆弧段:进行自适应递归细分 CircularArc3d arc = segCurve as CircularArc3d; if (arc != null) { AdaptiveSampleArcRecursive(arc, maxChordHeight, sampledPoints); } } // 注意:多段线也可能包含样条曲线段(SegmentType.Spline),但较少见。 // 若包含,可调用SampleSplineAdaptive方法处理。 } // 如果多段线闭合,移除最后一个与起点重复的点 if (isClosed && sampledPoints.Count > 1 && sampledPoints.First().DistanceTo(sampledPoints.Last()) < Tolerance.Global.EqualPoint) { sampledPoints.RemoveAt(sampledPoints.Count - 1); } return sampledPoints; } /// <summary> /// 递归自适应采样圆弧段 /// </summary> private void AdaptiveSampleArcRecursive(CircularArc3d arc, double tolerance, List<Point3d> points) { // 计算弦长和弦高 double chordLength = arc.StartPoint.DistanceTo(arc.EndPoint); Point3d chordMidPoint = (arc.StartPoint + arc.EndPoint) / 2.0; Point3d arcMidPoint = arc.EvaluatePoint((arc.StartAngle + arc.EndAngle) / 2.0); double chordHeight = chordMidPoint.DistanceTo(arcMidPoint); if (chordHeight <= tolerance || chordLength < 1e-6) { // 满足精度要求或弦长极短,添加终点 points.Add(arc.EndPoint); } else { // 不满足精度,从中间点分割圆弧,递归处理两半 double midAngle = (arc.StartAngle + arc.EndAngle) / 2.0; Point3d midPt = arc.EvaluatePoint(midAngle); // 创建左半弧和右半弧 CircularArc3d leftArc = new CircularArc3d(arc.Center, arc.Normal, arc.ReferenceVector, arc.StartAngle, midAngle); CircularArc3d rightArc = new CircularArc3d(arc.Center, arc.Normal, arc.ReferenceVector, midAngle, arc.EndAngle); AdaptiveSampleArcRecursive(leftArc, tolerance, points); AdaptiveSampleArcRecursive(rightArc, tolerance, points); } }2. 样条曲线离散(基于弦高误差的自适应采样)
样条曲线的离散需要在整个参数区间内进行递归评估。
/// <summary> /// 针对样条曲线的自适应离散 /// </summary> public List<Point3d> SampleSplineAdaptive(Spline spline, double maxChordHeight, double minParamStep = 1e-6) { List<Point3d> sampledPoints = new List<Point3d>(); if (spline == null || spline.Degree < 1) return sampledPoints; double startParam = spline.StartParameter; double endParam = spline.EndParameter; // 添加起点 sampledPoints.Add(spline.EvaluatePoint(startParam)); // 开始递归细分 AdaptiveSampleSplineRecursive(spline, startParam, endParam, maxChordHeight, minParamStep, sampledPoints); // 添加终点 sampledPoints.Add(spline.EvaluatePoint(endParam)); return sampledPoints; } /// <summary> /// 递归自适应采样样条曲线段 /// </summary> private void AdaptiveSampleSplineRecursive(Spline spline, double u1, double u2, double tolerance, double minStep, List<Point3d> points) { // 计算当前参数区间中点 double uMid = (u1 + u2) / 2.0; // 如果参数步长已经非常小,则停止递归以避免无限循环和精度问题 if (Math.Abs(u2 - u1) < minStep) { return; } // 获取三个关键点 Point3d p1 = spline.EvaluatePoint(u1); Point3d p2 = spline.EvaluatePoint(u2); Point3d pMid = spline.EvaluatePoint(uMid); // 计算弦中点 Point3d chordMidPoint = (p1 + p2) / 2.0; // 估算弦高(曲线中点到弦中点的距离) double chordHeight = chordMidPoint.DistanceTo(pMid); if (chordHeight <= tolerance) { // 满足精度,无需进一步细分,当前段用起点和终点足以表示 // 注意:这里不添加点,因为起点和终点由上层调用者或初始调用添加 return; } else { // 不满足精度,需要继续细分 // 先递归处理左半区间 [u1, uMid] AdaptiveSampleSplineRecursive(spline, u1, uMid, tolerance, minStep, points); // 添加中点(这是细分产生的新点) points.Add(pMid); // 再递归处理右半区间 [uMid, u2] AdaptiveSampleSplineRecursive(spline, uMid, u2, tolerance, minStep, points); } }混合处理与选择策略
在实际项目中,一个复杂图形可能同时包含多段线和样条曲线。通用的离散流程应能智能判断对象类型并分派给相应的处理方法。
/// <summary> /// 通用曲线离散入口函数 /// </summary> public List<Point3d> SampleCurveAdaptive(Curve curve, double tolerance) { if (curve is Polyline pl) { // 处理多段线 return SamplePolylineAdaptive(pl, tolerance); } else if (curve is Spline spl) { // 处理样条曲线 return SampleSplineAdaptive(spl, tolerance); } else if (curve is Line || curve is Arc || curve is Circle) { // 处理简单曲线:可以采用更简单或更精确的方法 // 例如对于圆弧,可以直接使用基于角度的均匀采样或弦高法 List<Point3d> points = new List<Point3d>(); double startParam = curve.StartParam; double endParam = curve.EndParam; // 简单实现:按参数均匀采样(对于简单曲线通常足够) int numSamples = 50; // 或根据曲线长度动态计算 for (int i = 0; i <= numSamples; i++) { double param = startParam + (endParam - startParam) * i / numSamples; points.Add(curve.GetPointAtParameter(param)); } return points; } else { // 其他类型曲线,降级为按参数均匀采样 List<Point3d> points = new List<Point3d>(); double startParam = curve.StartParam; double endParam = curve.EndParam; int numSamples = Math.Max(10, (int)(curve.GetDistanceAtParameter(endParam) / tolerance)); for (int i = 0; i <= numSamples; i++) { double param = startParam + (endParam - startParam) * i / numSamples; points.Add(curve.GetPointAtParameter(param)); } return points; } }性能与精度权衡的实践建议
- 针对多段线:始终优先采用基于段类型的自适应弦高误差法。它避免了在直线段上过度采样,同时确保了圆弧段的精度,是效率与精度兼顾的最佳选择。
- 针对样条曲线:自适应递归细分(如上述代码)是标准方法。需要注意设置最小参数步长(
minParamStep)以防止在曲线非常平坦但参数区间很长的特殊情况下陷入过深递归或无限循环。 - 精度容差的选择:
maxChordHeight(弦高容差)的选择直接影响点集数量和质量。通常,该值设置为图形显示精度或后续几何计算精度的1/10到1/5。例如,对于工程制图,0.01图形单位可能是一个合理的起点。 - 缓存与重用:如果同一条曲线需要被多次离散(例如在循环中),应考虑缓存离散结果,避免重复计算。
- 并行化:对于超长曲线或批量处理,离散化过程(尤其是样条曲线的递归细分)可以尝试并行化,但需要注意线程安全和集合同步。
总而言之,多段线离散因其分段组合的特性,允许更精细和高效的针对性优化;而样条曲线离散则更依赖于统一的数值方法。在CAD二次开发中,根据待处理曲线的具体类型选择正确的离散策略,是保证功能正确性和运行效率的关键。