2008-08-13 97 views
9

目前,我試圖讓多個貝塞爾曲線具有等距點。我目前使用三次插值來找到點,但是因爲貝塞爾曲線的工作方式,某些區域比其他區域更密集,並且由於距離可變,所以證明了紋理映射的總量。 有沒有辦法按距離而不是按百分比找到貝塞爾上的點?此外,是否有可能將其擴展到多條連接的曲線?Bezier曲線上的等距點

+0

也http://math.stackexchange.com/a/61796/589見。 – lhf 2016-01-13 12:09:59

回答

4

P_0和P_3之間的距離(以立方形式),是的,但我認爲你知道這一點,是直截了當的。

曲線上的距離僅僅是弧長:

fig 1 http://www.codecogs.com/eq.latex?%5Cint_%7Bt_0%7D%5E%7Bt_1%7D%20%7B%20|P'(t)|%20dt

其中:

fig 2 http://www.codecogs.com/eq.latex?P%27(t)%20=%20[%7Bx%27,y%27,z%27%7D]%20=%20[%7B%5Cfrac%7Bdx(t)%7D%7Bdt%7D,%5Cfrac%7Bdy(t)%7D%7Bdt%7D,%5Cfrac%7Bdz(t)%7D%7Bdt%7D%7D]

(see the rest)

也許,你必須T_0 = 0,T -1 = 1.0,並且dz(t)= 0(2d平面)。

+5

這就是你如何找到給定參數的弧長,但是找到等距點需要這個函數的反函數。從一個到另一個不是微不足道的。 @Christian Romo:你是怎麼做到的?我的意思是,你可以使用二分搜索,但這會非常慢(對於我正在嘗試做的事情)。 – CromTheDestroyer 2010-11-19 04:18:21

9

這被稱爲「弧長」參數化。我幾年前寫了一篇關於這一點:

http://www.saccade.com/writing/graphics/RE-PARAM.PDF

的想法是預先計算「參數」曲線,並評估通過的曲線。

+0

尚未完整閱讀論文。但是我想問一下,是否有更好的方法來定義不需要首先「轉換」的曲線。例如。你知道我是否使用NURBS來定義所有路徑/曲線,它是否支持更快的等距弧長參數化?或者以其他方式?編輯:更快我的意思是使用CPU或GPU。 – Ciantic 2013-05-11 12:49:50

2

我知道這是一個老問題,但我最近碰到這個問題,並創建了一個UIBezierPath延伸解決X座標給定Y座標,反之亦然。寫在迅速。

https://github.com/rkotzy/RKBezierMath

