2008-08-13 9 views
9

Actualmente, intento hacer que múltiples beziers tengan puntos equidistantes. Actualmente estoy usando la interpolación cúbica para encontrar los puntos, pero debido a la forma en que trabajan los beziers, algunas áreas son más densas que otras y resultan ser burdas para el mapeo de texturas debido a la distancia variable. ¿Hay alguna forma de encontrar puntos en un bezier por distancia en lugar de por porcentaje? Además, ¿es posible extender esto a múltiples curvas conectadas?Puntos equidistantes en curvas Bezier

+0

Ver también http://math.stackexchange.com/a/61796/589. – lhf

Respuesta

4

distancia entre P_0 y P_3 (en forma cúbica), sí, pero creo que ya lo sabía, es sencillo.

Distancia en una curva es simplemente la longitud de arco:

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

donde:

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)

Probablemente, tendría t_0 = 0, = t_1 1.0, y dz (t) = 0 (plano 2d).

+5

Así es como se encuentra la longitud del arco dado el parámetro, pero encontrar puntos equidistantes requiere el inverso de esta función. Pasar de uno a otro no es trivial. @ Christian Romo: ¿cómo lo hiciste? Quiero decir, puedes usar la búsqueda binaria, pero eso sería terriblemente lento (por lo que estoy tratando de hacer, de todos modos). – CromTheDestroyer

9

Esto se denomina parametrización de "longitud del arco". Escribí un artículo sobre esto hace varios años:

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

La idea es comprobar la validez de calcular una curva de "parametrización", y evaluar la curva través de eso.

+0

No he leído el documento completamente todavía. Pero me gustaría preguntar si hay una mejor manera de definir curvas que no necesitarían ser "convertidas" primero. P.ej. ¿Sabes si usaría NURBS para definir todas las rutas/curvas, soportaría una parametrización de longitud de arco equidistante más rápida? O de alguna otra manera tal vez? Editar: Más rápido me refiero al uso de CPU o GPU. – Ciantic

+0

El uso de NURB no ayudará, el problema fundamental es el mismo. El final del documento muestra un método para componer la curva de re parametrización con el original. Esto produce una nueva curva con la parametrización de la longitud del arco, pero el orden si la curva es más alta, por lo que es más lento de evaluar. –

2

Sé que esta es una pregunta anterior, pero recientemente me encontré con este problema y creé una extensión UIBezierPath para resolver una coordenada X dada una coordenada Y y viceversa. Escrito a la velocidad

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) 
} 

} 
Cuestiones relacionadas