PCB 板邊倒圓角的實現方法(基本演算法一)

pcbren發表於2019-07-15

當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;
            }
        }
View Code
 三.板邊凸點倒圓角方法

     方法一.也最簡單的倒角方法,我們將PCB板邊凸點找出來後,可以直接藉助genesis倒角功能就可以實現了

                   當然但偶爾會報錯的, 且當N個小線段組成的尖角倒角會出錯(要實現完美效果只有自己寫倒角演算法啦)             

             

   方法二:自己寫倒角演算法,這個演算法和加內角孔演算法類似(這裡只是介紹簡單的倒角)考慮特殊的需要擴充套件

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

            

 四.凸點加倒圓角實現效果   

 

相關文章