2012-02-13 15 views
5

Quiero reposicionar y remodelar una curva de bezier cúbica dibujada en Core Graphics tocándola y arrastrándola. Puedo dibujar la forma básica y puedo usar tocar y arrastrar para mover TODO de la forma, pero esto no es lo que quiero poder hacer.Objetivo C Bezier Curva Reesformar

Lo que quiero es poder moverlo y darle nueva forma a la curva de bezier como si fuera un trozo de hilo tirado en una mesa con el dedo. Es decir, tocar parte de la curva de bezier y tirar de ella en una dirección para cambiar la forma general de la curva.

¿Alguien sabe cómo hacer esto? Cualquier ayuda sería bienvenida.

Gracias de antemano

Respuesta

5

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.

5

¿Estás seguro de que necesitas exactamente la curva de Bezier? ¿O solo necesita alguna curva suave sobre una ruta? El comportamiento que necesita es muy fácil de implementar usando otros tipos de curvas: Cardinal o su subtipo es Catmull-Rom spline.

El beneficio de las esquinas cardinales sobre Bezier es que las splines siempre pasan los puntos de control, es decir, agregar, mover o borrar un punto de la ruta no afecta a otros puntos y la curva aún se ve bien. Además, las matemáticas son mucho más simples (y más rápidas de calcular).

splines cardinales no se presentan en los CoreGraphics, pero se puede sacar una aproximación de la curva con poli-línea con alguna pequeña t paso. El resto (encontrar si el toque está sobre la curva, etc.) se explica en la respuesta de Rob, solo puedo agregar que tener una aproximación de línea poligonal de una curva casi soluciona esta tarea ya que todo lo que necesitas después es encontrar un segmento con una distancia más corta al punto de contacto.

+0

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. –

+0

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

+0

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. –

Cuestiones relacionadas