2010-10-30 17 views
15

Immaginate di avere una curva di Bezier quattro punti del tutto normale (due punti e due punti di controllo) creati utilizzando curveToPoint: controlPoint1: controlPoint2: nell'applicazione cacao:Trova un punto, una distanza determinata, lungo una semplice curva bezier cubica. (! Su un iPhone)

simple cubic bezier curve example
Come fare trovi punti (e le tangenti), lungo la curva?


tardi: per un completa, semplificata, soluzione in base alla risposta di Michal qui sotto, clicca per:
Find the tangent of a point on a cubic bezier curve (on an iPhone)

E basta copiare e incollare il codice da: https://stackoverflow.com/a/31317254/294884

risposta

28

C'è un po ' semplice matematica dietro il calcolo delle posizioni, puoi leggere su di esso in ogni articolo che discute le curve di Bézier, anche su wikipedia. Ad ogni modo, posso relazionarmi con tutti quelli che hanno problemi a implementarlo realmente in codice, quindi ho scritto questo esempio di UIView perché è probabilmente il modo più semplice per iniziare.

#import "MBBezierView.h" 

CGFloat bezierInterpolation(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d) { 
    CGFloat t2 = t * t; 
    CGFloat t3 = t2 * t; 
    return a + (-a * 3 + t * (3 * a - a * t)) * t 
    + (3 * b + t * (-6 * b + b * 3 * t)) * t 
    + (c * 3 - c * 3 * t) * t2 
    + d * t3; 
} 

@implementation MBBezierView 

- (void)drawRect:(CGRect)rect { 
    CGPoint p1, p2, p3, p4; 
    p1 = CGPointMake(30, rect.size.height * 0.33); 
    p2 = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect)); 
    p3 = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect)); 
    p4 = CGPointMake(-30 + CGRectGetMaxX(rect), rect.size.height * 0.66); 

    [[UIColor blackColor] set]; 
    [[UIBezierPath bezierPathWithRect:rect] fill]; 

    [[UIColor redColor] setStroke]; 

    UIBezierPath *bezierPath = [[[UIBezierPath alloc] init] autorelease]; 
    [bezierPath moveToPoint:p1]; 
    [bezierPath addCurveToPoint:p4 controlPoint1:p2 controlPoint2:p3]; 
    [bezierPath stroke]; 

    [[UIColor brownColor] setStroke]; 
    for (CGFloat t = 0.0; t <= 1.00001; t += 0.05) { 
     CGPoint point = CGPointMake(bezierInterpolation(t, p1.x, p2.x, p3.x, p4.x), bezierInterpolation(t, p1.y, p2.y, p3.y, p4.y)); 
     UIBezierPath *pointPath = [UIBezierPath bezierPathWithArcCenter:point radius:5 startAngle:0 endAngle:2*M_PI clockwise:YES]; 
     [pointPath stroke]; 
    } 
} 

@end 

Questo è quello ottengo:

alt text

+1

la matematica è divertente! +1! – slf

+3

@Joe Blow: è davvero quello che volevi? Hai detto che volevi un punto che fosse una distanza D lungo la curva. Michal, correggimi se sbaglio, ma il tuo codice valuta solo la curva a valori di parametri diversi, che non è uguale alla lunghezza. Come puoi vedere, i punti sono più vicini tra loro nei picchi e più distanti nel mezzo della curva. – CromTheDestroyer

+0

Ciao Crom - hai ragione, sono solo APPROSSIMATAMENTE equidistanti lungo la curva.Apparentemente non esiste un modo matematico per ottenere spaziature ESATTE. Questa comune approssimazione funziona bene per i motori di gioco e simili. Vedi anche 4089443. – Fattie

0

Qualunque curva di Bezier può essere visto semplicemente come una funzione polinomiale con vettore o coefficienti complessi. Una curva cubica di Beziér come quella dello screenshot, verrà quindi generata da una funzione polinomiale di ordine 3 e ogni punto della curva descrive il risultato B (t) del polinomio della curva, valutato per un particolare valore di input t. Se non mi sbaglio, una volta che conosci il polinomio usato per creare la curva, puoi semplicemente risolvere B (t) = a + bi, dove a + bi descrive il punto sul piano complesso che vuoi trovare il valore t per. Trovare radici in polinomi come quello è un problema ben compreso e può essere risolto algebricamente per curve di ordine 2 o inferiori, e usando un metodo come forward-newton per polinomi di grado più elevato. Se conosci il polinomio generativo, dovrebbe anche essere molto semplice trovare le derivate. Beziér di solito viene estratto da "template polynomials" in cui solo i coefficienti vengono modificati quando viene disegnata una curva diversa, quindi è probabile che lo si possa cercare da qualche parte nella documentazione.

3

L'approssimazione che t è la distanza lungo la curva proposta da Michal può essere problematica con alcune curve e per alcuni scopi. Purtroppo ho cercato senza fortuna per un po 'di tempo per un'implementazione Obj-C della soluzione corretta.

La soluzione è, tuttavia, descritta in modo piuttosto fantastico da Mike "Pomax" Kamermans nel suo straordinario Primer on Bezier Curves. Ha anche tutto il codice, scritto in elaborazione e di pubblico dominio. Sono stupito che nessuno lo abbia mai convertito in Obj-C. Molto tentato, che io sia.

+0

Link incredibilmente utile @Daniel - grazie. Magnifico. Questa è una di quelle pagine ... http://pomax.github.io/bezierinfo ... che è "troppo utile!" Eh. Ancora grazie, il link è così utile che ti lascio! Eccezionale. – Fattie

+1

hahaha, felice che il Primer abbia trovato un uso. Fammi sapere se trovi qualcosa che manca o non chiaro = P –

Problemi correlati