gpt4 book ai didi

ios - 带圆角的UIBezierPath三角形

转载 作者:行者123 更新时间:2023-11-30 12:01:27 26 4
gpt4 key购买 nike

我已设计此代码来生成Bezier路径,该路径用作CAShapeLayer掩盖UIView的路径(视图的高度和宽度是可变的)

这段代码生成了一个带有尖锐边缘的三角形,但我想使其变成圆角!我花了2个小时尝试与addArcWithCenter...lineCapStylelineJoinStyle等一起工作,但似乎没有任何工作适合我。

UIBezierPath *bezierPath = [UIBezierPath bezierPath];

CGPoint center = CGPointMake(rect.size.width / 2, 0);
CGPoint bottomLeft = CGPointMake(10, rect.size.height - 0);
CGPoint bottomRight = CGPointMake(rect.size.width - 0, rect.size.height - 0);

[bezierPath moveToPoint:center];
[bezierPath addLineToPoint:bottomLeft];
[bezierPath addLineToPoint:bottomRight];
[bezierPath closePath];


所以我的问题是,我将如何对UIBezierPath中的三角形的所有边缘进行四舍五入(我是否需要子层,多个路径等)?

N.B我没有绘制此BezierPath,所以 CGContext...中的所有 drawRect函数都无法帮助我:(

谢谢!

最佳答案

编辑

FWIW:此答案通过解释CGPathAddArcToPoint(...)对您的作用来达到其教育目的。我强烈建议您通读它,因为它可以帮助您理解和欣赏CGPath API。然后,您应该继续使用它,如在an0's answer中看到的那样,当在应用程序中倒圆角时,请使用此代码,而不要使用此代码。仅当您想尝试并了解这样的几何计算时,才应将此代码用作参考。



原始答案

因为我发现这样的问题很有趣,所以我不得不回答:)

这是一个很长的答案。没有简短的版本:D



注意:为简单起见,我的解决方案是对用于形成三角形的点进行一些假设,例如:


三角形的面积足够大以适合圆角(例如,三角形的高度大于拐角处的圆的直径。我不是要检查或尝试防止可能发生的任何奇怪结果)除此以外。
角按逆时针顺序列出。您可以使它适用于任何订单,但为简化起见,它似乎是一个足够公平的约束。


如果需要,可以使用相同的技术对任何多边形进行圆化处理,只要它是严格凸的即可(即不是尖锐的星形)。虽然我不会解释如何做,但是它遵循相同的原理。



一切都以三角形开始,您想要以一定的半径r来圆角化:



圆角三角形应包含在尖角三角形中,因此第一步是找到尽可能靠近拐角的位置,在该位置上您可以拟合半径为r的圆。

一种简单的方法是创建与三角形中3个边平行的3条新线,并将距离r中的每一个都向内移动,正交于原始边的边。

为此,您需要计算每条线的斜率/角度以及要应用于两个新点的偏移量:

CGFloat angle = atan2f(end.y - start.y,
end.x - start.x);

CGVector offset = CGVectorMake(-sinf(angle)*radius,
cosf(angle)*radius);


注意:为清楚起见,我使用的是CGVector类型(在iOS 7中可用),但是您也可以使用点或大小来与以前的OS版本一起使用。

然后将偏移量添加到每行的起点和终点:

CGPoint offsetStart = CGPointMake(start.x + offset.dx,
start.y + offset.dy);

CGPoint offsetEnd = CGPointMake(end.x + offset.dx,
end.y + offset.dy);


当您这样做时,您会看到三条线在三个地方相互交叉:



每个相交点恰好是与两个侧面之间的距离r(假定三角形足够大,如上所述)。

您可以将两条线的交点计算为:

//       (x1⋅y2-y1⋅x2)(x3-x4) - (x1-x2)(x3⋅y4-y3⋅x4)
// px = –––––––––––––––––––––––––––––––––––––––––––
// (x1-x2)(y3-y4) - (y1-y2)(x3-x4)

// (x1⋅y2-y1⋅x2)(y3-y4) - (y1-y2)(x3⋅y4-y3⋅x4)
// py = –––––––––––––––––––––––––––––––––––––––––––
// (x1-x2)(y3-y4) - (y1-y2)(x3-x4)

CGFloat intersectionX = ((x1*y2-y1*x2)*(x3-x4) - (x1-x2)*(x3*y4-y3*x4)) / ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4));
CGFloat intersectionY = ((x1*y2-y1*x2)*(y3-y4) - (y1-y2)*(x3*y4-y3*x4)) / ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4));

CGPoint intersection = CGPointMake(intersectionX, intersectionY);


