¿Cómo calcular los puntos de control para un path suave dado un set de puntos?

Estoy usando UIBezierPath, pero esta pregunta se refiere a los puntos de control para las routes, no el dibujo. Dado un set de puntos, puedo representar una ruta. Sin embargo, no he podido averiguar cómo calcular los puntos de control para tener una línea fluida como en un editor de curvas de fotos ( Cómo implementar un editor de Photoshop Curves en UIKit ).

La respuesta más cercana que he visto está aquí: ¿cómo puedo rastrear el movimiento del dedo al tacto para dibujar curvas suaves?

Sin embargo, todavía no puedo captar el cálculo adecuado. Para resumirlo en el código:

for (int i = 0; i< points; i++) { ... [path addQuadCurveToPoint:nextPoint controlPoint:WTF]; } 

La image a la que se ha vinculado es un ejemplo que no utiliza curvas cuadráticas, por lo que voy a ejecutar con la image y no con el código.

Una ruta bezier en ios (y os x) debajo es básicamente una list de commands de dibujo y puntos. p.ej:

 [path moveTo:CGMakePoint(1,1)]; [path curveToPoint:(10,10) controPoint1:(3,7) controlPoint2:(4,1)]; [path curveToPoint:(10,10) controPoint1:(15,17) controlPoint2:(21,11)]; [path closePath]; 

Resultados en:

 moveto (1,1) curveto (10,10) (3,7) (4,1) curveto (20,0) (15,17) (21,11) closepath 

Los puntos de control en una ruta Bézier controlan la dirección y la velocidad de la curva fuera de un punto. El primer punto de control (cp) controla la dirección y la velocidad de la curva que sale del punto anterior y el segundo cp controla lo mismo para el punto al que se está curvando. Para una curva cuadrática (lo que obtienes usando addQuadCurveToPoint: controlPoint:), estos dos puntos son los mismos, como puedes ver en los documentos del método aquí .

Obtener una curva suave a lo largo de un set de puntos implica hacer que cp1 y cp2 sean colineales entre sí y que la línea sea paralela a los puntos en cada extremo de ese segmento.

Curva anotada

Esto se vería como:

 [path moveTo:2]; [path curveTo:3 controlPoint1:cp1 controlPoint2:cp2]; 

cp1 y cp2 pueden calcularse eligiendo una longitud de línea constante y haciendo alguna geometry (olvido todas mis ecuaciones de línea ahora mismo, pero son fácilmente google )

Va a usar # -> # para designar un segmento y # -> # (cp #) para designar un punto de control para la llamada curveto de ese segmento.

El próximo problema es hacer que la curva sea suave desde el segmento 2> 3 y entrar en el segmento 3> 4. En este punto de su código, debe tener un punto de control calculado para 2-> 3 (cp2). Teniendo en count la longitud constante de la línea desde antes (esto controlará cuán afilada es la curva), puedes calcular un cp1 para 3-> 4 obteniendo un punto colineal con 2-> 3 (cp2) y el punto 3 en el diagtwig. Luego, calcule un 3-> 4 (cp2) que sea colineal con 3-> 4 (cp1) y paralelo a la línea que señala el punto 3 y el punto 4. Enjuague y repita a través de su matriz de puntos.

No estoy seguro de cuánto ayudará esto, pero tuve que hacer algo similar para implementar una ruta curva para las notas a seguir en esta aplicación, (www.app.net/hereboy). Esencialmente, es un path con tres curvas.

Para hacer esto, creé 4 puntos por curva, un punto inicial, un punto final y dos puntos de control en la marca del 25% y la marca del 75%.

Aquí está el código que escribí para hacer esto:

 //create points along the keypath for curve. CGMutablePathRef curvedPath = CGPathCreateMutable(); const int TOTAL_POINTS = 3; int horizontalWiggle = 15; int stepChangeX = (endPoint.x - viewOrigin.x) / TOTAL_POINTS; int stepChangeY = (endPoint.y - viewOrigin.y) / TOTAL_POINTS; for(int i = 0; i < TOTAL_POINTS; i++) { int startX = (int)(viewOrigin.x + i * stepChangeX); int startY = (int)(viewOrigin.y + i * stepChangeY); int endX = (int)(viewOrigin.x + (i+1) * stepChangeX); int endY = (int)(viewOrigin.y + (i+1) * stepChangeY); int cpX1 = (int)(viewOrigin.x + (i+0.25) * stepChangeX); if((i+1)%2) { cpX1 -= horizontalWiggle; } else { cpX1 += horizontalWiggle; } int cpY1 = (int)(viewOrigin.y + (i+0.25) * stepChangeY); int cpX2 = (int)(viewOrigin.x + (i+0.75) * stepChangeX); if((i+1)%2) { cpX2 -= horizontalWiggle; } else { cpX2 += horizontalWiggle; } int cpY2 = (int)(viewOrigin.y + (i+0.75) * stepChangeY); CGPathMoveToPoint(curvedPath, NULL, startX, startY); CGPathAddCurveToPoint(curvedPath, NULL, cpX1, cpY1, cpX2, cpY2, endX, endY); } 

¡Buena suerte!