# rscvn


分段双圆弧 Hermite 插值

函数库: TyCurveFitting

# 语法

c = rscvn(p, u)
c = rscvn(p)

# 说明

c=rscvn(p , u) 以二次的 rB 型返回一条平面分段双圆弧曲线,该曲线依序穿过给定点 p[:,j],且由以下形式构造。在两个不同点 p[:,j]、p[:,j+1] 之间,曲线由两条连续相切的圆弧构成(其中也包括直线),其中第一条圆弧起于 p[:,j] 且在此点与 u[:,j] 正交,第二条圆弧终于 p[:,j+1] 且在此点与 u[:,j+1] 正交,这两条圆弧如果能写成一条就会写成一条。因此,该曲线几乎处处可微,除了在重复的点(这些点可能为角点)或者交于点 p[:,j] 的两圆弧片段夹角过小(这些点可能为尖点)。示例

p 必须是两行至少两列的实矩阵,所有列与相邻列至少有一点不同。

u 必须是两行实矩阵,列数与 p 相同(例外情形如下)。u 可以有 0 列。


c=rscvn(p) 使用以下方式选择 u。对于 j=2:end-1,u[:,j] 为向量 p[:,j]-p[:,j-1] 与向量 p[:,j+1]-p[:,j] 的右行正规法向量的均值。如果 p[:,1]=p[:,end],则两端的正交向量分别选为 p[:,2]-p[:,1] 和 p[:,end]-p[:,end-1] 的法向量,这样可以在输出曲线中避免角点。如若不满足,那么端点法向量将会选择使得在两端的段中仅有一条圆弧(非扭结边界条件)。示例

如果 u 有两列,rscvn(p,u) 则会在内部使用内部正交向量(rscvn(p)),而在端点使用 u 的两列作为法向量。

# 示例

圆的构造

下面的代码使用4段分支就构造了一个圆。仅阶点序列的放缩倍数不同,这与 rsmak("circle", 1, [1;1]) 的结果相同。

