繼續銅皮多邊形的相關的演算法, 如何用程式碼實現多邊形的擴大與縮小,這部份內容準備分為四節內容來講解,
第一節,折線多邊形的擴大縮小(不包含圓弧) 此篇講第一節
第二節,帶圓弧的多邊形的擴大縮小
第三節,多邊形擴大縮小----尖角處理
第四節,多邊形擴大縮小----自相交處理
一.多邊形擴大縮小偏移演算法
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; }
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; } }
三.折線多邊形擴大縮小實現效果