假设我有一条三次贝塞尔曲线(我知道它的长度)。我如何将它分成相等的部分,比方说,三个相等的部分?
换句话说,我如何计算将它分成三个相等部分的
t
参数?在 t = 0.33 和 t = 0.66 时拆分不会产生相等的段。事实上,在对称曲线上除 0.5 以外的任何 t 处分裂都不会产生相等的线段。
实际曲线上 t = 0.33 和 t = 0.66 的示例:
我没有任何神奇的方程式来确定 X(0) 和 X(t) 的贝塞尔曲线长度之间的关系。
我能建议的最好的方法是通过积分计算长度(通过递归,将贝塞尔曲线分成 2 直到它“很小”),然后计算相同的积分停止在有趣的距离(第一次计算的一小部分距离)。
在 OCaml 中,这很容易完成。请注意,bezier_stop 取 4 个点 - a、b、c、d - 一个 current_length(a 之前贝塞尔曲线段的长度)和阈值。这个想法是,如果所需的点在贝塞尔曲线中,它将返回
Return t
其中 t
是你想要的(相对于当前的贝塞尔曲线......调用者必须缩放这个值)。如果所需点在 d 点之后(您的贝塞尔曲线太短),则返回 Length l
其中 l
是当前贝塞尔曲线的长度。
你会发现一个让你大吃一惊的
Return 0.5
。在所需点的非常接近的邻居处(在我们拒绝进一步挖掘的点),我们无法获得精确的 t
。这并不重要,因为每次递归函数返回时,不精确度都会除以 2。
此处,程序计算长度为 2.0 和 len-2.0 的
t
。由于建议的曲线是对称的,总和必须为 1。它超过 0.00036,但如果我们想要更精确的值,我们可以减少 epsilon。
let epsilon = 0.001
let middle (x,y) (x',y') = ((x+.x')/.2.,(y+.y')/.2.)
let closed (x,y) (x',y') = Float.abs(x-.x') +. Float.abs(y-.y') < epsilon
let square x = x *. x
let distance (x,y) (x',y') = Float.sqrt (square (x-.x')+.square (y-.y'))
let rec bezier_length a b c d =
if closed a d then
distance a d
else
let e = middle a b in
let f = middle b c in
let g = middle c d in
let h = middle e f in
let i = middle f g in
let j = middle h i in
bezier_length a e h j +. bezier_length j i g d
type r = Length of float | Result of float
let rec bezier_stop a b c d current_len threshold =
if closed a d then
let dl = distance a d in
Length dl
else
let e = middle a b in
let f = middle b c in
let g = middle c d in
let h = middle e f in
let i = middle f g in
let j = middle h i in
match bezier_stop a e h j current_len threshold with
| Result r -> Result (r /. 2.)
| Length l -> begin
if l < current_len -. threshold then Result 0.5 else
match bezier_stop j i g d (current_len +. l) threshold with
| Result r' -> Result ((r' +. 1.)/. 2.)
| Length l' -> Length (l +. l')
end
let a = (0. , 0.)
let b = (0. , 1.)
let c = (10. , 1.)
let d = (10. , 0.)
let len = (bezier_length a b c d)
let () =
(match bezier_stop a b c d 0. 2. with
Result x -> print_float x
| Length _ -> print_string "not found");
print_string "\n";
(match bezier_stop a b c d 0. (len -. 2.) with
Result x -> print_float x
| Length _ -> print_string "not found")