MS 关于 Fraction 的使用说明

在利用 MS 二开的时候,我们经常会等分线段,通常我们会用到 fraction 来操作,在此记录下 MS 中 fraction 小坑,以作提醒。

原理介绍

fraction 对于 B样条曲线 来说不是各段长度相等的等分点。

fraction 在开始时总是 0,然后结束时总是 1。

fraction 只有在下列基础类型时,才是线性增加的

  • line segment,线段
  • circular arc,圆或者圆弧
  • transition spirals,螺旋线

而在其它复杂类型的曲线中, fraction 的含义与曲线类型的内部参数化有关。

  • 对于有N个线段(即N+1个点)的线串 (Linestring),各个顶点的 fraction 分别为 0,1/N,2/N,…1。
  • 对于bspline 曲线,fraction 表示节点范围的分数。
  • 对于椭圆弧(elliptic arc),fraction 与角度变化成正比,公式为:X = center + vector0 * cos(theta) + vector90 * sin (theta)

源代码

在此,分享一个等分线段的算法,若有不足之处,还请指教。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/// <summary>
/// 用 fraction 获取 curve 上的点
/// 该方法将全局 fraction 换算到每根线上的 fraction 来进行计算
/// </summary>
/// <param name="curve"></param>
/// <param name="globalFraction">值在 [0,1] 之间,如果不在该区间,则会向外延伸</param>
/// <returns></returns>
public static DPoint3d GlobalFraction2Point(this CurveVector curve, double globalFraction, out DVector3d vectorAtFraction)
{
// 如果 <=0,按切线反向延长获取点
if (globalFraction <= 0)
{
curve.GetStartEnd(out DPoint3d pa, out _, out DVector3d ua, out _);
double length = curve.SumOfLengths();
DPoint3d pnt = pa + ua * length * globalFraction;
vectorAtFraction = ua;
return pnt;
}
// 正向切线延长
else if (globalFraction >= 1)
{
curve.GetStartEnd(out _, out DPoint3d pb, out _, out DVector3d ub);
double length = curve.SumOfLengths();
DPoint3d pnt = pb + ub * length * (globalFraction - 1);
vectorAtFraction = ub;
return pnt;
}

// 按 fraction 返回
double totalLength = 0;
double targetLength = globalFraction * curve.SumOfLengths();
foreach (var cp in curve)
{
cp.Length(out var length);
totalLength += length;
if(totalLength >= targetLength)
{
// 说明位于当前分段上
var distance = targetLength - totalLength;
var curveDetail = cp.PointAtSignedDistanceFromFraction(1, distance, false);
cp.FractionToPoint(curveDetail.Fraction, out DPoint3d point, out vectorAtFraction);
return point;
}
}

vectorAtFraction = DVector3d.Zero;
return DPoint3d.Zero;
}

参考

CurveLocationDetail的fraction总是得不到正确结果