2012-02-13 15 views
5

Voglio essere in grado di riposizionare e rimodellare una curva di Bezier cubica disegnata in Core Graphics toccandolo e trascinandolo. Posso disegnare la forma di base e posso usare il tocco e trascinare per spostare l'INTERO della forma, ma questo non è ciò che voglio essere in grado di fare.Objective C Bezier Curve ReShaping

Quello che voglio è essere in grado di spostare e rimodellare la curva di Bezier come se fosse un pezzo di corda steso su un tavolo che viene tirato attorno al mio dito. Cioè toccando una parte della curva di Bezier e tirandola in una direzione per cambiare la forma della curva nel suo complesso.

Qualcuno sa come fare? Qualsiasi aiuto sarebbe molto gradito.

Grazie in anticipo

risposta

5

E 'abbastanza facile tracciare i punti di controllo e consentire all'utente di trascinare in giro. Sfortunatamente, la curva non passa attraverso tutti i punti di controllo, quindi l'esperienza non corrisponde esattamente a ciò che stai suggerendo.

Per fare ciò che stai suggerendo, devi prima rispondere alla domanda "l'utente sta toccando la curva?" Questo è lo stesso della domanda "è un dato punto all'interno di una certa distanza della curva". Questa non è una domanda banale, ma può essere calcolata. Probabilmente l'approccio più semplice è semplicemente calcolare i punti X lungo la curva (dove X è sufficientemente alto per darti una ragionevole precisione) e controllare la distanza per ciascuno. In linea di principio, potresti anche prendere la derivata dell'equazione della distanza e risolverla per i suoi zeri, ma ciò richiede l'iterazione. Nella mia esperienza è possibile calcolare le circa 1000 distanze necessarie abbastanza velocemente (anche su un iPad 1) che potrebbe non valerne la complessità aggiuntiva.

Una volta che l'utente sta effettivamente toccando la curva, è facile capire quale sia il punto di controllo più vicino. La cosa difficile a questo punto è decidere cosa fare al riguardo. Alcune opzioni:

  • Spostare il punto di controllo più vicino nella direzione in cui si muove l'utente. Potrebbe essere necessario eseguire più calcoli finché non trovi una nuova curva che passa attraverso il punto di contatto. Questo è l'approccio più semplice e probabilmente da dove iniziare.
  • È possibile suddividere la curva nel punto toccato e spostare gli endpoint appena creati. (Vedere l'algoritmo De Casteljau's per questo.) Questo tenderà a creare spigoli vivi a meno che non si regolino gli altri punti di controllo per creare pendenze corrispondenti. Ciò consente curve più arbitrarie, ma potrebbe diventare molto difficile sapere cosa volesse veramente l'utente. Avresti anche quasi certamente bisogno di applicare Ramer-Douglas-Peucker per evitare che il tuo numero di curve esploda.

Attualmente sono abbastanza interessato ai problemi con la curva di Bézier in Objective-C. Potresti essere interessato al mio first post on the subject. Il mio lavoro iniziale in quest'area è disponibile su GitHub nel codice di esempio iOS:PTL. Spero di avere un altro post su questa settimana. Il tuo problema particolare è interessante, quindi potrei vedere cosa posso costruire intorno a questo.

5

Hmm, sei sicuro di aver bisogno esattamente della curva di Bezier? O ti serve solo lo una curva liscia su un percorso? Il comportamento di cui hai bisogno è molto semplice da implementare usando altri tipi di curve: Cardinal o sottotipo come Catmull-Rom spline.

Il vantaggio del cardinale splines su Bezier è infatti che le spline passano sempre i punti di controllo, vale a dire l'aggiunta, lo spostamento o la cancellazione di un punto dal percorso non influisce su altri punti e la curva sembra ancora a posto. Inoltre, la matematica è molto più semplice (e più veloce da calcolare).

Le spline cardinali non sono presentate nei CoreGraphics, ma è possibile disegnarne una che approssima la curva con poly-line con un piccolo passo t. Il resto (scoprire se il tocco è sopra la curva, ecc.) È spiegato nella risposta di Rob, posso solo aggiungere che avere un'approssimazione di una linea poligonale quasi risolve questo compito poiché tutto ciò che serve dopo è trovare un segmento con una distanza minima dal punto di contatto.

+0

Puntatore eccellente. Sarei un po 'nervoso nel cercare di disegnare una polilinea in questo modo, comunque. La mia esperienza è che CGPath molto grandi tendono ad essere estremamente costosi da disegnare. Ma è possibile convertire un Catmull-Rom in un Bézier: http://stackoverflow.com/questions/1030596/drawing-hermite-curves-in-opengl. Quello potrebbe essere uno strumento utile. Lascia ancora la complessità di dividere la curva e quindi semplificarla di nuovo in cubico, ma un ottimo punto di partenza. –

+0

Ho diviso il percorso di alcuni segmenti per ottimizzare il processo (aggiornamenti, disegno), inoltre c'è un trucco: il parametro t può essere variabile. Possiamo controllare la lunghezza del segmento della curva e regolare la t, in modo che i punti di chiusura ottengano poche "linee di levigatura" mentre i punti di distanza possono avere una dozzina di linee di approssimazione. – Gobra

+0

Vero. Il mio codice collegato qui sotto include "dammi il' t' per una determinata distanza lungo il percorso ", così puoi assicurarti di non provare mai a disegnare linee troppo corte (sicuramente mai più brevi di 1px, cosa che può facilmente accadere se passi oltre 't' linearmente). I miei gravi problemi di prestazioni si sono verificati nei dintorni di 4000-5000 segmenti di linea, e questa tecnica potrebbe tenerti sotto. –