PCB 板边倒圆角的实现方法(基本算法一)

当PCB外形是直角时,通常工程制作外形(锣带)时,会将直角或尖角的地方倒成圆角,主要是为了防止板边容易划伤板且容易扎伤人

所以当客户没有特殊要求时,PCB外形是直角一般会默认倒角0.5mm圆角(如下图所示)

 一.PCB板边倒圆角点分析

原PCB外形  如下图图示:看了这个PCB外形,产生有2个问题点.

1.外形中哪些点需倒圆角?

2.如何怎么倒圆角?

1.外形中哪些点需倒圆角?

看下图: PCB外形倒圆角的点,刚好就是我们凸包需求出的点,接下来我们将玩转凸包了,只要求出凸包,那么就可以实现PCB板边倒圆角啦。

求凸包的算法:我们可以借鉴算法导论中的查找凸包的算法(加以改进得到新的求凸包方法,详见【方法一】与【方法二】)

2.如何怎么倒圆角?

在下面有说明倒角方法.

 二. 求凸点

方法一求凸点:【采用多轮遍历,一遍一遍将凹点踢除,剩于的即是凸点】

方法一求凸点:  代码

        /// <summary>
        /// 求最大多边形最大凸包1  【采用多轮遍历将凹点踢除,剩于的即是凸点】
        /// </summary>
        /// <param name="gSur_Point_list"></param>
        /// <returns></returns>
        public List<gSur_Point> s_convex_polyon1(List<gSur_Point> gSur_Point_list)
        {
            add addCOM = new add();
            bool isOK = true;
            List<gSur_Point> PointList = new List<gSur_Point>();
            var isCCW = s_isCCW(gSur_Point_list);
            int sum = gSur_Point_list.Count() - 1;
            int n = gSur_Point_list.Count();
            for (int i = 0; i < n; i++)
            {
                int IndexPre = (i - 1) % sum;
                if (IndexPre == -1) IndexPre = sum - 1;
                int IndexCurrent = i % sum;
                int IndexNext = (i + 1) % sum;
                if (gSur_Point_list[IndexPre].type_point > 0) continue;
                if (gSur_Point_list[IndexCurrent].type_point > 0) continue;
                var multiVal = multi(gSur_Point_list[IndexPre].p, gSur_Point_list[IndexCurrent].p, gSur_Point_list[IndexNext].p);
                if ((isCCW && multiVal > 0) || (!isCCW && multiVal < 0))
                    PointList.Add(gSur_Point_list[IndexCurrent]);
                else
                    isOK = false;
            }
            List<gSur_Point> Point2List = new List<gSur_Point>(PointList);
            while (!isOK)
            {
                isOK = true;
                PointList.Clear();
                PointList.AddRange(Point2List);
                Point2List.Clear();
                sum = PointList.Count() - 1;
                n = PointList.Count();
                for (int i = 0; i < n; i++)
                {
                    int IndexPre = (i - 1) % sum;
                    if (IndexPre == -1) IndexPre = sum - 1;
                    int IndexCurrent = i % sum;
                    int IndexNext = (i + 1) % sum;
                    var multiVal = multi(PointList[IndexPre].p, PointList[IndexCurrent].p, PointList[IndexNext].p);
                    if ((isCCW && multiVal > 0) || (!isCCW && multiVal < 0))
                        Point2List.Add(PointList[IndexCurrent]);
                    else
                        isOK = false;
                }
            }
            return Point2List;
        }

方法二求凸包:【采用一边遍历找出凸点并加入队列,并同时将队列中的凸点队列中找出凹点踢除】

方法二求凸包代码:

        /// <summary>
        /// 求最大多边形最大凸包2  【采用一边遍历找出凸点并加入队列,并同时将队列中的凸点队列中找出凹点踢除】
        /// </summary>
        /// <param name="gSur_Point_list"></param>
        /// <returns></returns>
        public List<gSur_Point> s_convex_polyon2(List<gSur_Point> gSur_Point_list)
        {
            Stack<gSur_Point> StackPoint = new Stack<gSur_Point>();
            var isCCW = s_isCCW(gSur_Point_list);
            int sum = gSur_Point_list.Count() - 1;
            int n = gSur_Point_list.Count();
            for (int i = 0; i < n; i++)
            {
                int IndexPre = (i - 1) % sum;
                if (IndexPre == -1) IndexPre = sum - 1;
                int IndexCurrent = i % sum;
                int IndexNext = (i + 1) % sum;
                if (gSur_Point_list[IndexPre].type_point > 0) continue;
                if (gSur_Point_list[IndexCurrent].type_point > 0) continue;
                var multiVal = multi(gSur_Point_list[IndexPre].p, gSur_Point_list[IndexCurrent].p, gSur_Point_list[IndexNext].p);
                if ((isCCW && multiVal > 0) || (!isCCW && multiVal < 0))
                {
                    L1:
                    if (StackPoint.Count > 1)
                    {
                        var Top1Point = StackPoint.Pop();
                        var Top2Point = StackPoint.Peek();
                        multiVal = multi(Top2Point.p, Top1Point.p, gSur_Point_list[IndexCurrent].p);
                        if ((isCCW && multiVal > 0) || (!isCCW && multiVal < 0))
                            StackPoint.Push(Top1Point);
                        else
                            goto L1;
                    }
                    StackPoint.Push(gSur_Point_list[IndexCurrent]);
                }
            }
            return StackPoint.Reverse().ToList();
        }