其中(x1,y1)至(x2,y2)为第一行,(x3,y3)至(x4,y4)为第二行。

如果然后在每个相交点上放置一个半径为r的圆,则可以看到它确实适用于圆角三角形(忽略三角形和圆的不同线宽):

 

现在要创建圆角三角形,您需要创建一条路径,该路径在原始三角形与交点正交的点上从直线到圆弧再到直线(等)。这也是圆与原始三角形相切的点。

知道三角形中所有3个边的斜率,拐角半径和圆心(相交点),每个圆角的起始和终止角就是该边的斜率-90度。为了将这些东西组合在一起,我在代码中创建了一个结构,但是如果您不想这样做,则不必:

typedef struct {
CGPoint centerPoint;
CGFloat startAngle;
CGFloat endAngle;
} CornerPoint;


为了减少代码重复,我为自己创建了一种方法,该方法计算给定一条线从一个点到另一个点(到最后一个点)的交点和角度(该点未闭合,因此不是三角形):



代码如下(实际上只是我上面显示的代码放在一起):

- (CornerPoint)roundedCornerWithLinesFrom:(CGPoint)from
via:(CGPoint)via
to:(CGPoint)to
withRadius:(CGFloat)radius
{
CGFloat fromAngle = atan2f(via.y - from.y,
via.x - from.x);
CGFloat toAngle = atan2f(to.y - via.y,
to.x - via.x);

CGVector fromOffset = CGVectorMake(-sinf(fromAngle)*radius,
cosf(fromAngle)*radius);
CGVector toOffset = CGVectorMake(-sinf(toAngle)*radius,
cosf(toAngle)*radius);


CGFloat x1 = from.x +fromOffset.dx;
CGFloat y1 = from.y +fromOffset.dy;

CGFloat x2 = via.x +fromOffset.dx;
CGFloat y2 = via.y +fromOffset.dy;

CGFloat x3 = via.x +toOffset.dx;
CGFloat y3 = via.y +toOffset.dy;

CGFloat x4 = to.x +toOffset.dx;
CGFloat y4 = to.y +toOffset.dy;

CGFloat intersectionX = ((x1*y2-y1*x2)*(x3-x4) - (x1-x2)*(x3*y4-y3*x4)) / ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4));
CGFloat intersectionY = ((x1*y2-y1*x2)*(y3-y4) - (y1-y2)*(x3*y4-y3*x4)) / ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4));

CGPoint intersection = CGPointMake(intersectionX, intersectionY);

CornerPoint corner;
corner.centerPoint = intersection;
corner.startAngle = fromAngle - M_PI_2;
corner.endAngle = toAngle - M_PI_2;

return corner;
}


然后,我使用该代码3次来计算3个角:

CornerPoint leftCorner  = [self roundedCornerWithLinesFrom:right
via:left
to:top
withRadius:radius];

CornerPoint topCorner = [self roundedCornerWithLinesFrom:left
via:top
to:right
withRadius:radius];

CornerPoint rightCorner = [self roundedCornerWithLinesFrom:top
via:right
to:left
withRadius:radius];


现在,拥有所有必要的数据,开始创建实际路径的部分。我将依靠CGPathAddArc将在当前点到起点之间添加一条直线的事实,而不必自己绘制这些线(这已记录为行为)。

我需要手动计算的唯一点是路径的起点。我选择右下角的起点(没有具体原因)。在此处,您只需添加一个弧,以从起点和终点角度的交点为中心:

CGMutablePathRef roundedTrianglePath = CGPathCreateMutable();
// manually calculated start point
CGPathMoveToPoint(roundedTrianglePath, NULL,
leftCorner.centerPoint.x + radius*cosf(leftCorner.startAngle),
leftCorner.centerPoint.y + radius*sinf(leftCorner.startAngle));
// add 3 arcs in the 3 corners
CGPathAddArc(roundedTrianglePath, NULL,
leftCorner.centerPoint.x, leftCorner.centerPoint.y,
radius,
leftCorner.startAngle, leftCorner.endAngle,
NO);
CGPathAddArc(roundedTrianglePath, NULL,
topCorner.centerPoint.x, topCorner.centerPoint.y,
radius,
topCorner.startAngle, topCorner.endAngle,
NO);
CGPathAddArc(roundedTrianglePath, NULL,
rightCorner.centerPoint.x, rightCorner.centerPoint.y,
radius,
rightCorner.startAngle, rightCorner.endAngle,
NO);
// close the path
CGPathCloseSubpath(roundedTrianglePath);


看起来像这样:

  

没有所有支持线的最终结果如下所示:

关于ios - 带圆角的UIBezierPath三角形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47164784/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com