PCB 銅皮(Surface)折線多邊形擴大縮小實現(第一節)

pcbren發表於2019-05-27

    繼續銅皮多邊形的相關的演算法, 如何用程式碼實現多邊形的擴大與縮小,這部份內容準備分為四節內容來講解,

      第一節,折線多邊形的擴大縮小(不包含圓弧)   此篇講第一節

      第二節,帶圓弧的多邊形的擴大縮小

      第三節,多邊形擴大縮小----尖角處理

      第四節,多邊形擴大縮小----自相交處理

一.多邊形擴大縮小偏移演算法

     1.偏移點計算方法: (具體見貼的程式碼)

          1.求出ABC三角形的角度【1】,即可求出BDP三角形角度【2】
          2.通過偏移距離L與BDP角度【2】求出:點B到點D距離
          3.求出ABC方位角位角
          4.以點B為基準點,ABC方位角,點B到點D距離求出點D座標

        

     2.演算法步驟:

         1.獲取多邊形點陣列 List<gSur_Point_list>

         2.先檢測多邊形是順時針,還是逆時針,這步必不可少,決定後面多邊形計算是向內偏移還是向外偏移

            這好比鑼(銑)帶的偏移演算法,鑼外形必須向外偏移,如果鑼帶是逆時針,那麼它是Right補償,那如果是順時針那麼它就是Left補償;

         3.遍歷List<gSur_Point_list>,依次求出3點偏移後的的相交點(按上圖偏移點計算方法實現)。