方法三求凸包:按算法导论Graham扫描法 各节点按方位角+距离 逆时针排序  依次检查,当不属凸点于则弹出】

方法三求凸包代码

        /// <summary>
        /// 求最大多边形最大凸包5  【按算法导论Graham扫描法 各节点按方位角+距离 逆时针排序  依次检查,当不属凸点于则弹出】
        /// 由于把各点的排列顺序重新排序了,只支持折线节点(当存在弧节点时会出异常 !!!)
        /// </summary>
        /// <param name="gSur_Point_list"></param>
        /// <returns></returns>
        public List<gSur_Point> s_convex_polyon3(List<gSur_Point> gSur_Point_list)
        {
            var LeftBottomPoint = gSur_Point_list.OrderBy(tt => tt.p.y).ThenBy(tt => tt.p.x).FirstOrDefault();
            gSur_Point_list.RemoveAt(gSur_Point_list.Count - 1);
            gSur_Point_list.ForEach(tt =>
                                        {
                                            tt.Value = p2p_di(LeftBottomPoint.p, tt.p);
                                            tt.Angle = p_ang(LeftBottomPoint.p, tt.p);
                                        }
                );
            gSur_Point_list = gSur_Point_list.OrderBy(tt => tt.Angle).ThenBy(tt => tt.Value).ToList();
            gSur_Point_list.Add(gSur_Point_list[0]);
            Stack<gSur_Point> StackPoint = new Stack<gSur_Point>();
            var isCCW = true;
            int sum = gSur_Point_list.Count() - 1;
            int n = gSur_Point_list.Count();
            for (int i = 0; i < n; i++)
            {
                int IndexPre = (i - 1) % sum;
                if (IndexPre == -1) IndexPre = sum - 1;
                int IndexCurrent = i % sum;
                int IndexNext = (i + 1) % sum;
                var multiVal = multi(gSur_Point_list[IndexPre].p, gSur_Point_list[IndexCurrent].p, gSur_Point_list[IndexNext].p);
                if (isCCW && multiVal > 0)
                {
                    L1:
                    if (StackPoint.Count > 1)
                    {
                        var Top1Point = StackPoint.Pop();
                        var Top2Point = StackPoint.Peek();
                        multiVal = multi(Top2Point.p, Top1Point.p, gSur_Point_list[IndexCurrent].p);
                        if (isCCW && multiVal > 0)
                            StackPoint.Push(Top1Point);
                        else
                            goto L1;
                    }
                    StackPoint.Push(gSur_Point_list[IndexCurrent]);
                }
            }
            return StackPoint.Reverse().ToList();
        }

