在三次贝塞尔曲线上找到点的切线
对于三次贝塞尔曲线,通常具有四个点a,b,c和d,
对于给定的值t
如何最优雅地找到 ?
回答:
这是经过完全测试的代码,可以复制和粘贴:
它沿着曲线绘制 点, 绘制切线。
bezierInterpolation
找到要点
bezierTangent
找到切线
有 的bezierInterpolation
下面提供:
bezierInterpolation
完美地工作。
altBezierInterpolation
完全相同,但以扩展,清晰,解释性的方式编写。它使算法更容易理解。
使用这两个例程之一:结果相同。
在这两种情况下,bezierTangent
都可用于查找切线。
drawRect:
还包括如何使用with的完整示例。
// 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];
这是两个用于计算 沿贝塞尔三次方 例程。
为了清楚和可靠,这些例程以最简单,最具解释性的方式编写。
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 );
}
四个预先计算的值C1 C2 C3 C4有时被称为贝塞尔 。(回想一下abcd通常称为四个 。)
当然,t从0到1,例如每0.05。
只需 调用这些例程 调用
希望它能对某人有所帮助!
重要事实:
(1) 是:不幸的是,Apple确实没有提供任何方法来从UIBezierPath中提取点。截至2019年为准。
(2)不要忘记, 沿着 UIBezierPath
进行动画设置就像馅饼一样容易。Google的例子很多。
(3)许多人问: “不能使用CGPathApply从UIBezierPath中提取点吗?” 不,
:它只是为您提供“进行任何路径的说明”的列表(因此,“从此处开始”,“到此处画一条直线”等)。名称令人困惑,但CGPathApply与贝塞尔曲线完全无关。
对于游戏程序员-正如@Engineer指出的那样,您可能想要切线的法线,幸运的是Apple内置了矢量数学:
https://developer.apple.com/documentation/accelerate/simd/working_with_vectors
https://developer.apple.com/documentation/simd/2896658-simd_normalize
以上是 在三次贝塞尔曲线上找到点的切线 的全部内容, 来源链接: utcz.com/qa/431990.html