如何将三次贝塞尔曲线分成相等的段?

问题描述 投票:0回答:1

假设我有一条三次贝塞尔曲线(我知道它的长度)。我如何将它分成相等的部分,比方说,三个相等的部分?

换句话说,我如何计算将它分成三个相等部分的

t
参数?在 t = 0.33 和 t = 0.66 时拆分不会产生相等的段。事实上,在对称曲线上除 0.5 以外的任何 t 处分裂都不会产生相等的线段。

实际曲线上 t = 0.33 和 t = 0.66 的示例:

bezier cubic-bezier
1个回答
0
投票

我没有任何神奇的方程式来确定 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")
© www.soinside.com 2019 - 2024. All rights reserved.