二.銅皮Surface折線多邊形擴大縮小程式碼

    1.呼叫程式碼:

           //獲取層名為3的Surface資料
            gLayer workLayerInfo = g.getFEATURES("3");
            var PolyListUp= calc2.s_offset(workLayerInfo.Slist, 1);
            addCOM.line_poly(PolyListUp, 120);
            var PolyListDown = calc2.s_offset(workLayerInfo.Slist, 1);
            addCOM.line_poly(PolyListDown, 120);

            //獲取Profile資料
            var Profile = g.getFEATURES_Profile();
            var ProfileUp = calc2.s_offset(Profile.sur_list, 1);
            addCOM.line_poly(ProfileUp, 120);
            var ProfileDown = calc2.s_offset(Profile.sur_list, -1);
            addCOM.line_poly(ProfileDown, 120);

    2. 折線多邊形擴大縮小實現函式

        /// <summary>
        /// Surface偏移(擴大或縮小)
        /// </summary>
        /// <param name="gSur_Point_list"></param>
        /// <param name="offset_val">偏移數值(正值加大  負值縮小)</param>
        /// <returns></returns>
        public List<gSur_Point> s_offset(List<gSur_Point> gSur_Point_list, double offset_val)
        {
            bool isCCW = s_isCCW(gSur_Point_list);
            int count = gSur_Point_list.Count();
            List<gSur_Point> Point_list = new List<gSur_Point>();
            Point_list.Add(gSur_Point_list[0]);
            gPoint CurrentP = new gPoint();
            for (int i = 1; i < count; i++)
            {
                int NextIndex = (count == i + 1) ? 1 : i + 1;
                CurrentP = l2l_OffsetIntersect(gSur_Point_list[i - 1].p, gSur_Point_list[i].p, gSur_Point_list[NextIndex].p, isCCW, offset_val);
                Point_list.Add(new gSur_Point(CurrentP, gSur_Point_list[i].type_point));
            }
            gSur_Point_list[0].p = CurrentP;
            return Point_list;
        }
        /// <summary>
        /// Surface偏移(擴大或縮小)
        /// </summary>
        /// <param name="gS"></param>
        /// <param name="offset_val"></param>
        /// <returns></returns>
        public gS s_offset(gS gS, double offset_val)
        {
            gS SurfacePolyline = new gS();
            SurfacePolyline.negative = gS.negative;
            SurfacePolyline.attribut = gS.attribut;
            foreach (var Polyline in gS.sur_group)
            {
                gSur_list sur_list = new gSur_list();
                sur_list.is_ccw = Polyline.is_ccw;
                sur_list.is_hole = Polyline.is_hole;
                if (sur_list.is_hole)
                    sur_list.sur_list = s_offset(Polyline.sur_list, -offset_val);
                else
                    sur_list.sur_list = s_offset(Polyline.sur_list, offset_val);
                SurfacePolyline.sur_group.Add(sur_list);
            }
            return SurfacePolyline;
        }
        /// <summary>
        /// Surface偏移(擴大或縮小)
        /// </summary>
        /// <param name="gS_list"></param>
        /// <param name="offset_val"></param>
        /// <returns></returns>
        public List<gS> s_offset(List<gS> gS_list, double offset_val)
        {
            List<gS> surface_list = new List<gS>();
            foreach (var item in gS_list)
            {
                surface_list.Add(s_offset(item, offset_val));
            }
            return surface_list;
        }
        /// <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="ps"></param>
        /// <param name="pc"></param>
        /// <param name="pe"></param>
        /// <param name="ccw"></param>
        /// <param name="OffsetVal"></param>
        /// <returns></returns>
        public gPoint l2l_OffsetIntersect(gPoint ps, gPoint pc, gPoint pe, bool ccw, double OffsetVal)
        {
            double center_dirdction = 0;
            bool islg180deg = false;
            double pcAng = a_Angle(ps, pc, pe, ccw, ref center_dirdction, ref islg180deg);//交點圓心角
            double pcSinVal = OffsetVal / (Math.Sin(pcAng * 0.5 * Math.PI / 180)); //交點增量
            var IntersectP = p_val_ang(pc, pcSinVal, center_dirdction);
            return IntersectP;
        }
        /// <summary>
        /// 求弧Arc圓心角   3點    //後續改進  用叉積 與3P求角度求解  驗證哪個效率高
        /// </summary>
        /// <param name="ps"></param>
        /// <param name="pc"></param>
        /// <param name="pe"></param>
        /// <param name="ccw"></param>
        /// <param name="center_dirdction">中心方位角</param> 
        ///  <param name="islg180deg">3點組成的內角不超180度,超出計算按反方位角計算 當值為true時  ccw值則失效了  返回值確認與P1,P2關係</param> 
        /// <returns></returns>
        public double a_Angle(gPoint ps, gPoint pc, gPoint pe, bool ccw, ref double center_dirdction, ref bool islg180deg)
        {
            double angle_s, angle_e, angle_sum;
            if (ccw)
            {
                angle_s = p_ang(pc, pe);
                angle_e = p_ang(pc, ps);
            }
            else
            {
                angle_s = p_ang(pc, ps);
                angle_e = p_ang(pc, pe);
            }
            if (angle_s == 360) { angle_s = 0; }
            if (angle_e >= angle_s)
            {
                angle_sum = 360 - (angle_e - angle_s);
                center_dirdction = (angle_s + angle_e) * 0.5 + 180;
            }
            else
            {
                angle_sum = angle_s - angle_e;
                center_dirdction = (angle_s + angle_e) * 0.5;
            }
            if (islg180deg) //
            {
                if (angle_sum > 180)
                {
                    angle_sum = 360 - angle_sum;
                    center_dirdction = p_ang_invert(center_dirdction);
                    if (angle_e >= angle_s)
                        islg180deg = !(angle_e >= angle_s);
                    else
                        islg180deg = (angle_e >= angle_s);
                }
                else
                {
                    //islg180deg = (angle_e >= angle_s); //例1  PS 30 PE 330 true   例2  PS 80 PE 30 false
                    if (angle_e >= angle_s)
                        islg180deg = (angle_e >= angle_s);
                    else
                        islg180deg = !(angle_e >= angle_s);
                }
            }
            else
            {
                if (center_dirdction > 360) { center_dirdction = center_dirdction - 360; }

            }
            return angle_sum;
        }
        /// <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;
            }
        }
        /// <summary>
        /// 求反方位角
        /// </summary>
        /// <param name="ang_direction"></param>
        /// <returns></returns>
        public double p_ang_invert(double ang_direction)//求反方位角
        {
            if (ang_direction >= 180)
                return ang_direction - 180;
            else
                return ang_direction + 180;
        }
        /// <summary>
        /// 求增量座標
        /// </summary>
        /// <param name="ps">起點</param>
        /// <param name="val">增量值</param>
        /// <param name="ang_direction">角度</param>
        /// <returns></returns>
        public gPoint p_val_ang(gPoint ps, double val, double ang_direction)
        {
            gPoint pe;
            pe.x = ps.x + val * Math.Cos(ang_direction * Math.PI / 180);
            pe.y = ps.y + val * Math.Sin(ang_direction * Math.PI / 180);
            return pe;
        }
