2010-11-03 5 views

Répondre

29

La tangente d'une courbe est simplement sa dérivée. L'équation paramétrique que Michal utilise:

P(t) = (1 - t)^3 * P0 + 3t(1-t)^2 * P1 + 3t^2 (1-t) * P2 + t^3 * P3 

devrait avoir un dérivé de

dP(t)/dt = -3(1-t)^2 * P0 + 3(1-t)^2 * P1 - 6t(1-t) * P1 - 3t^2 * P2 + 6t(1-t) * P2 + 3t^2 * P3 

qui, par ailleurs, semble être mal dans votre question précédente. Je crois que vous utilisez la pente pour une courbe de Bézier quadratique, pas cubique. De là, il devrait être trivial d'implémenter une fonction C qui effectue ce calcul, comme Michal l'a déjà prévu pour la courbe elle-même.

7

est ici un code entièrement testé pour copier et coller:

Il tire approxidistant points le long de la courbe, et il tire les tangentes.

bezierInterpolation trouve des points

bezierTangent trouve les tangentes

Il y a DEUX VERSIONS de bezierInterpolation fournis ci-dessous:

bezierInterpolation fonctionne parfaitement

altBezierInterpolation est exactement le même, mais il est écrit d'une manière élargie, très claire, explicative . Cela rend l'arithmétique beaucoup plus facile à comprendre. Utilisez l'une de ces deux routines: les résultats sont identiques.

Dans les deux cas, utilisez bezierTangent pour rechercher les tangentes. (Note: fabuleuse base de code de Michal here.)

Un exemple complet de la façon d'utiliser avec drawRect: est également inclus.

// MBBezierView.m original BY MICHAL stackoverflow #4058979 

#import "MBBezierView.h" 



CGFloat bezierInterpolation(
    CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d) { 
// see also below for another way to do this, that follows the 'coefficients' 
// idea, and is a little clearer 
    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; 
} 

CGFloat altBezierInterpolation(
    CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d) 
    { 
// here's an alternative to Michal's bezierInterpolation above. 
// the result is absolutely identical. 
// of course, you could calculate the four 'coefficients' only once for 
// both this and the slope calculation, if desired. 
    CGFloat C1 = (d - (3.0 * c) + (3.0 * b) - a); 
    CGFloat C2 = ((3.0 * c) - (6.0 * b) + (3.0 * a)); 
    CGFloat C3 = ((3.0 * b) - (3.0 * a)); 
    CGFloat C4 = (a); 

    // it's now easy to calculate the point, using those coefficients: 
    return (C1*t*t*t + C2*t*t + C3*t + C4 ); 
    } 







CGFloat bezierTangent(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d) 
{ 
    // note that abcd are aka x0 x1 x2 x3 

/* the four coefficients .. 
    A = x3 - 3 * x2 + 3 * x1 - x0 
    B = 3 * x2 - 6 * x1 + 3 * x0 
    C = 3 * x1 - 3 * x0 
    D = x0 

    and then... 
    Vx = 3At2 + 2Bt + C   */ 

    // first calcuate what are usually know as the coeffients, 
    // they are trivial based on the four control points: 

    CGFloat C1 = (d - (3.0 * c) + (3.0 * b) - a); 
    CGFloat C2 = ((3.0 * c) - (6.0 * b) + (3.0 * a)); 
    CGFloat C3 = ((3.0 * b) - (3.0 * a)); 
    CGFloat C4 = (a); // (not needed for this calculation) 

    // finally it is easy to calculate the slope element, 
    // using those coefficients: 

    return ((3.0 * C1 * t* t) + (2.0 * C2 * t) + C3); 

    // note that this routine works for both the x and y side; 
    // simply run this routine twice, once for x once for y 
    // note that there are sometimes said to be 8 (not 4) coefficients, 
    // these are simply the four for x and four for y, 
    // calculated as above in each case. 
} 







@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]; 