using TyCurveFitting
using TyPlot
p = [1 0 -1 0 1; 0 1 0 -1 0]
q = [p[1, :] .+ 1 p[2, :] .+ 1]'
c = rscvn([p[1, :] .+ 1 p[2, :] .+ 1]', p)

使用两段分支也可以构造同样的圆

c2 = rscvn([0 2 0; 1 1 1])

绘制这两个圆,其中红色粗线为 c 表示的圆,蓝色细线为 c2 表示的圆,可以发现这两个圆完全重合。

fnplt(c,5,"r")
hold("on")
fnplt(c2,1,"b")
legend(["circle 1", "circle 2"])
axis("equal")
字母的构造

接下来的代码画了两个字母。注意其中第二个字母仅为四个点的插值,注意第二个字母绘制时使用 fncmb 进行平移。

using TyCurveFitting
using TyPlot
using TyBase
p = [-1 .8 -1 1 -1 -1 -1; 3 1.75 0.5 -1.25 -3 -3 3]
i = eye(2)
u = i[:, [2, 1, 2, 1, 2, 1, 1]]
B = rscvn(p, u)
S = rscvn([1 -1 1 -1; 2.5 2.5 -2.5 -2.5])
fnplt(B)
hold("on")
fnplt(fncmb(S, [3; 0]))
hold("off")
axis("equal")
axis("off")
双圆弧的构造

以下的代码展示了双圆弧的构造,注意fntlr对圆弧端点法向量的寻找的使用,这些点处两端圆弧相交。

using TyCurveFitting
using TyPlot
p = [0 1; 0 0]
u = [0.5 -.1; -.25 0.5]
plot(p[1, :], p[2, :], color = "k")
hold("on")
biarc = rscvn(p, u)
breaks = fnbrk(biarc, "b")
fnplt(biarc,breaks[1:2],"b",3)
fnplt(biarc,breaks[2:3],"r",3)
vd = fntlr(biarc, breaks, 2)
quiver(vd[1,:],vd[2,:],vd[4,:],-vd[3,:], length=1)
hold("off")

# 输入参数

p - 输入矩阵
矩阵

必须是两行至少两列的实矩阵,所有列与相邻列至少有一点不同。

数据类型: Int | Float

u - 输入矩阵
矩阵

u 必须是两行实矩阵,列数与 p 相同(有例外情形)。u 可以有 0 列。

数据类型: Int | Float

# 输出参数

c - 样条结构体
样条结构体

pp型的样条,为带有以下元素的结构体。

Form - 样条格式
"rb"

样条的格式,以 "rb" 返回。"rb" 样条是以分段多项式的形式给出。

数据类型: String

Breaks-样条节点位置
向量 | 数组元组

样条节点位置,单变元时以向量,多变元时以向量元组形式返回。向量包含严格增元素,逐点分别表示各段多项式定义区间的左右端点。

数据类型: Int | Float

Coefs - 多项式的系数
矩阵 | 数组

各段多项式的系数,单变元时以矩阵,多变元时以数组形式返回。

数据类型: Int | Float

Pieces - 多项式分段段数
标量 | 向量

描述样条的多项式分段的段数,单变元时以标量,多变元时以各变元段数组成的向量形式返回。

数据类型: Int

Order - 多项式阶数
标量 | 向量

描述样条的分段多项式的阶数,单变元时以标量,多变元时以各变元阶数组成的向量形式返回。

数据类型: Int

Dim - 维数
标量

目标函数的维数,以标量形式返回。

数据类型: Int

# 算法

给定平面的两个不同点,p1 和 p2,以及对应的非零向量 u1 和 h2,则存在单参变族双圆弧(即,两条圆弧组成的曲线,且交点有相同的切线),起点为 p1,与 u1 正交,终点为 p2,与 u2 正交。一种参数化这个双圆弧族的方法是通过在角点 q 的正交方向 v。当取了非零向量 v,则有且仅有一个满足条件的 q,因此此时整条双圆弧就被决定了。rscvn 使用的方法中,v 被选为对从 p1 到 p2 连接线段的垂线的向量 u1 和 u2 的平均向量的反射,这种反射是在把 u1 和 u2 正规化以后,即使它们的长度为 1 且指向从 p1 到 p2 线段的右侧。这种 v 的选择在以下两种标准情形下是自然的:(i) u2 是 u1 对从 p1 到 p2 线段的垂线的反射;(ii)u1 与 u2 平行。这种 v 的选择在例子“作为左正交向量函数的双圆弧”中使用,该例展示了当 p1,p2,u2=[.809;-.588] 保持不变,仅 p1 处的正交向量发生变化时的双圆弧,代码及结果如下:

作为左正交向量函数的双圆弧

using TyCurveFitting
using TyPlot
p = [0 1; 0 0]
x = LinRange(-0.809,0.588,10)
for i = 1:length(x)
    u = [-sqrt(1-x[i]^2) 0.809; x[i] -0.588]
    biarc = rscvn(p, u)
    fnplt(biarc,"b")
    hold("on")
    u = [x[i] 0.809; sqrt(1-x[i]^2) -0.588]
    biarc = rscvn(p, u)
    fnplt(biarc, "b")
end
hold("off")
axis("equal")
axis("off")

但是希望让双圆弧对所有的四个数据 p1,p2,u1,u2 连续依赖是不可能的。在 u1,u2 从 p1 到 p2 的方向上越过的时候,正交向量会出现不连续。下例“作为一端端点函数的双圆弧”展示了当一个点 p1=[0;0],和两个正交向量 u1=[1;1] 和 u2=[1;-1] 固定,仅第二个点 p2 在 p1 的圆上发生变动时产生的双圆弧。

作为一端端点函数的双圆弧

using TyCurveFitting
using TyPlot
u = [1 1;1 -1]
x = LinRange(-1,1,20)
for i = 1:length(x)
    p = [0 x[i]; 0 sqrt(1 - x[i]^2)]
    biarc = rscvn(p, u)
    fnplt(biarc, "b")
    hold("on")
    p = [0 x[i]; 0 -sqrt(1 - x[i]^2)]
    biarc = rscvn(p, u)
    fnplt(biarc, "b")
    p = [0 sqrt(1 - x[i]^2); 0 x[i]]
    biarc = rscvn(p, u)
    fnplt(biarc, "b")
    p = [0 -sqrt(1 - x[i]^2); 0 x[i]]
    biarc = rscvn(p, u)
    fnplt(biarc, "b")
end
hold("off")
axis("equal")
axis("off")

# 另请参阅

rsmak | cscvn