cod*_*rut 4 math cocoa geometry bezier uibezierpath
处所
我有一个动画成由8个贝塞尔曲线组成的形状的圆。为了使过渡平滑,我还需要用8立方贝塞尔曲线制成圆。这是我到目前为止的内容:
代码
- (UIBezierPath*)pathBubbleLeft {
UIBezierPath *path = [UIBezierPath new];
[path moveToPoint:p(sqlx, sqlMidy)];
CGFloat r = sqlW/2;
CGFloat sin45 = 0.7071 * r;
CGFloat cos45 = 0.7071 * r;
[path addRelativeCurveToPoint:point(sqlMidx - cos45, sqlMidy - sin45) control1:vector(0, 0.4) control2:vector(0.5, 0.8)];
[path addRelativeCurveToPoint:point(sqlMidx, sqly) control1:vector(0.2, 0.5) control2:vector(0.4, 1)];
[path addRelativeCurveToPoint:point(sqlMidx + cos45, sqlMidy - sin45) control1:vector(0.6, 0) control2:vector(0.8, 0.5)];
[path addRelativeCurveToPoint:point(sqlMaxx, sqlMidy) control1:vector(0.5, 0.2) control2:vector(1, 0.5)];
[path addRelativeCurveToPoint:point(sqlMidx + cos45, sqlMidy + sin45) control1:vector(0, 0.4) control2:vector(0.5, 0.8)];
[path addRelativeCurveToPoint:point(sqlMidx, sqlMaxy) control1:vector(0.2, 0.5) control2:vector(0.4, 1)];
[path addRelativeCurveToPoint:point(sqlMidx - cos45, sqlMidy + sin45) control1:vector(0.6, 0) control2:vector(0.8, 0.5)];
[path addRelativeCurveToPoint:point(sqlx, sqlMidy) control1:vector(0.5, 0.2) control2:vector(1, 0.5)];
return path;
}
Run Code Online (Sandbox Code Playgroud)
路径从左开始并顺时针旋转(从pi到pi / 2、0、3pi / 4,pi)
point和vector是短裤CGPointMake和CGVectorMake
“SQL”中sqlx,sqly,sqlMidx,sqlMidY,sqlMaxx和sqlMaxy代表“squareLeft”,圆的边界矩形。这些都是CGFloats。
addRelativeCurveToPoint用于定义相对于起点/终点的控制点。(0,0)是开始,(1,1)是结束。更容易阅读。
- (void)addRelativeCurveToPoint:(CGPoint)endPoint control1:(CGVector)controlPoint1 control2:(CGVector)controlPoint2 {
CGPoint start = self.currentPoint;
CGPoint end = endPoint;
CGFloat x1 = start.x + controlPoint1.dx*(end.x - start.x);
CGFloat x2 = start.x + controlPoint2.dx*(end.x - start.x);
CGFloat y1 = start.y + controlPoint1.dy*(end.y - start.y);
CGFloat y2 = start.y + controlPoint2.dy*(end.y - start.y);
[self addCurveToPoint:endPoint controlPoint1:CGPointMake(x1, y1) controlPoint2:CGPointMake(x2, y2)];
}
Run Code Online (Sandbox Code Playgroud)
红色圆圈有点波浪。这就是我要解决的问题。
在下面,左圆使用上面的代码,右圆由4条曲线组成,顶部有2个零长度插入,底部有2个零长度插入([path addLineToPoint:path.currentPoint];)。
从左边的一个过渡到花生中段是可以的,但是从中间的向右过渡很奇怪
使用四个线段,使用三次贝塞尔曲线的圆逼近不能比@fang在评论中提供的0.55228 [...]值更圆:这只是数学上唯一三次贝塞尔曲线最佳的值近似一个圆。在无限精度表示中,它实际上是您从中获得的价值:
4 angle 4 sqrt(2) - 1
k = - * tan(-------) = - * tan(pi/8) = 4 * -----------
3 2 3 3
Run Code Online (Sandbox Code Playgroud)
是0.5522847498307933984022516322796 [...]。这将为您提供4个线段的最佳近似值,因此,如果您需要使用8个线段,我们需要一个不同的值,这意味着我们需要使用为pi / 2(四分之一圆)的角度提供k的导数。,看看它对pi / 4(第八个圆)的作用。因此:我们将角度pi/4插入到Bezier Curve的Primer上有关用三次曲线逼近圆的部分中概述的函数中,得到:
start = {
x: 1,
y: 0
}
c1 = {
x: 1,
y: 4/3 * tan(pi/16)
}
c2 = {
x: cos(pi/4) + 4/3 * tan(pi/16) * sin(pi/4)
y: sin(pi/4) - 4/3 * tan(pi/16) * cos(pi/4)
}
e = {
x: cos(pi/4)
y: sin(pi/4)
}
Run Code Online (Sandbox Code Playgroud)
这为我们提供了这些(非常有用的)近似坐标:
s = (1, 0)
c1 = (1, 0.265216...)
c2 = (0.894643..., 0.51957...)
e = (0.7071..., 0.7071...)
Run Code Online (Sandbox Code Playgroud)
那将是第1段,然后其余的段简单地通过对称性得出,第2段为:
s = (0.7071..., 0.7071...)
c1 = (0.51957..., 0.894643...)
c2 = (0.265216..., 1)
e = (0, 1)
Run Code Online (Sandbox Code Playgroud)
此处显示了覆盖四分之一圆的这些坐标的示例:http : //jsbin.com/ridedahixu/edit?html,输出
其余是(+,-),(-,+)和(-,-)象限中的明显对称性。
这些是最好的近似值,因此:如果bezierPathWithOvalInRect(...)做其他事情,它的准确性不如我们几十年前得出的值=)