公共方法与数据结构

    /// <summary>
    /// Surface 坐标泛型集类1
    /// </summary>
    public class gSur_Point
    {
        public gSur_Point()
        { }
        public gSur_Point(double x_val, double y_val, byte type_point_)
        {
            this.p.x = x_val;
            this.p.y = y_val;
            this.type_point = type_point_;
        }
        public gSur_Point(gPoint p, byte type_point_)
        {
            this.p = p;
            this.type_point = type_point_;
        }
        public gPoint p;
        /// <summary>
        /// 0为折点  1为顺时针 2为逆时针
        /// </summary>
        public byte type_point { get; set; } = 0;
        /// <summary>
        /// 值
        /// </summary>
        public double Value { get; set; } = 0;
        /// <summary>
        /// 角度
        /// </summary>
        public double Angle { get; set; } = 0;
        /// <summary>
        /// 标记
        /// </summary>
        public bool isFalg { get; set; }
    }
    /// <summary>
    /// 点  数据类型 (XY)
    /// </summary>
    public struct gPoint
    {
        public gPoint(gPoint p_)
        {
            this.x = p_.x;
            this.y = p_.y;
        }
        public gPoint(double x_val, double y_val)
        {
            this.x = x_val;
            this.y = y_val;
        }
        public double x;
        public double y;
        public static gPoint operator +(gPoint p1, gPoint p2)
        {
            p1.x += p2.x;
            p1.y += p2.y;
            return p1;
        }
        public static gPoint operator -(gPoint p1, gPoint p2)
        {
            p1.x -= p2.x;
            p1.y -= p2.y;
            return p1;
        }
        public static gPoint operator +(gPoint p1, double val)
        {
            p1.x += val;
            p1.y += val;
            return p1;
        }
        public static bool operator ==(gPoint p1, gPoint p2)
        {
            return (p1.x == p2.x && p1.y == p2.y);
        }
        public static bool operator !=(gPoint p1, gPoint p2)
        {
            return !(p1.x == p2.x && p1.y == p2.y);
        }
    }
        /// <summary>
        /// 求叉积   判断【点P与线L】位置关系【小于0】在右边   【大于0】在左边   【等于0】共线
        /// </summary>
        /// <param name="ps"></param>
        /// <param name="pe"></param>
        /// <param name="p"></param>
        /// <returns>【小于0】在右边   【大于0】在左边   【等于0】共线</returns>
        public double multi(gPoint ps, gPoint pe, gPoint p)
        {
            return ((ps.x - p.x) * (pe.y - p.y) - (pe.x - p.x) * (ps.y - p.y));
        }
        /// <summary>
        /// 检测 Surface是否逆时针
        /// </summary>
        /// <param name="gSur_Point_list"></param>
        /// <returns></returns>
        public bool s_isCCW(List<gSur_Point> gSur_Point_list)
        {
            double d = 0;
            int n = gSur_Point_list.Count() - 1;
            for (int i = 0; i < n; i++)
            {
                if (gSur_Point_list[i].type_point > 0) continue;
                int NextI = i + 1 + (gSur_Point_list[i + 1].type_point > 0 ? 1 : 0);
                d += -0.5 * (gSur_Point_list[NextI].p.y + gSur_Point_list[i].p.y) * (gSur_Point_list[NextI].p.x - gSur_Point_list[i].p.x);
            }
            return d > 0;
        }
        /// <summary>
        /// 返回两点之间欧氏距离
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        public double p2p_di(gPoint p1, gPoint p2)
        {
            return Math.Sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
        }
        /// <summary>
        /// 求方位角
        /// </summary>
        /// <param name="ps"></param>
        /// <param name="pe"></param>
        /// <returns></returns>
        public double p_ang(gPoint ps, gPoint pe)
        {
            double a_ang = Math.Atan((pe.y - ps.y) / (pe.x - ps.x)) / Math.PI * 180;
            //象限角  转方位角   计算所属象限   并求得方位角
            if (pe.x >= ps.x && pe.y >= ps.y)  //    第一象限
            {
                return a_ang;
            }
            else if (!(pe.x >= ps.x) && pe.y >= ps.y)  //    第二象限
            {
                return a_ang + 180;
            }
            else if (!(pe.x >= ps.x) && !(pe.y >= ps.y))  //   第三象限
            {
                return a_ang + 180;
            }
            else if (pe.x >= ps.x && !(pe.y >= ps.y))  //    第四象限
            {
                return a_ang + 360;
            }
            else
            {
                return a_ang;
            }
        }

 三.板边凸点倒圆角方法

方法一.也最简单的倒角方法,我们将PCB板边凸点找出来后,可以直接借助genesis倒角功能就可以实现了

当然但偶尔会报错的, 且当N个小线段组成的尖角倒角会出错(要实现完美效果只有自己写倒角算法啦)

方法二:自己写倒角算法,这个算法和加内角孔算法类似(这里只是介绍简单的倒角)考虑特殊的需要扩展

可以参考这篇文章: https://www.cnblogs.com/pcbren/p/9665304.html

 四.凸点加倒圆角实现效果   

原文地址:https://www.cnblogs.com/pcbren/p/11141062.html

时间: 2024-10-05 04:19:26

PCB 板边倒圆角的实现方法(基本算法一)的相关文章

PCB板蛇形走线有什么作用

PCB上的不论什么一条走线在通过高频信号的情况下都会对该信号造成时延时,蛇形走线的主要作用是补偿"同一组相关"信号线中延时较小的部分,这些部分一般是没有或比其他信号少通过另外的逻辑处理:最典型的就是时钟线.通常它不需经过不论什么其他逻辑处理.因而其延时会小于其他相关信号. 快速数字PCB板的等线长是为了使各信号的延迟差保持在一个范围内,保证系统在同一周期内读取的数据的有效性(延迟差超过一个时钟周期时会错读下一周期的数据),一般要求延迟差不超过14时钟周期,单位长度的线延迟差也是固定的,

