背景:
在我的应用程序中,我必须沿着用户点击屏幕的点绘制曲线。我在下面的博客中找到了解释如何根据贝塞尔曲线的路径点计算控制点。
Spline Interpolation - Scaled Innovation
示例是在javascript中,我将其改编为iOS。以下是我在我的应用中使用的代码
void (^calculateAndAddControlPoints)(CGPoint firstPoint, CGPoint secondPoint, CGPoint thirdPoint) = ^void (CGPoint firstPoint, CGPoint secondPoint, CGPoint thirdPoint)
{
CGPoint controlPoint1;
CGPoint controlPoint2;
CGFloat distance12 = sqrt(pow(firstPoint.x-secondPoint.x, 2) + pow(firstPoint.y-secondPoint.y, 2));
CGFloat distance23 = sqrt(pow(secondPoint.x-thirdPoint.x, 2) + pow(secondPoint.y-thirdPoint.y, 2));
CGFloat scallingFactor12 = (tension * distance12) / (distance12 + distance23);
CGFloat scallingFactor23 = tension - scallingFactor12;
controlPoint1.x = secondPoint.x - (scallingFactor12 * (thirdPoint.x - firstPoint.x));
controlPoint1.y = secondPoint.y - (scallingFactor12 * (thirdPoint.y - firstPoint.y));
controlPoint2.x = secondPoint.x + (scallingFactor23 * (thirdPoint.x - firstPoint.x));
controlPoint2.y = secondPoint.y + (scallingFactor23 * (thirdPoint.y - firstPoint.y));
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor yellowColor].CGColor);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:controlPoint1];
[path addLineToPoint:controlPoint2];
[path stroke];
[controlPoints addObject:[NSValue valueWithCGPoint:controlPoint1]];
[controlPoints addObject:[NSValue valueWithCGPoint:controlPoint2]];
};
NSUInteger curvePointsCount = [curvePoints count];
if (_closed) {
NSValue *lastPoint = [curvePoints lastObject];
[curvePoints addObject:curvePoints[0]];
[curvePoints addObject:curvePoints[1]];
[curvePoints insertObject:lastPoint atIndex:0];
for (NSInteger i=0; i < curvePointsCount; ++i) {
calculateAndAddControlPoints([curvePoints[i + 0] CGPointValue],
[curvePoints[i + 1] CGPointValue],
[curvePoints[i + 2] CGPointValue]);
}
[controlPoints addObject:controlPoints[0]];
[controlPoints addObject:controlPoints[1]];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:[curvePoints[1] CGPointValue]];
for (NSInteger i=1; i < curvePointsCount + 1; ++i) {
[path addCurveToPoint:[curvePoints[i + 1] CGPointValue]
controlPoint1:[controlPoints[2 * i + 0] CGPointValue]
controlPoint2:[controlPoints[2 * i + 1] CGPointValue]];
}
[path stroke];
} else {
for (NSInteger i=0; i < curvePointsCount - 2; ++i) {
calculateAndAddControlPoints([curvePoints[i + 0] CGPointValue],
[curvePoints[i + 1] CGPointValue],
[curvePoints[i + 2] CGPointValue]);
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:[curvePoints[0] CGPointValue]];
[path addQuadCurveToPoint:[curvePoints[1] CGPointValue]
controlPoint:[controlPoints[0] CGPointValue]];
for (NSInteger i=1; i < curvePointsCount - 2; ++i) {
[path addCurveToPoint:[curvePoints[i + 1] CGPointValue]
controlPoint1:[controlPoints[2 * i + 0] CGPointValue]
controlPoint2:[controlPoints[2 * i + 1] CGPointValue]];
}
[path moveToPoint:[curvePoints[curvePointsCount - 2] CGPointValue]];
[path addQuadCurveToPoint:[curvePoints[curvePointsCount - 1] CGPointValue]
controlPoint:[controlPoints[[controlPoints count] - 1] CGPointValue]];
[path stroke];
}
问题:
iOS中的输出与javascript中的输出不同,如下所示,曲线在路径点附近不平滑。白线是实际曲线,黄线是连接控制点的线,红点是曲线点。
这是javascript中相同曲线的外观。
使用的曲线点:(100,100),(300,100),(300,300),(100,300)
生成的控制点:(50,150),(150,50),(250,50),(350,150),(350,250),(250,350),(150,350), (50,250)
为什么我没有在iOS中获得一个圈子,如何解决这个问题?
解决方案:
事实证明我正在正确地计算控制点,但错误地将它们映射到曲线点。感谢@gabbler我现在修复它,下面是绘制曲线的正确循环
for (NSInteger i=1; i < curvePointsCount + 1; ++i) {
[_bezierPath moveToPoint:[curvePoints[i] CGPointValue]];
[_bezierPath addCurveToPoint:[curvePoints[i + 1] CGPointValue]
controlPoint1:[controlPoints[2 * i - 1] CGPointValue]
controlPoint2:[controlPoints[2 * i + 0] CGPointValue]];
}
答案 0 :(得分:1)
当你从(100,100)到(300,100)添加曲线时,控制点应该是(150,50)和(250,50),在你的代码中,控制点是(350,150)和(250,50) ),指定正确的控制点应该使它工作。