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), 指定正确的控制点应该使它工作。