extension UIBezierPath { 

func solveBezerAtY(start: CGPoint, point1: CGPoint, point2: CGPoint, end: CGPoint, y: CGFloat) -> [CGPoint] { 

    // bezier control points 
    let C0 = start.y - y 
    let C1 = point1.y - y 
    let C2 = point2.y - y 
    let C3 = end.y - y 

    // The cubic polynomial coefficients such that Bez(t) = A*t^3 + B*t^2 + C*t + D 
    let A = C3 - 3.0*C2 + 3.0*C1 - C0 
    let B = 3.0*C2 - 6.0*C1 + 3.0*C0 
    let C = 3.0*C1 - 3.0*C0 
    let D = C0 

    let roots = solveCubic(A, b: B, c: C, d: D) 

    var result = [CGPoint]() 

    for root in roots { 
     if (root >= 0 && root <= 1) { 
      result.append(bezierOutputAtT(start, point1: point1, point2: point2, end: end, t: root)) 
     } 
    } 

    return result 
} 

func solveBezerAtX(start: CGPoint, point1: CGPoint, point2: CGPoint, end: CGPoint, x: CGFloat) -> [CGPoint] { 

    // bezier control points 
    let C0 = start.x - x 
    let C1 = point1.x - x 
    let C2 = point2.x - x 
    let C3 = end.x - x 

    // The cubic polynomial coefficients such that Bez(t) = A*t^3 + B*t^2 + C*t + D 
    let A = C3 - 3.0*C2 + 3.0*C1 - C0 
    let B = 3.0*C2 - 6.0*C1 + 3.0*C0 
    let C = 3.0*C1 - 3.0*C0 
    let D = C0 

    let roots = solveCubic(A, b: B, c: C, d: D) 

    var result = [CGPoint]() 

    for root in roots { 
     if (root >= 0 && root <= 1) { 
      result.append(bezierOutputAtT(start, point1: point1, point2: point2, end: end, t: root)) 
     } 
    } 

    return result 

} 

func solveCubic(a: CGFloat?, var b: CGFloat, var c: CGFloat, var d: CGFloat) -> [CGFloat] { 

    if (a == nil) { 
     return solveQuadratic(b, b: c, c: d) 
    } 

    b /= a! 
    c /= a! 
    d /= a! 

    let p = (3 * c - b * b)/3 
    let q = (2 * b * b * b - 9 * b * c + 27 * d)/27 

    if (p == 0) { 
     return [pow(-q, 1/3)] 

    } else if (q == 0) { 
     return [sqrt(-p), -sqrt(-p)] 

    } else { 

     let discriminant = pow(q/2, 2) + pow(p/3, 3) 

     if (discriminant == 0) { 
      return [pow(q/2, 1/3) - b/3] 

     } else if (discriminant > 0) { 
      let x = crt(-(q/2) + sqrt(discriminant)) 
      let z = crt((q/2) + sqrt(discriminant)) 
      return [x - z - b/3] 
     } else { 

      let r = sqrt(pow(-(p/3), 3)) 
      let phi = acos(-(q/(2 * sqrt(pow(-(p/3), 3))))) 

      let s = 2 * pow(r, 1/3) 

      return [ 
       s * cos(phi/3) - b/3, 
       s * cos((phi + CGFloat(2) * CGFloat(M_PI))/3) - b/3, 
       s * cos((phi + CGFloat(4) * CGFloat(M_PI))/3) - b/3 
      ] 

     } 

    } 
} 

func solveQuadratic(a: CGFloat, b: CGFloat, c: CGFloat) -> [CGFloat] { 

    let discriminant = b * b - 4 * a * c; 

    if (discriminant < 0) { 
     return [] 

    } else { 
     return [ 
      (-b + sqrt(discriminant))/(2 * a), 
      (-b - sqrt(discriminant))/(2 * a) 
     ] 
    } 

} 

private func crt(v: CGFloat) -> CGFloat { 
    if (v<0) { 
     return -pow(-v, 1/3) 
    } 
    return pow(v, 1/3) 
} 

private func bezierOutputAtT(start: CGPoint, point1: CGPoint, point2: CGPoint, end: CGPoint, t: CGFloat) -> CGPoint { 

    // bezier control points 
    let C0 = start 
    let C1 = point1 
    let C2 = point2 
    let C3 = end 

    // The cubic polynomial coefficients such that Bez(t) = A*t^3 + B*t^2 + C*t + D 
    let A = CGPointMake(C3.x - 3.0*C2.x + 3.0*C1.x - C0.x, C3.y - 3.0*C2.y + 3.0*C1.y - C0.y) 
    let B = CGPointMake(3.0*C2.x - 6.0*C1.x + 3.0*C0.x, 3.0*C2.y - 6.0*C1.y + 3.0*C0.y) 
    let C = CGPointMake(3.0*C1.x - 3.0*C0.x, 3.0*C1.y - 3.0*C0.y) 
    let D = C0 

    return CGPointMake(((A.x*t+B.x)*t+C.x)*t+D.x, ((A.y*t+B.y)*t+C.y)*t+D.y) 
} 

// TODO: - future implementation 
private func tangentAngleAtT(start: CGPoint, point1: CGPoint, point2: CGPoint, end: CGPoint, t: CGFloat) -> CGFloat { 

    // bezier control points 
    let C0 = start 
    let C1 = point1 
    let C2 = point2 
    let C3 = end 

    // The cubic polynomial coefficients such that Bez(t) = A*t^3 + B*t^2 + C*t + D 
    let A = CGPointMake(C3.x - 3.0*C2.x + 3.0*C1.x - C0.x, C3.y - 3.0*C2.y + 3.0*C1.y - C0.y) 
    let B = CGPointMake(3.0*C2.x - 6.0*C1.x + 3.0*C0.x, 3.0*C2.y - 6.0*C1.y + 3.0*C0.y) 
    let C = CGPointMake(3.0*C1.x - 3.0*C0.x, 3.0*C1.y - 3.0*C0.y) 

    return atan2(3.0*A.y*t*t + 2.0*B.y*t + C.y, 3.0*A.x*t*t + 2.0*B.x*t + C.x) 
} 

}