Para una curva Bézier cúbica con las habituales cuatro puntos A, B, C y D,Encuentra la tangente de un punto en una curva de Bezier cúbico
para un valor dado t,
cómo más elegante encuentra la tangente en ese punto?
Para una curva Bézier cúbica con las habituales cuatro puntos A, B, C y D,Encuentra la tangente de un punto en una curva de Bezier cúbico
para un valor dado t,
cómo más elegante encuentra la tangente en ese punto?
La tangente de una curva es simplemente su derivada. La ecuación paramétrica que utiliza Michal:
P(t) = (1 - t)^3 * P0 + 3t(1-t)^2 * P1 + 3t^2 (1-t) * P2 + t^3 * P3
debe tener un derivado 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
Lo cual, por cierto, parece estar mal en su pregunta anterior. Creo que estás usando la pendiente para una curva de Bezier cuadrática allí, no cúbica.
A partir de ahí, debe ser trivial implementar una función C que realiza este cálculo, como Michal ya ha proporcionado para la curva en sí.
Aquí está plenamente probado código para copiar y pegar:
Se basa approxidistant puntos a lo largo de la curva, y dibuja las tangentes.
bezierInterpolation
encuentran los puntos
bezierTangent
encuentra las tangentes
Hay dos versiones de bezierInterpolation
suministran a continuación:
bezierInterpolation
funciona perfectamente
altBezierInterpolation
es exactamente el mismo, pero está escrito de una manera expandida, muy clara y explicativa . Hace que la aritmética sea mucho más fácil de entender.
Utilice cualquiera de esas dos rutinas: los resultados son idénticos.
En ambos casos, use bezierTangent
para encontrar las tangentes. (Nota: el código base de Michal fabuloso here.)
También se incluye un ejemplo completo de cómo usar con drawRect:
.
// 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];
Estas son las dos rutinas para calcular puntos aproximadamente equidistantes, y las tangentes de los, a lo largo de Bézier cúbica.
Para mayor claridad y confiabilidad, estas rutinas están escritas de la manera más simple y más explicativa posible.
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);
}
Los cuatro valores precalculados, C1 C2 C3 C4, a veces se llaman los coeficientes de la bezier.(Recuerde que a b c d se suele llamar los cuatro puntos de control ).
Por supuesto, t va de 0 a 1, por ejemplo cada 0.05.
simplemente llame a estas rutinas vez para X, y luego una vez separado para Y.
espero que ayude a alguien!
hechos importantes:
(1) Es un hecho absoluto que: por desgracia, no es, sin duda, ningún método, proporcionado por Apple, para extraer puntos de un UIBezierPath.
(2) No olvide que es tan fácil como un pastel animar algo a lo largo de a UIBezierPath. Google many examples.
(3) Muchos preguntan, "¿No se puede usar CGPathApply para extraer los puntos de un UIBezierPath?" No, CGPathApply tiene nada que ver: simplemente le da una lista de sus instrucciones en la toma de cualquier camino (así, "comenzar aquí", "trazar una línea recta a este punto", etc, etc)
I también lo encontró propenso a errores al usar las ecuaciones provistas. Demasiado fácil pasar por alto un soporte sutil o mal colocado.
Por el contrario, la Wikipedia proporciona una mucho más claro, más limpio, en mi humilde opinión derivada:
... que implementa fácilmente en código como:
3f * oneMinusT * oneMinusT * (p1 - p0)
+ 6f * t * oneMinusT * (p2 - p1)
+ 3f * t * t * (p3 - p2)
(suponiendo que tiene por vectores menos configurado en el idioma de su elección; la pregunta no está marcada como ObjC específicamente, y iOS ahora tiene varias langs disponibles)
Entrada muy elegante Ad am, gracias (Puramente como curiosidad, en el actual Swift-iOS, tienen un f * -up donde las líneas de código que son demasiado largas ... no funcionan. ¡Entonces, uno simplemente dividiría eso en unas pocas partes!) – Fattie