如何计算多边形的圆角?
Posted
技术标签:
【中文标题】如何计算多边形的圆角?【英文标题】:How to calculate rounded corners for a polygon? 【发布时间】:2014-09-06 10:52:11 【问题描述】:我正在寻找一种允许我从多边形创建圆角的算法。
我有一个代表多边形的点数组(红色轮廓),在输出时我想要一个代表圆角多边形的点数组(黑色轮廓)。
我也想有办法控制每个角的半径。
我尝试使用贝塞尔曲线和细分,但这不是我想要的。贝塞尔曲线和细分正在平滑多边形。
我想要的只是使角落变圆。
有人知道这样做的好算法吗?
我正在使用 C#,但代码必须独立于任何 .NET 库。
【问题讨论】:
给定 R,找到与两个相邻线段相切的圆。圆心在角平分线上,t=R/sin(a/2)
,其中t
是圆心到角点的距离,a
是角。
【参考方案1】:
您正在寻找与给定半径的两条连接线段相切的弧,该半径由一些连续的点数组给出。找到这条弧的算法如下:
对于每个段,构造一个法线向量。
如果您在 2d 中工作,您只需将两个端点相减即可得到一个切向量 (X, Y)。在这种情况下,法线向量将为正或负(-Y,X)。 Normalize 长度为 1 的法线向量。最后,选择与下一段的切向量为正点积的方向。 (请参阅下面的更新)。
如果您在 3d 而不是 2d 中工作,则要获得法线,cross 要舍入顶点处的两个线段的切向量以获得与线平面垂直的向量。如果垂线的长度为零,则线段是平行的,不需要倒圆。否则,对其进行归一化,然后将垂线与切线相交得到法线。)
使用法线向量,将每条线段向多边形内部偏移所需的半径。要偏移段,请使用您刚刚计算的法线向量 N 偏移其端点,如下所示:P' = P + r * N(线性组合)。
Intersect the two offset lines 找到中心。 (这是因为圆的半径矢量总是垂直于它的切线。)
要找到圆与每个线段相交的点,请将圆心向后偏移到每个原始线段。这些将是您的弧线的端点。
确保弧端点在每个段内,否则您将创建一个自相交的多边形。
使用您确定的中心和半径创建一个通过两个端点的弧。
我手头没有合适的绘图软件,但这张图显示了这个想法:
此时,您需要引入类来表示由线段和弧段组成的图形,或者将弧线多边形化到适当的精度并将所有线段添加到多边形中。
更新:我更新了图像,标记了点 P1、P2 和 P3,以及法线向量 Norm12 和 Norm23。归一化法线仅在翻转方向上是唯一的,您应该按如下方式选择翻转:
带有 (P3 - P2) 的 Norm12 的 dot product 必须为正。如果为负数,则将 Norm12 乘以 -1.0。如果为零,则这些点是共线的,不需要创建圆角。这是因为您要向 P3 偏移。
Norm23 与 (P1 - P2) 的点积也必须为正,因为您正向 P1 偏移。
【讨论】:
谢谢,我理解您希望我这样做的方式。但我现在有一个问题:如何将一条线向多边形内部偏移? @ZouBi 基本上这条线总是与其他两条线相交。也许你可以检查一下。 dbc,感谢您的编辑。我认为这是最好的答案,我会尝试为此编写代码。 @JakeStelman - 我注意到你的编辑被拒绝了,但如果你愿意,你可以添加你的 Matlab 代码作为单独的答案。看起来很有用!【参考方案2】:这是一种使用几何的方法:-
两条线与内接圆相切 切线的法线在圆心处相交。 设线间夹角为 X 圆心对角为 K = 360-90*2-X = 180-X 让我们决定两个切线点为 (x1,y) 和 (x2,y) 连接点的和弦长度为 l = (x2-x1) 在圆内,弦和长度为 r(半径)的两条法线形成等腰三角形 下垂线将三角形分成相等的对半直角三角形。 其中一个角是K/2,边是l/2 使用直角三角形的性质 sin(K/2) = (l/2)/r r = (l/2)/sin(K/2) 但是 K = 180-X 所以 r = (l/2)/sin(90-X/2) = (l/2)/cos(X/2) 因此 r = (x2-x1)/(2*cos(X/2)) 现在只需使用半径 r 从 (x1,y) 到 (x2,y) 绘制一条弧
注意:-
上面的解释仅适用于在原点相交的线并且 Y 轴将它们之间的角度分成两半。但它同样适用于所有角落,只需要在应用上述内容之前应用旋转和平移。此外,您需要从要绘制圆弧的位置选择一些 x 交点值。值不应太远或太接近原点
【讨论】:
感谢您抽出宝贵时间,但我几乎不了解您的方式以及如何实现它... 试着想象你的角顶点在原点并朝向正 y 轴,Y 轴平分它们之间的角度。 抱歉,没有图片我无法解释,但会尝试添加一张。 如果你理解的话,解决办法就是常数时间,而且你可以旋转和平移其他顶点并做步骤和反向平移和旋转。【参考方案3】:一些带有 Paint 的几何图形:
0.你有一个角: 1.你知道角点的坐标,设P1, P2 and P: 2. 现在可以从向量之间的点和角度获取向量:
角度 = atan(PY - P1Y, PX - P1 X) - atan(PY - P2Y, PX - P2X)
3.获取角点与圆交点之间的线段长度。
段 = PC1 = PC2 = 半径 / |tan(角度 / 2)|
4.这里需要检查段的长度和PP1和PP2的最小长度: PP1的长度:
PP1 = sqrt((PX - P1X)2 sup> + (PY - P1Y)2)
PP长度2:
PP2 = sqrt((PX - P2X)2 sup> + (PY - P2Y)2)
如果段 > PP1 或段 > PP2 那么你需要减小半径:
min = Min(PP1, PP2) (对于多边形,最好将此值除以 2) 段 > 分钟 ? 段 = 分钟 半径 = 段 * |tan(角度 / 2)|
5.获取PO的长度:
PO = sqrt(半径2 + 段2)
6、通过向量坐标、长度的比例得到C1X和C1Y向量的长度和段的长度:
比例:
(PX - C1X) / (PX - P1X) = PC1 / PP1
所以:
C1X = PX - (PX - P1 X) * PC1 / PP1
C1Y也是一样:
C1Y = PY - (PY - P1 Y) * PC1 / PP1
7、用同样的方法得到C2X和C2Y:
C2X = PX - (PX - P2 X) * PC2 / PP2 C2Y = PY - (PY - P2Y sub>) * PC2 / PP2
8、现在可以用向量PC1和PC2相加,按比例求圆心: p>
(PX - OX) / (PX - CX) = PO / PC (PY - OY) / (PY - CY) = PO / PC
这里:
CX = C1X + C2X - P X CY = C1Y + C2Y - P是的 PC = sqrt((PX - CX)2 + (PY - CY )2)
让:
dx = PX - CX = PX * 2 - C1X - C2X dy = PY - CY = PY * 2 - C1Y - C2Y
所以:
PC = sqrt(dx2 + dy2) OX = PX - dx * PO / PC OY = PY - dy * PO / PC
9.在这里你可以画一个圆弧。为此,您需要获取弧的开始角度和结束角度: 找到了here:
startAngle = atan((C1Y - OY) / (C1X - OX)) endAngle = atan((C2Y - OY) / (C2X sub> - OX))
10. 最后你需要得到一个扫角并做一些检查:
sweepAngle = endAngle - startAngle
如果 sweepAngle
sweepAngle < 0 ?
sweepAngle = - sweepAngle
startAngle = endAngle
检查sweepAngle是否> 180度:
sweepAngle > 180 ?
sweepAngle = 180 - sweepAngle
11. 现在你可以画圆角了:
一些几何与 c#:
private void DrawRoundedCorner(Graphics graphics, PointF angularPoint,
PointF p1, PointF p2, float radius)
//Vector 1
double dx1 = angularPoint.X - p1.X;
double dy1 = angularPoint.Y - p1.Y;
//Vector 2
double dx2 = angularPoint.X - p2.X;
double dy2 = angularPoint.Y - p2.Y;
//Angle between vector 1 and vector 2 divided by 2
double angle = (Math.Atan2(dy1, dx1) - Math.Atan2(dy2, dx2)) / 2;
// The length of segment between angular point and the
// points of intersection with the circle of a given radius
double tan = Math.Abs(Math.Tan(angle));
double segment = radius / tan;
//Check the segment
double length1 = GetLength(dx1, dy1);
double length2 = GetLength(dx2, dy2);
double length = Math.Min(length1, length2);
if (segment > length)
segment = length;
radius = (float)(length * tan);
// Points of intersection are calculated by the proportion between
// the coordinates of the vector, length of vector and the length of the segment.
var p1Cross = GetProportionPoint(angularPoint, segment, length1, dx1, dy1);
var p2Cross = GetProportionPoint(angularPoint, segment, length2, dx2, dy2);
// Calculation of the coordinates of the circle
// center by the addition of angular vectors.
double dx = angularPoint.X * 2 - p1Cross.X - p2Cross.X;
double dy = angularPoint.Y * 2 - p1Cross.Y - p2Cross.Y;
double L = GetLength(dx, dy);
double d = GetLength(segment, radius);
var circlePoint = GetProportionPoint(angularPoint, d, L, dx, dy);
//StartAngle and EndAngle of arc
var startAngle = Math.Atan2(p1Cross.Y - circlePoint.Y, p1Cross.X - circlePoint.X);
var endAngle = Math.Atan2(p2Cross.Y - circlePoint.Y, p2Cross.X - circlePoint.X);
//Sweep angle
var sweepAngle = endAngle - startAngle;
//Some additional checks
if (sweepAngle < 0)
startAngle = endAngle;
sweepAngle = -sweepAngle;
if (sweepAngle > Math.PI)
sweepAngle = Math.PI - sweepAngle;
//Draw result using graphics
var pen = new Pen(Color.Black);
graphics.Clear(Color.White);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.DrawLine(pen, p1, p1Cross);
graphics.DrawLine(pen, p2, p2Cross);
var left = circlePoint.X - radius;
var top = circlePoint.Y - radius;
var diameter = 2 * radius;
var degreeFactor = 180 / Math.PI;
graphics.DrawArc(pen, left, top, diameter, diameter,
(float)(startAngle * degreeFactor),
(float)(sweepAngle * degreeFactor));
private double GetLength(double dx, double dy)
return Math.Sqrt(dx * dx + dy * dy);
private PointF GetProportionPoint(PointF point, double segment,
double length, double dx, double dy)
double factor = segment / length;
return new PointF((float)(point.X - dx * factor),
(float)(point.Y - dy * factor));
要获得弧点,您可以使用:
//One point for each degree. But in some cases it will be necessary
// to use more points. Just change a degreeFactor.
int pointsCount = (int)Math.Abs(sweepAngle * degreeFactor);
int sign = Math.Sign(sweepAngle);
PointF[] points = new PointF[pointsCount];
for (int i = 0; i < pointsCount; ++i)
var pointX =
(float)(circlePoint.X
+ Math.Cos(startAngle + sign * (double)i / degreeFactor)
* radius);
var pointY =
(float)(circlePoint.Y
+ Math.Sin(startAngle + sign * (double)i / degreeFactor)
* radius);
points[i] = new PointF(pointX, pointY);
【讨论】:
谢谢!它完美地工作! dbc 的答案解释了方法,你的答案给出了实现。很遗憾我无法验证你的两个答案。对于那些想要生成点而不是使用图形库绘制圆弧的人,这里是代码:PointF[] points = new PointF[pointsCount]; for(int i=0; i<pointsCount; ++i) points[i] = new PointF(circleRadius.x + Math.Cos(startAngle + i * sweepAngle / pointsCount) * radius, circleRadius.y + Math.Sin(startAngle + i * sweepAngle / pointsCount) * radius);
@ZouBi 我更正了对sweepAngle 的附加检查中的错误(检查新代码),并用您的代码更新了我的答案并进行了一些更改。我的算法与 dbc 的算法不同。
@ZouBi 我已经用我的算法解释更新了我的答案。
谢谢你,你的评论很清楚,解释了一切,并在 c# 中给出了一个实现。我将你标记为正确答案。
万一有人回来遇到和我一样的问题。我不得不将 if (sweepAngle > Math.PI) sweepAngle = Math.PI - sweepAngle;
更改为 if (sweepAngle > Math.PI) sweepAngle = -(2 * Math.PI - sweepAngle);
以修复一些缺少部分的曲线。【参考方案4】:
下面是我在c#上对dbc的想法的实现:
/// <summary>
/// Round polygon corners
/// </summary>
/// <param name="points">Vertices array</param>
/// <param name="radius">Round radius</param>
/// <returns></returns>
static public GraphicsPath RoundCorners(PointF[] points, float radius)
GraphicsPath retval = new GraphicsPath();
if (points.Length < 3)
throw new ArgumentException();
rects = new RectangleF[points.Length];
PointF pt1, pt2;
//Vectors for polygon sides and normal vectors
Vector v1, v2, n1 = new Vector(), n2 = new Vector();
//Rectangle that bounds arc
SizeF size = new SizeF(2 * radius, 2 * radius);
//Arc center
PointF center = new PointF();
for (int i = 0; i < points.Length; i++)
pt1 = points[i];//First vertex
pt2 = points[i == points.Length - 1 ? 0 : i + 1];//Second vertex
v1 = new Vector(pt2.X, pt2.Y) - new Vector(pt1.X, pt1.Y);//One vector
pt2 = points[i == 0 ? points.Length - 1 : i - 1];//Third vertex
v2 = new Vector(pt2.X, pt2.Y) - new Vector(pt1.X, pt1.Y);//Second vector
//Angle between vectors
float sweepangle = (float)Vector.AngleBetween(v1, v2);
//Direction for normal vectors
if (sweepangle < 0)
n1 = new Vector(v1.Y, -v1.X);
n2 = new Vector(-v2.Y, v2.X);
else
n1 = new Vector(-v1.Y, v1.X);
n2 = new Vector(v2.Y, -v2.X);
n1.Normalize(); n2.Normalize();
n1 *= radius; n2 *= radius;
/// Points for lines which intersect in the arc center
PointF pt = points[i];
pt1 = new PointF((float)(pt.X + n1.X), (float)(pt.Y + n1.Y));
pt2 = new PointF((float)(pt.X + n2.X), (float)(pt.Y + n2.Y));
double m1 = v1.Y / v1.X, m2 = v2.Y / v2.X;
//Arc center
if (v1.X == 0) // first line is parallel OY
center.X = pt1.X;
center.Y = (float)(m2 * (pt1.X - pt2.X) + pt2.Y);
else if (v1.Y == 0) // first line is parallel OX
center.X = (float)((pt1.Y - pt2.Y) / m2 + pt2.X);
center.Y = pt1.Y;
else if (v2.X == 0) // second line is parallel OY
center.X = pt2.X;
center.Y = (float)(m1 * (pt2.X - pt1.X) + pt1.Y);
else if (v2.Y == 0) //second line is parallel OX
center.X = (float)((pt2.Y - pt1.Y) / m1 + pt1.X);
center.Y = pt2.Y;
else
center.X = (float)((pt2.Y - pt1.Y + m1 * pt1.X - m2 * pt2.X) / (m1 - m2));
center.Y = (float)(pt1.Y + m1 * (center.X - pt1.X));
rects[i] = new RectangleF(center.X - 2, center.Y - 2, 4, 4);
//Tangent points on polygon sides
n1.Negate(); n2.Negate();
pt1 = new PointF((float)(center.X + n1.X), (float)(center.Y + n1.Y));
pt2 = new PointF((float)(center.X + n2.X), (float)(center.Y + n2.Y));
//Rectangle that bounds tangent arc
RectangleF rect = new RectangleF(new PointF(center.X - radius, center.Y - radius), size);
sweepangle = (float)Vector.AngleBetween(n2, n1);
retval.AddArc(rect, (float)Vector.AngleBetween(new Vector(1, 0), n2), sweepangle);
retval.CloseAllFigures();
return retval;
【讨论】:
【参考方案5】:nempoBu4 answer的Objective-C改编:
typedef enum
path_move_to,
path_line_to
Path_command;
static inline CGFloat sqr (CGFloat a)
return a * a;
static inline CGFloat positive_angle (CGFloat angle)
return angle < 0 ? angle + 2 * (CGFloat) M_PI : angle;
static void add_corner (UIBezierPath* path, CGPoint p1, CGPoint p, CGPoint p2, CGFloat radius, Path_command first_add)
// 2
CGFloat angle = positive_angle (atan2f (p.y - p1.y, p.x - p1.x) - atan2f (p.y - p2.y, p.x - p2.x));
// 3
CGFloat segment = radius / fabsf (tanf (angle / 2));
CGFloat p_c1 = segment;
CGFloat p_c2 = segment;
// 4
CGFloat p_p1 = sqrtf (sqr (p.x - p1.x) + sqr (p.y - p1.y));
CGFloat p_p2 = sqrtf (sqr (p.x - p2.x) + sqr (p.y - p2.y));
CGFloat min = MIN(p_p1, p_p2);
if (segment > min)
segment = min;
radius = segment * fabsf (tanf (angle / 2));
// 5
CGFloat p_o = sqrtf (sqr (radius) + sqr (segment));
// 6
CGPoint c1;
c1.x = (CGFloat) (p.x - (p.x - p1.x) * p_c1 / p_p1);
c1.y = (CGFloat) (p.y - (p.y - p1.y) * p_c1 / p_p1);
// 7
CGPoint c2;
c2.x = (CGFloat) (p.x - (p.x - p2.x) * p_c2 / p_p2);
c2.y = (CGFloat) (p.y - (p.y - p2.y) * p_c2 / p_p2);
// 8
CGFloat dx = p.x * 2 - c1.x - c2.x;
CGFloat dy = p.y * 2 - c1.y - c2.y;
CGFloat p_c = sqrtf (sqr (dx) + sqr (dy));
CGPoint o;
o.x = p.x - dx * p_o / p_c;
o.y = p.y - dy * p_o / p_c;
// 9
CGFloat start_angle = positive_angle (atan2f ((c1.y - o.y), (c1.x - o.x)));
CGFloat end_angle = positive_angle (atan2f ((c2.y - o.y), (c2.x - o.x)));
if (first_add == path_move_to)
[path moveToPoint: c1];
else
[path addLineToPoint: c1];
[path addArcWithCenter: o radius: radius startAngle: start_angle endAngle: end_angle clockwise: angle < M_PI];
UIBezierPath* path_with_rounded_corners (NSArray<NSValue*>* points, CGFloat corner_radius)
UIBezierPath* path = [UIBezierPath bezierPath];
NSUInteger count = points.count;
for (NSUInteger i = 0; i < count; ++i)
CGPoint prev = points[i > 0 ? i - 1 : count - 1].CGPointValue;
CGPoint p = points[i].CGPointValue;
CGPoint next = points[i + 1 < count ? i + 1 : 0].CGPointValue;
add_corner (path, prev, p, next, corner_radius, i == 0 ? path_move_to : path_line_to);
[path closePath];
return path;
【讨论】:
这是一道 C# 题,不是客观的 C 题。 @Teepeemm,您对 C# 的看法是对的,但 nempoBu4 的精彩回答帮助我进行 ios 开发。许多 iOS 和 Mac OS 开发人员,像我一样,从谷歌搜索访问这个页面。我认为,我们的目标是帮助他们。 meta.***.com/q/290046/2336725 可能是一个有用的参考。我不知道任何一种语言都知道Objective C和C#有多么不同。除了对编程语言的简单更改之外,您的实现是否添加了任何其他内容?此外,您可能希望删除所有多余的空行。 我的改编对原始算法进行了一些小改动:1)角度转换为正值; 2) iOs 使用不同的方式来定义弧(开始、结束角度和顺时针标志)与 .Net(开始、扫描角度)。 3)我的算法用圆角构建完全封闭的图形路径,而不是在拐角处绘制圆弧。【参考方案6】:我可以提供一种简单且非常可计算且可编程的方法,可以说它可以使用最少的计算——注意“只有”3 个平方根,没有反三角函数。
由于这是 Stack Overflow,而且我已经通过使用 javascript 和 SVG 的实际计算验证了这一点,因此我将使用 ECMAScript (JavaScript) 编程语言来帮助解释解决方案。
假设您要“圆”的某个角由已知点 A、B 和 C 组成,其中 B 是“角落”。
解决方案可以通过以下步骤来描述:
计算BF向量的长度。
长度等于圆的半径 (FO)(显然是您自己选择并因此知道)除以向量 BF 和BO。这显然是因为由点 B、O 和 F 组成的三角形是一个“直角”三角形(向量 BF 之间的夹角 和 FO 是 90 度)。
向量BF和BO之间的夹角是向量BA和BC之间夹角的一半。这听起来可能很明显,也可能不明显,请放心,这是可以证明的,但我省略了证明。
角度之间的关系很有用,因为恰好有一个相当简单的方程来表示角度的正切和两倍角度的余弦之间的关系:Math.tan(a/2) == Math.sqrt((1 - Math.cos(a)) / (1 + Math.cos(a))
。
碰巧向量 BA 和 BC (Math.cos(a)
) 之间的夹角的余弦是两个向量的点积除以它们的长度(参见definition of vector dot product on Wikipedia)。
因此,在计算了角度的余弦后,您可以计算半角的正切,然后计算 BF 的长度:
(图例:我将向量(BA
、BC
等)建模为具有x
和y
属性的对象,用于它们在屏幕空间中各自的坐标(X 向右增加,Y 向下);@ 987654330@ 是想要的圆角半径,BF_length
是 BF 的长度(显然))
/// Helper functions
const length = v => Math.sqrt(v.x * v.x + v.y * v.y);
const dot_product = (v1, v2) => v1.x * v2.x + v1.y * v2.y;
const cosine_between = (v1, v2) => dot_product(v1, v2) / (length(v1) * length(v2));
const cos_a = cosine_between(BA, BC);
const tan_half_a = Math.sqrt((1 - cos_a) / (1 + cos_a));
const BF_length = radius / tan_half_a;
计算 BF 向量。我们现在知道它的长度(上面的BF_length
),并且由于 BF 位于同一条线上,因此矢量 BA 位于前者(以及,暗示的坐标点 F 相对于点 B) 可通过将 BF 的长度乘以 的单位向量等价物进行标量乘法计算巴:
/// Helper functions
const unit = v =>
const l = length(v);
return x: v.x / l, y: v.y / l ;
;
const scalar_multiply = (v, n) => ( x: v.x * n, y: v.y * n );
const BF = scalar_multiply(unit(BA), BF_length);
现在您已经从上一步获得了 F 的坐标,您可以计算 FO 向量或 O 坐标。这是通过旋转一些长度为 radius
的向量来完成的,该向量位于向量 BA 所在的同一条线上,两个向量都指向同一方向,旋转 90 度,然后移动它使其开始在 F。
现在,旋转是顺时针还是逆时针取决于向量BA和BC之间角度的符号,更具体地说,如果BA之间的角度差 em> 和 BC 为正则逆时针旋转,否则顺时针旋转。
如果我们可以避免的话,我们不想计算 角度——毕竟这是我们想要的差异的标志。长话短说,角度的符号(sign
)可以用表达式Math.sign(BA.x * BC.y - BA.y * BC.x)
来计算。
这里是计算O(O
)的坐标,F
是井的坐标,F:
/// Helper functions
const add = (v1, v2) => ( x: v1.x + v2.x, y: v1.y + v2.y );
const rotate_by_90_degrees = (v, sign) => ( x: -v.y * sign, y: v.x * sign );
const sign = Math.sign(BA.x * BC.y - BA.y * BC.x);
const O = add(F, rotate_by_90_degrees(scalar_multiply(unit(BA), radius), sign));
就是这样——因为您已经获得了点 O,其坐标与原始点的坐标在同一空间中(A、B em> 和 C),你可以把一个使用半径的圆以 O 为圆心。
这对于大多数使用这个答案的人来说可能是显而易见的,但为了安全起见:请记住,在这个答案中,我通常将向量和坐标称为同一种度量——向量具有 arity ,这是它具有的组件数量;对于二维坐标系统,arity 显然是 2。因此,向量对象没有专门编码其“开始”,只有“结束”——因为只有两个分量,这意味着向量“开始”在坐标系原点。例如,向量BA
确实是点B
和A
之间的向量,但是由于程序只存储向量的两个分量(sn-ps 中的x
和y
),所以它是就好像矢量被移动了一样,点B
现在位于坐标系的原点。一个点也由两个分量组成,所以“向量”和“点”是可以互换的。你必须非常清楚地理解这一点,否则我提供的一些计算有时可能看起来很奇怪。如果您只是将此答案中的向量视为具有两个元素的“一维”数组,则可能会更容易。事实上,这就是我最初编写这些的方式,但为了用代码说明解决方案,我切换到具有 x
和 y
属性的对象。
从点 F 和一些 F' (它在BC
向量上的等价物)计算相应的圆弧应该相当容易,考虑到一切,但我除非有人表示希望,否则不包括它。
【讨论】:
以上是关于如何计算多边形的圆角?的主要内容,如果未能解决你的问题,请参考以下文章