Es bastante fácil dibujar los puntos de control y permitir al usuario arrastrar a su alrededor. Lamentablemente, la curva no pasa por todos los puntos de control, por lo que la experiencia no coincide exactamente con lo que sugiere.
Para hacer lo que está sugiriendo, primero debe responder la pregunta "¿está el usuario tocando la curva?" Esto es lo mismo que la pregunta "es un punto dado dentro de una cierta distancia de la curva". Esa no es una pregunta trivial, pero se puede calcular. Probablemente el enfoque más simple es simplemente calcular X puntos a lo largo de la curva (donde X es lo suficientemente alto para darle una precisión razonable) y verificar la distancia para cada uno. En principio, también puedes tomar la derivada de la ecuación de distancia y resolverla por sus ceros, pero esto requiere iteración. Según mi experiencia, puede calcular las 1000 distancias necesarias más o menos lo suficientemente rápido (incluso en un iPad 1) que puede no valer la pena la complejidad adicional.
Una vez que encuentre que el usuario está de hecho tocando la curva, es fácil determinar qué punto de control está más cerca. Lo difícil en este punto es decidir qué hacer al respecto. Algunas opciones:
- Mueva el punto de control más cercano en la dirección en que se mueve el usuario. Es posible que deba hacer varios cálculos hasta que encuentre una nueva curva que pase por el punto de contacto. Este es el enfoque más simple y probablemente donde comenzaría.
- Puede subdividir la curva en el punto tocado y mover los puntos finales recién creados. (Consulte el algoritmo De Casteljau's para esto.) Esto tenderá a crear esquinas agudas a menos que ajuste los otros puntos de control para crear pendientes coincidentes. Esto permite más curvas arbitrarias, pero podría ser muy difícil saber lo que el usuario realmente quería. También es probable que necesite aplicar Ramer-Douglas-Peucker para evitar que su número de curvas explote.
Actualmente estoy bastante interesado en los problemas de curvas de Bézier en Objective-C. Puede que le interese mi first post on the subject. Mi trabajo inicial en esta área está disponible en GitHub en el código de muestra iOS:PTL. Espero tener otra publicación esta semana. Su problema particular es interesante, por lo que puedo ver lo que puedo desarrollar al respecto.
Excelente puntero. Sin embargo, estaría un poco nervioso por tratar de dibujar una polilínea de esta manera. Mi experiencia es que los CGPaths muy grandes tienden a ser extremadamente caros de dibujar. Pero es posible convertir un Catmull-Rom en un Bézier: http://stackoverflow.com/questions/1030596/drawing-hermite-curves-in-opengl. Esa podría ser una herramienta útil. Aún deja la complejidad de dividir la curva y simplificarla de nuevo a cúbica, pero es un excelente punto de partida. –
Me gustaría dividir el camino a algunos segmentos para optimizar el proceso (actualizaciones, dibujo), además hay un truco: el paso del parámetro t podría ser variable. Podemos verificar la longitud del segmento de curva y ajustar la t, de modo que los puntos de cierre obtendrán pocas "líneas de suavizado", mientras que los puntos de distancia pueden tener una docena de líneas de aproximación. – Gobra
Es cierto. Mi código vinculado a continuación incluye un "dame la' t' para una distancia dada a lo largo del camino ", así que puedes asegurarte de que nunca trates de dibujar líneas demasiado cortas (definitivamente nunca más cortas que 1px, lo que puede suceder fácilmente si pasas por alto 't' linealmente). Mis serios problemas de rendimiento ocurrieron en el vecindario de segmentos de línea de 4000-5000, y esta técnica podría mantenerte por debajo de eso. –