ios 和 javascript 的三次贝塞尔曲线不同
cubic bezier cuve different in ios and javascript
背景:
在我的应用程序中,我必须沿着用户点击屏幕的点绘制一条曲线。我发现下面的博客解释了如何根据贝塞尔曲线的路径点计算控制点。
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]];
}
当你添加一条从(100,100)到(300,100)的曲线时,控制点应该是(150,50)和(250,50),在你的代码中,控制点是(350,150)和(250) ,50), 指定正确的控制点应该使它工作。
背景:
在我的应用程序中,我必须沿着用户点击屏幕的点绘制一条曲线。我发现下面的博客解释了如何根据贝塞尔曲线的路径点计算控制点。
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]];
}
当你添加一条从(100,100)到(300,100)的曲线时,控制点应该是(150,50)和(250,50),在你的代码中,控制点是(350,150)和(250) ,50), 指定正确的控制点应该使它工作。