View Code

   3.資料結構

    /// <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>
    /// Surface 座標泛型集類2
    /// </summary>
    public class gSur_list
    {
        public List<gSur_Point> sur_list = new List<gSur_Point>();
        /// <summary>
        /// 是否為空洞
        /// </summary>
        public bool is_hole { get; set; }
        /// <summary>
        /// 是否逆時針
        /// </summary>
        public bool is_ccw { get; set; }
    }
    /// <summary>
    /// Surface 座標泛型集類3
    /// </summary>
    public class gS
    {
        public List<gSur_list> sur_group = new List<gSur_list>();
        /// <summary>
        /// 是否為負  polarity-- P N
        /// </summary>
        public bool negative { get; set; }
        public string attribut { 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;
        }
    }
    /// <summary>
    /// ARC 資料型別
    /// </summary>
    public struct gA
    {
        public gA(double ps_x, double ps_y, double pc_x, double pc_y, double pe_x, double pe_y, double width_, bool ccw_)
        {
            this.ps = new gPoint(ps_x, ps_y);
            this.pc = new gPoint(pc_x, pc_y);
            this.pe = new gPoint(pe_x, pe_y);
            this.negative = false;
            this.ccw = ccw_;
            this.symbols = "r" + width_.ToString();
            this.attribut = string.Empty;
            this.width = width_;
        }
        public gA(gPoint ps_, gPoint pc_, gPoint pe_, double width_, bool ccw_ = false)
        {
            this.ps = ps_;
            this.pc = pc_;
            this.pe = pe_;
            this.negative = false;
            this.ccw = ccw_;
            this.symbols = "r" + width_.ToString();
            this.attribut = string.Empty;
            this.width = width_;
        }
        public gPoint ps;
        public gPoint pe;
        public gPoint pc;
        public bool negative;//polarity-- positive  negative
        public bool ccw; //direction-- cw ccw
        public string symbols;
        public string attribut;
        public double width;
        public static gA operator +(gA arc1, gPoint move_p)
        {
            arc1.ps += move_p;
            arc1.pe += move_p;
            arc1.pc += move_p;
            return arc1;
        }
        public static gA operator +(gA arc1, gPP move_p)
        {
            arc1.ps += move_p.p;
            arc1.pe += move_p.p;
            arc1.pc += move_p.p;
            return arc1;
        }
        public static gA operator +(gA arc1, gP move_p)
        {
            arc1.ps += move_p.p;
            arc1.pe += move_p.p;
            arc1.pc += move_p.p;
            return arc1;
        }
        public static gA operator -(gA arc1, gPoint move_p)
        {
            arc1.ps -= move_p;
            arc1.pe -= move_p;
            arc1.pc -= move_p;
            return arc1;
        }
        public static gA operator -(gA arc1, gPP move_p)
        {
            arc1.ps -= move_p.p;
            arc1.pe -= move_p.p;
            arc1.pc -= move_p.p;
            return arc1;
        }
        public static gA operator -(gA arc1, gP move_p)
        {
            arc1.ps -= move_p.p;
            arc1.pe -= move_p.p;
            arc1.pc -= move_p.p;
            return arc1;
        }
    }
View Code

 

三.折線多邊形擴大縮小實現效果

     

 

相關文章