如何计算多边形的圆角?

2023-12-23

我正在寻找一种允许我从多边形创建圆角的算法。

我有一个代表多边形的点数组(以红色轮廓),并且在输出时我想要一个代表带圆角的多边形(以黑色轮廓)的点数组。

我还希望有一种方法来控制每个角的半径。

我尝试使用贝塞尔曲线和细分,但这不是我想要的。贝塞尔曲线和细分正在平滑多边形。

我想要的只是使角变圆。

有人知道有什么好的算法可以做到这一点吗?

我正在使用 C#,但代码必须独立于任何 .NET 库。


一些带有 Paint 的几何图形:


0. You have a corner:
Corner

1. You know the coordinates of corner points, let it be P1, P2 and P:
Points of corner

2. Now you can get vectors from points and angle between vectors:
Vectors and angle


angle = atan(PY - P1Y, PX - P1X) - atan(PY - P2Y, PX - P2X)  


3. Get the length of segment between angular point and the points of intersection with the circle.
Segment


segment = PC1 = PC2 = radius / |tan(angle / 2)|  


4. Here you need to check the length of segment and the minimal length from PP1 and PP2:
Minimal length
Length of PP1:


PP1 = sqrt((PX - P1X)2 + (PY - P1Y)2)  

Length of PP2:


PP2 = sqrt((PX - P2X)2 + (PY - P2Y)2)  

If segment > PP1 or segment > PP2 then you need to decrease the radius:


min = Min(PP1, PP2) (for polygon is better to divide this value by 2)
segment > min ?
    segment = min
    radius = segment * |tan(angle / 2)|  


5、获取PO长度:


PO = sqrt(radius2 + segment2)  


6. Get the C1X and C1Y by the proportion between the coordinates of the vector, length of vector and the length of the segment:
Coordinates of PC1

部分:


(PX - C1X) / (PX - P1X) = PC1 / PP1  

So:


C1X = PX - (PX - P1X) * PC1 / PP1  

The same for C1Y:


C1Y = PY - (PY - P1Y) * PC1 / PP1  


7. Get the C2X and C2Y by the same way:


C2X = PX - (PX - P2X) * PC2 / PP2
C2Y = PY - (PY - P2Y) * PC2 / PP2  


8. Now you can use the addition of vectors PC1 and PC2 to find the centre of circle by the same way by proportion:
Addition of vectors


(PX - OX) / (PX - CX) = PO / PC
(PY - OY) / (PY - CY) = PO / PC  

Here:


CX = C1X + C2X - PX
CY = C1Y + C2Y - PY
PC = sqrt((PX - CX)2 + (PY - CY)2)  

Let:


dx = PX - CX = PX * 2 - C1X - C2X
dy = PY - CY = PY * 2 - C1Y - C2Y  

So:


PC = sqrt(dx2 + dy2)

OX = PX - dx * PO / PC
OY = PY - dy * PO / PC  


9. Here you can draw an arc. For this you need to get start angle and end angle of arc:
Arc
Found it here https://stackoverflow.com/a/4246898/1805640:


startAngle = atan((C1Y - OY) / (C1X - OX))
endAngle = atan((C2Y - OY) / (C2X - OX))  


10. At last you need to get a sweep angle and make some checks for it:
Sweep angle

sweepAngle = endAngle - startAngle

如果sweepAngle

sweepAngle < 0 ?    
    sweepAngle = - sweepAngle
    startAngle = endAngle

检查sweepAngle是否> 180度:

sweepAngle > 180 ?    
    sweepAngle = 180 - sweepAngle


11. And now you can draw a rounded corner:
The result

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);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何计算多边形的圆角? 的相关文章