怎样做一块好的PCB板

大家都知道理做PCB板就是把设计好的原理图变成一块实实在在的PCB电路板,请别小看这一过程,有很多原理上行得通的东西在工程中却难以实现,或是别人能实现的东西另一些人却实现不了,因此说做一块PCB板不难,但要做好一块PCB板却不是一件容易的事情.微电子领域的两大难点在于高频信号和微弱信号的处理,在这方面PCB制作水平就显得尤其重要,同样的原理设计,同样的元器件,不同的人制作出来的PCB就具有不同的结果,那么如何才能做出一块好的PCB板呢?根据我们以往的经验,想就以下几方面谈谈自己的看法: 一:要明

PCB板的线宽、覆铜厚度与通过的电流对应的关系

1英尺=12英寸1英寸inch=1000密尔mil1mil=25.4um =0.0254mm1mil=1000uin   mil密耳有时也成英丝1um=40uin(有些公司称微英寸为麦,Es-- 保护版权!尊重作者!本文来自: 热点频道(http://www.fm898.net)--其实是微英寸)1OZ=28.35克/平方英尺=35微米 PCB板铜箔载流量                                 铜箔宽度   铜箔厚度    70um        50um       

4个设计绝招教你减少PCB板电磁干扰

电子设备的电子信号和处理器的频率不断提升,电子系统已是一个包含多种元器件和许多分系统的复杂设备.高密和高速会令系统的辐射加重,而低压和高灵敏度 会使系统的抗扰度降低. 因此,电磁干扰(EMI)实在是威胁着电子设备的安全性.可靠性和稳定性.我们在设计电子产品时,PCB板的设计对解决EMI问题至关重要. 本文主要讲解PCB设计时要注意的地方,从而减低PCB板中的电磁干扰问题. 电磁干扰(EMI)的定义 电磁干扰(EMI,Electro MagneTIc Interference),可分为辐射和传导干

PCB板焊盘不容易上锡的原因?

大家都知道PCB板焊盘不容易上锡会影响元器件贴片,从而间接导致后面测试不能正常进行.这里就给大家介绍下PCB焊盘不容易上锡的原因都有哪些?希望大家制作和使用时可以规避掉这些问题,把损失降到最低. 第一个原因是:我们要考虑到是否是客户设计的问题,需要检查是否存在焊盘与铜皮的连接方式会导致焊盘加热不充分.第二个原因是:是否存在客户操作上的问题.如果焊接方法不对,那么会影响加热功率不够.温度不够,接触时间不够造成的.第三个原因是:储藏不当的问题.①一般正常情况下喷锡面一个星期左右就会完全氧化甚至更短②

PCB_&lt;2&gt;PCB板的覆铜以及泪滴

1.将之前做的pcb板打开,将他布线在同一个面上 都是一样的线就是在一个面上 2.先补泪滴 完成补泪滴 3.覆铜 完成

Altium Designer设计PCB板之“精神”

通过一小段时间的练习,感觉先领悟设计PCB板的“精神”更加重要.在这里,我指的“精神”是指PCB板中涉及的元器件原理图及其封装设计.当然,设计PCB板还有其他方面重要的精神需要掌握.本文所提到的“精神”是画PCB板的基础.只要有这个“精神”在,你就大概能够画画板子啦(画的好不好另当别论). 一个电路是由诸多元器件组成的,其中有些元器件是可以在官方提供的库或第三方的库找到,另外的就需要自己设计啦.如果你不知道如何去设计在库中找不到的元器件,你就会无从下手.下边我们就来看看如何设计元器件吧(软件版本

PCB板层介绍

TopLayer(顶层)画出来的线条是红色,就是一般双面板的上面一层,单面板就用不到这层. BottomLayer(底层)画出来的线条是蓝色,就是单面板上面的线路这层. MidLayer1(中间层1)这个是第一层中间层,好像有30层,一般设计人员用不到,你先不用管他,多面板时候用的.默认在99SE中不显示,也用不到. Mechanical Layers(机械层)(紫红色)用于标记尺寸,板子说明,在PCB抄板加工的时候是忽略的,也就是板子做出来是看不出来的,简单点式注释的意思. Top Overl

Altium designer 6 PCB中放置厂家Logo的方法

问题现状描述: 如何在PCB中放置单位的 LOGO (单位徽标)? 处理问题方法: 1. 把LOGO 保存成 单色的 BMP 图像. 2. 在PCB 界面下使用 Open\file 命令打开文件"C:\Program Files\Altium Designer   6\Examples\Scripts\Delphiscript Scripts\Pcb\PCB Logo Creator\" 3. 运行 DXP\run script-, 运行刚才导入的script 程序. 4. 在出现的窗