// now mark in points along the bezier! 

    for (CGFloat t = 0.0; t <= 1.00001; t += 0.05) { 
    [[UIColor brownColor] setStroke]; 

     CGPoint point = CGPointMake(
      bezierInterpolation(t, p1.x, p2.x, p3.x, p4.x), 
      bezierInterpolation(t, p1.y, p2.y, p3.y, p4.y)); 

      // there, use either bezierInterpolation or altBezierInterpolation, 
      // identical results for the position 

     // just draw that point to indicate it... 
     UIBezierPath *pointPath = 
      [UIBezierPath bezierPathWithArcCenter:point 
      radius:5 startAngle:0 endAngle:2*M_PI clockwise:YES]; 
     [pointPath stroke]; 

     // now find the tangent if someone on stackoverflow knows how 
     CGPoint vel = CGPointMake(
      bezierTangent(t, p1.x, p2.x, p3.x, p4.x), 
      bezierTangent(t, p1.y, p2.y, p3.y, p4.y)); 

     // the following code simply draws an indication of the tangent 
     CGPoint demo = CGPointMake(point.x + (vel.x*0.3), 
             point.y + (vel.y*0.33)); 
     // (the only reason for the .3 is to make the pointers shorter) 
     [[UIColor whiteColor] setStroke]; 
     UIBezierPath *vp = [UIBezierPath bezierPath]; 
     [vp moveToPoint:point]; 
     [vp addLineToPoint:demo]; 
     [vp stroke]; 
    } 
} 

@end 

to draw that class... 
MBBezierView *mm = [[MBBezierView alloc] 
        initWithFrame:CGRectMake(400,20, 600,700)]; 
[mm setNeedsDisplay]; 
[self addSubview:mm]; 

Voici les deux routines pour calculer points à peu près à égale distance, et les tangentes de ceux, le long d'un cube Bézier. Par souci de clarté et de fiabilité, ces routines sont écrites de la manière la plus simple et la plus explicative possible.

CGFloat bezierPoint(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d) 
    { 
    CGFloat C1 = (d - (3.0 * c) + (3.0 * b) - a); 
    CGFloat C2 = ((3.0 * c) - (6.0 * b) + (3.0 * a)); 
    CGFloat C3 = ((3.0 * b) - (3.0 * a)); 
    CGFloat C4 = (a); 

    return (C1*t*t*t + C2*t*t + C3*t + C4 ); 
    } 

CGFloat bezierTangent(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d) 
    { 
    CGFloat C1 = (d - (3.0 * c) + (3.0 * b) - a); 
    CGFloat C2 = ((3.0 * c) - (6.0 * b) + (3.0 * a)); 
    CGFloat C3 = ((3.0 * b) - (3.0 * a)); 
    CGFloat C4 = (a); 

    return ((3.0 * C1 * t* t) + (2.0 * C2 * t) + C3); 
    } 

Les quatre valeurs précalculées, C1 C2 C3 C4, sont parfois appelés les coefficients du Bézier.(Rappelons que a b c d sont généralement appelés les quatre points de contrôle .)

Bien sûr, t va de 0 à 1, par exemple tous les 0,05.

Appelez simplement ces routines une fois pour X, puis une fois séparément pour Y.

aide quelqu'un espère que cela!


Faits importants:

(1) Il est un fait absolu que: malheureusement, il est, sans aucun doute, aucune méthode, fourni par Apple, pour extraire les points d'un UIBezierPath.

(2) N'oubliez pas que c'est aussi simple que de faire un tour pour animer quelque chose le long de un UIBezierPath. Google many examples.

(3) Beaucoup demandent, "Impossible d'utiliser CGPathApply pour extraire les points d'un UIBezierPath?" Non, CGPathApply est totalement sans rapport avec: il vous donne simplement une liste de vos instructions, en faisant un chemin (donc, « commencer ici », « tracer une ligne droite à ce point », etc., etc.)

1

I trouvé trop enclin à l'erreur d'utiliser les équations fournies. Trop facile de rater un support subtil ou mal placé.

En revanche, Wikipédia fournit beaucoup plus claire, plus propre, à mon humble avis dérivé:

enter image description here

... qui met en œuvre facilement dans le code comme:

3f * oneMinusT * oneMinusT * (p1 - p0) 
+ 6f * t * oneMinusT * (p2 - p1) 
+ 3f * t * t * (p3 - p2) 

(en supposant que vous avez Vector- moins configuré dans la langue de votre choix, la question n'est pas marquée comme ObjC spécifiquement, et iOS a maintenant plusieurs langs disponibles)

+0

suis, merci. (Purement comme une curiosité, dans l'actuel Swift-iOS, ils ont un f * -up où les lignes de code qui sont trop longues ... ne fonctionnent pas, donc, on ne ferait que casser cela en quelques parties!) – Fattie

Questions connexes