随机推荐

  • 是否有可能“盗用”会话变量(我不想知道如何)

    我目前正在用 php 做一个网站 我们使用 Session 变量来存储每个用户的权限级别 例如 如果你们中的任何一个人访问该网站 您将自动获得一个值为 member 的会话变量 我要问的是 攻击者是否有可能进入网站并将会话变量的值修改为 a
  • 输出 pandas 分组数据框而不聚合

    假设我有一个 pandas DF 其中列名称为 A B C A B C a1 b11 c11 a1 b12 c12 a2 b21 c21 a2 b22 c22 我可以按 A 分组 但是我可以得到 A B C a1 b11 b12 c11 c
  • Qt 创建者:c++:对 Class::Function 的未定义引用

    我正在 Qt 创建器中创建两个 C 项目 第一个是Application project另一个是unit test project 这两个项目分别运行良好 然而 当将两者联系在一起时 我遇到了一些问题 我包括 INCLUDEPATH app
  • 使用 Outlook VBA 区分可见和不可见附件

    要求是将附件上传到服务器 但是 我们只想上传出现在 Outlook 的 消息 行 见下图 中的内容 而不是电子邮件正文中的其他内容 由于 Outlook 本身知道应在行中显示哪个附件 因此必须有用于在内部区分它们的信息 那么 我怎样才能在我
  • 使用 libgdx 在运行时用文本生成纹理

    我正在开发一个电话文字游戏 昨天 我决定使用 libgdx 切换到 OpenGL 以尝试提高图形性能和电池使用率 瞄准更多平台 字母图块在 2D 画布上绘图的方式是每个字母图块都会为自己创建一个位图 我会 从背景位图创建一个新的可变位图 在
  • 在不实际打开大写锁定的情况下打开键盘大写锁定指示灯的方法

    我正在编写一个程序 使用大写锁定 https en wikipedia org wiki Caps Lock作为拨动开关 如果能设置按键的 LED 来显示我的程序打开或关闭 就像 Caps Lock 键自然所做的那样 那就太好了 我知道我可
  • 使用 NSAttributedString 将 UILabel 中的文本居中

    对我正在开发的应用程序进行一些基本改进 对于 iOS 快速开发场景来说还是个新手 我认为代码中的文本行会自动居中 因为我将标签设置为居中 经过一番研究后 我发现事实并非如此 我如何将这样的代码对齐到中心 let atrString try
  • 如何抓取需要身份验证的 ASPX 页面

    使用 python 的库 Requests 我试图抓取一个 ASPX 网站 https cei bmfbovespa com br CEI Responsivo home aspx https cei bmfbovespa com br C
  • 如何在 Recharts 中为 YAxis 创建自定义垂直标签,该标签将缩放以适应标签太长的情况?

    我正在使用 Recharts 库和一些垂直标签构建一些组合图表YAxis太长并且被切断 我的标签被剪掉的照片 https i stack imgur com FUwrl png 我尝试使用自定义标签
  • “分支与起源是最新的”但实际上不是

    该消息是否有点误导 当结帐分支时 例如 b535 git 这样做并说 您的分支已更新为 origin b535 这听起来就像我当地分行的情况b535已是最新 git checkout b535 Previous HEAD position
  • 日期选择器的多个实例

    我如何能够使用日期选择器的多个实例 一个日期选择器正在工作 但日期选择器的下一个实例正在调用相同的弹出窗口 如何继续 任何人都可以给我您的电子邮件 ID 我会将整个代码邮寄给您 然后您将能够以更好的方式了解整个想法 http jsfiddl
  • Sequelize '自 v4.0.0 起需要显式提供方言'

    我需要在 node js 环境中运行第三方应用程序 但 Sequelize 抛出 自 v4 0 0 起需要显式提供方言 我在这里找到了类似的主题自 v4 0 0 起需要显式提供方言 https stackoverflow com quest
  • 打开活动而不显示 UIActivityViewController

    我希望能够在滚动视图中包含社交图标 当单击该图标时 其功能与我在呈现 UIActivityViewController 后单击它们时的功能相同 我不想呈现 UIActivityViewController 我知道这是不可能的 但是一个名为
  • CATransaction:图层更改但不动画

    我正在尝试在用户点击按钮时为 iPad 应用程序中的部分 UI 制作动画 我的操作方法中有这段代码 它的工作原理是 用户界面按照我的预期进行更改 但不会以动画方式进行更改 它只是立即改变 我肯定错过了什么 IBAction someActi
  • 可以从 (a,b) 移动到 (c,d)

    问题是输出是否可以从给定点移动 a b 达到目标 c d 我们仅限于正坐标 可以进行以下两种动作 a b gt a b b a b gt a b a 例如 1 1 to 5 4 is True您可以执行以下操作 使用第 2 步 3 次 1
  • 如何清空 JS 对象?

    我有一个像 var person id null name John Doe 将对象值插入数据库后 我将从服务器获取另一个对象 var personInDB id 1234 name John Doe 我用过angular merge使用更
  • 如何远程访问私有 docker-registry?

    我正在尝试使用来自以下位置的图像设置一个私有 docker 注册表 https github com docker docker registry https github com docker docker registry 只需运行 d
  • 使用 iTextSharp 将图像添加到 PDF 并正确缩放

    这是我的代码 它正确添加了我想要的图片并且一切正常except图像使用其原始分辨率 因此如果图像很大 则会被裁剪以适合页面 有没有什么方法可以让图片像缩放功能一样拉伸以适合 但又保持纵横比 那里一定有我想念的东西 P Here s a pi
  • R中的数据帧累积游程长度编码

    我有一个数据帧 其中包含与观察值 1 或 0 相关的值 我想计算 1 的连续出现次数 重置为 0 游程长度编码函数 rle 看起来它可以完成工作 但我无法将数据转换为所需的格式 我想尝试在不编写自定义函数的情况下执行此操作 在下面的数据中
  • 如何计算多边形的圆角?

    我正在寻找一种允许我从多边形创建圆角的算法 我有一个代表多边形的点数组 以红色轮廓 并且在输出时我想要一个代表带圆角的多边形 以黑色轮廓 的点数组 我还希望有一种方法来控制每个角的半径 我尝试使用贝塞尔曲线和细分 但这不是我想要的 贝塞尔曲