明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 546|回复: 1

[【IFoxCAD】] 關於優化多段線中重複點

[复制链接]
发表于 2024-8-8 11:48:12 | 显示全部楼层 |阅读模式
本帖最后由 箭头_Row 于 2024-8-8 23:29 编辑

功能介紹:優化多段線中重複點
優化方向:感覺首尾那段單獨弄出來的就為了做索引號好笨的方法,想考慮用其它方法替代。

  1. namespace ArrowTools;
  2. public class P_PolylineTest
  3. {
  4.     [CommandMethod("P`")]
  5.     public void P_Polyline()
  6.     {
  7.         var p1 = new Point2d(5, 0);
  8.         var p2 = new Point2d(10, 0);
  9.         var p3 = new Point2d(15, 0);

  10.         var bu1 = PointEx.GetArcBulge(p1, p2, p3);
  11.         Env.Print(bu1);

  12.         p3 = new Point2d(0, 0);
  13.         var bu2 = p1.GetArcBulge(p2, p3);
  14.         // 備註:此處如果形參點順序不同剛輸出結果不同。
  15.         Env.Print(bu2);
  16.         Env.Print(MathEx.ConvertRadToDeg(bu2));
  17.     }

  18.     [CommandMethod("P1")]
  19.     public void P_Polyline_02()
  20.     {
  21.         using DBTrans tr = new();
  22.         var ents = Env.Editor.SSGet()?.Value?.GetEntities<Polyline>();
  23.         if (ents == null)
  24.             return;

  25.         foreach (var pl in ents)
  26.         {
  27.             Env.Printl($"顶点个数:{pl.NumberOfVertices}");
  28.             var deleteNum = SimplifyPolylineDemo(pl);
  29.             Env.Printl($"优化点个数:{deleteNum}");
  30.         }
  31.     }
  32.     [CommandMethod("P3")]
  33.     public void P_Polyline_03()
  34.     {
  35.         using DBTrans tr = new();
  36.         var ents = Env.Editor.SSGet()?.Value?.GetEntities<Polyline>();
  37.         if (ents == null)
  38.             return;

  39.         foreach (var pl in ents)
  40.         {
  41.             Env.Printl($"顶点个数:{pl.NumberOfVertices}");

  42. #if Debug
  43.             for (int i = pl.NumberOfVertices - 1; i >= 0; i--)
  44.             {
  45.                 var type = pl.GetSegmentType(i);
  46.                 Env.Printl(type.GetType().Name + ":" + type.ToString());
  47.             }
  48. #endif

  49. #if true
  50.             var deleteNum_Coincident = SimplifyPolyline_Coincident(pl);
  51.             Env.Printl($"优化点个数_Coincident: {deleteNum_Coincident} ");
  52. #endif
  53. #if true
  54.             var deleteNum_Line = SimplifyPolyline_Line(pl);
  55.             Env.Printl($"优化点个数_Line: {deleteNum_Line}");
  56. #endif
  57. #if true
  58.             var deleteNum_Arc = SimplifyPolyline_Arc(pl);
  59.             Env.Printl($"优化点个数_Arc: {deleteNum_Arc}");
  60. #endif

  61.         }


  62.     }

  63.     [CommandMethod("P5")]
  64.     public void P_Polyline_05()
  65.     {
  66.         using DBTrans tr = new();
  67.         var ents = Env.Editor.SSGet()?.Value?.GetEntities<Polyline>();
  68.         if (ents == null)
  69.             return;

  70.         foreach (var pl in ents)
  71.         {
  72.             Env.Printl($"顶点个数:{pl.NumberOfVertices}");
  73.             for (var i = pl.NumberOfVertices - 1; i >= 0; i--)
  74.             {
  75.                 var type = pl.GetSegmentType(i);
  76.                 if (type == SegmentType.Arc)
  77.                     Env.Printl(pl.GetBulgeAt(i));
  78.             }
  79.         }
  80.     }

  81.     [CommandMethod("P7")]
  82.     public void P_Polyline_07()
  83.     {
  84.         using DBTrans tr = new();
  85.         var ents = Env.Editor.SSGet()?.Value?.GetEntities<Polyline>();
  86.         if (ents == null)
  87.             return;

  88.         foreach (var pl in ents)
  89.         {
  90.             Env.Printl($"顶点个数:{pl.NumberOfVertices}");
  91.             var deleteNum = SimplifyPolyline(pl);
  92.             Env.Printl($"优化端点个数: {deleteNum}");
  93.         }
  94.     }
  95.     /// <summary>
  96.     /// 優化多段線中重複點:清理多余端點
  97.     /// </summary>
  98.     /// <param name="pl">多段線</param>
  99.     /// <param name="tol">容差值</param>
  100.     /// <returns>清理的端點數量</returns>
  101.     private static int SimplifyPolyline(Polyline pl, double tol = 1e-6)
  102.     {
  103.         if (pl.NumberOfVertices < 3)
  104.             return 0;

  105.         int num = pl.NumberOfVertices;
  106.         SimplifyPolyline_Coincident(pl);
  107.         SimplifyPolyline_Line(pl, tol);
  108.         SimplifyPolyline_Arc(pl, tol);

  109.         return num - pl.NumberOfVertices;
  110.     }
  111.     /// <summary>
  112.     /// 優化多段線中重複點:清理相同點的端點
  113.     /// </summary>
  114.     /// <param name="pl">多段線</param>
  115.     /// <returns>清理的端點數量</returns>
  116.     private static int SimplifyPolyline_Coincident(Polyline pl, double tol = 1e-6)
  117.     {
  118.         if (pl.NumberOfVertices < 3)
  119.             return 0;

  120.         int num = pl.NumberOfVertices;
  121.         using (pl.ForWrite())
  122.         {
  123.             for (var i = pl.NumberOfVertices - 1; i >= 0; i--)
  124.             {
  125.                 if (pl.NumberOfVertices < 3)
  126.                     break;

  127.                 // 優化多段線:頂點去重
  128.                 var type = pl.GetSegmentType(i);
  129.                 if (type == SegmentType.Coincident)
  130.                     pl.RemoveVertexAt(i);
  131.             }
  132.         }

  133.         return num - pl.NumberOfVertices;
  134.     }

  135.     /// <summary>
  136.     /// 優化多段線中重複點:清理共線的多余端點
  137.     /// </summary>
  138.     /// <param name="pl">多段線</param>
  139.     /// <param name="tol">容差值</param>
  140.     /// <returns>清理的端點數量</returns>
  141.     private static int SimplifyPolyline_Line(Polyline pl, double tol = 1e-6)
  142.     {
  143.         if (pl.NumberOfVertices < 3)
  144.             return 0;

  145.         int num = pl.NumberOfVertices;
  146.         using (pl.ForWrite())
  147.         {
  148.             var p1 = Point2d.Origin;
  149.             var p2 = Point2d.Origin;
  150.             var p3 = Point2d.Origin;
  151.             double bu = default;

  152.             for (var i = pl.NumberOfVertices - 1; i >= 2; i--)
  153.             {
  154.                 if (pl.NumberOfVertices < 3)
  155.                     break;

  156.                 // 優化多段線:頂點去重,非直線跳過
  157.                 var type01 = pl.GetSegmentType(i);
  158.                 if (i != pl.NumberOfVertices - 1)
  159.                     if (type01 != SegmentType.Line)
  160.                         continue;
  161.                 var type02 = pl.GetSegmentType(i - 1);
  162.                 if (type02 != SegmentType.Line)
  163.                     continue;

  164.                 // 獲取相鄰三点
  165.                 p1 = pl.GetPoint2dAt(i);
  166.                 p2 = pl.GetPoint2dAt(i - 1);
  167.                 p3 = pl.GetPoint2dAt(i - 2);

  168.                 // 優化第1點至 第2點、第3點 向量.Normal相等時
  169.                 bu = p1.GetArcBulge(p2, p3, tol);
  170.                 if (bu == 0)
  171.                     pl.RemoveVertexAt(index: i - 1); // 移除第2点
  172.             }

  173.             // 判斷首尾如果閉合的部份
  174.             if (pl.Closed && pl.NumberOfVertices > 2)
  175.             {
  176.                 // 從結束-1點往前比對
  177.                 p1 = pl.GetPoint2dAt(pl.NumberOfVertices - 2);
  178.                 p2 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);
  179.                 p3 = pl.GetPoint2dAt(0);

  180.                 bu = p1.GetArcBulge(p2, p3, tol);
  181.                 if (bu == 0)
  182.                     pl.RemoveVertexAt(index: pl.NumberOfVertices - 1); // 移除第2点

  183.                 // 從起始+1點往后比對
  184.                 p1 = pl.GetPoint2dAt(1);
  185.                 p2 = pl.GetPoint2dAt(0);
  186.                 p3 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);
  187.                 bu = p1.GetArcBulge(p2, p3, tol);
  188.                 if (bu == 0)
  189.                     pl.RemoveVertexAt(index: 0); // 移除第2点
  190.             }
  191.         }

  192.         return num - pl.NumberOfVertices;
  193.     }
  194.     /// <summary>
  195.     /// 優化多段線中重複點:清理共圓弧的多余端點
  196.     /// </summary>
  197.     /// <param name="pl">多段線</param>
  198.     /// <param name="tol">容差值</param>
  199.     /// <returns>清理的端點數量</returns>
  200.     // AutoCAD 中凸度(bulge)的概念:
  201.     // https://www.cnblogs.com/BensonLaur/p/16189673.html
  202.     private static int SimplifyPolyline_Arc(Polyline pl, double tol = 1e-6)
  203.     {
  204.         if (pl.NumberOfVertices < 3)
  205.             return 0;

  206.         int num = pl.NumberOfVertices;
  207.         using (pl.ForWrite())
  208.         {
  209.             double bulge = default;
  210.             double bulge02 = default;
  211.             double angle = default;
  212.             double angle02 = default;
  213.             double radius = default;
  214.             double radius02 = default;

  215.             var p1 = Point2d.Origin;
  216.             var p2 = Point2d.Origin;
  217.             var p3 = Point2d.Origin;

  218.             var center = Point2d.Origin;
  219.             var center02 = Point2d.Origin;

  220.             double b = default;
  221.             double x = default;
  222.             double y = default;

  223.             for (var i = pl.NumberOfVertices - 1; i >= 2; i--)
  224.             {
  225.                 if (pl.NumberOfVertices < 3)
  226.                     break;

  227.                 // 優化多段線:頂點去重,非Arc跳過
  228.                 var type01 = pl.GetSegmentType(i);
  229.                 if (i != pl.NumberOfVertices - 1)
  230.                     if (type01 != SegmentType.Arc)
  231.                         continue;
  232.                 var type02 = pl.GetSegmentType(i - 1);
  233.                 if (type02 != SegmentType.Arc)
  234.                     continue;

  235.                 // 獲取相鄰三点
  236.                 p1 = pl.GetPoint2dAt(i);
  237.                 p2 = pl.GetPoint2dAt(i - 1);
  238.                 p3 = pl.GetPoint2dAt(i - 2);

  239.                 bulge = pl.GetBulgeAt(i - 1);
  240.                 angle = 4 * Math.Atan(bulge);  // 計算出角度值  
  241.                 radius = (p1.GetDistanceTo(p2) / 2) / Math.Sin(angle / 2);  // 計算出半徑值
  242.                 b = (1 / bulge - bulge) / 2;
  243.                 x = 0.5 * ((p1.X + p2.X) - b * (p1.Y - p2.Y));
  244.                 y = 0.5 * ((p1.Y + p2.Y) + b * (p1.X - p2.X));
  245.                 center = new Point2d(x, y);

  246.                 bulge02 = pl.GetBulgeAt(i - 2);
  247.                 angle02 = 4 * Math.Atan(bulge02);  // 計算出角度值
  248.                 radius02 = (p2.GetDistanceTo(p3) / 2) / Math.Sin(angle02 / 2);  // 計算出半徑值
  249.                 b = (1 / bulge02 - bulge02) / 2;
  250.                 x = 0.5 * ((p2.X + p3.X) - b * (p2.Y - p3.Y));
  251.                 y = 0.5 * ((p2.Y + p3.Y) + b * (p2.X - p3.X));
  252.                 center02 = new Point2d(x, y);

  253. #if Debug
  254.                 Env.Printl($"before 半徑01 {radius} ,{bulge} ,{center}");
  255.                 Env.Printl($"before 半徑02 {radius02} ,{bulge02} ,{center02}");
  256. #endif
  257.                 // 比對半徑及圓心座標值
  258.                 if (Math.Abs(radius - radius02) < tol && center.GetDistanceTo(center02) < tol)
  259.                 {
  260.                     pl.RemoveVertexAt(index: i - 1); // 移除第2点
  261.                     angle += angle02;  // 計算出角度值
  262.                     bulge = Math.Tan(angle / 4);
  263.                     pl.SetBulgeAt(i - 2, bulge);
  264. #if Debug
  265.                     Env.Printl($"after 半徑{radius} ,{bulge}");
  266. #endif
  267.                 }

  268.             }

  269.             // 判斷首尾如果閉合的部份
  270.             if (pl.Closed && pl.NumberOfVertices > 2)
  271.             {
  272.                 // 從結束-1點往前比對
  273.                 p1 = pl.GetPoint2dAt(pl.NumberOfVertices - 2);
  274.                 p2 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);
  275.                 p3 = pl.GetPoint2dAt(0);

  276.                 bulge = pl.GetBulgeAt(pl.NumberOfVertices - 1);
  277.                 angle = 4 * Math.Atan(bulge);  // 計算出角度值  
  278.                 radius = (p1.GetDistanceTo(p2) / 2) / Math.Sin(angle / 2);  // 計算出半徑值
  279.                 b = (1 / bulge - bulge) / 2;
  280.                 x = 0.5 * ((p1.X + p2.X) - b * (p1.Y - p2.Y));
  281.                 y = 0.5 * ((p1.Y + p2.Y) + b * (p1.X - p2.X));
  282.                 center = new Point2d(x, y);

  283.                 bulge02 = pl.GetBulgeAt(0);
  284.                 angle02 = 4 * Math.Atan(bulge02);  // 計算出角度值
  285.                 radius02 = (p2.GetDistanceTo(p3) / 2) / Math.Sin(angle02 / 2);  // 計算出半徑值
  286.                 b = (1 / bulge02 - bulge02) / 2;
  287.                 x = 0.5 * ((p2.X + p3.X) - b * (p2.Y - p3.Y));
  288.                 y = 0.5 * ((p2.Y + p3.Y) + b * (p2.X - p3.X));
  289.                 center02 = new Point2d(x, y);

  290.                 // 比對半徑及圓心座標值
  291.                 if (Math.Abs(radius - radius02) < tol && center.GetDistanceTo(center02) < tol)
  292.                 {
  293.                     pl.RemoveVertexAt(pl.NumberOfVertices - 1); // 移除第2点
  294.                     angle += angle02;  // 計算出角度值
  295.                     bulge = Math.Tan(angle / 4);
  296.                     pl.SetBulgeAt(0, bulge);
  297. #if Debug
  298.                     Env.Printl($"after 半徑{radius} ,{bulge}");
  299. #endif
  300.                 }

  301.                 // 從起始+1點往后比對
  302.                 p1 = pl.GetPoint2dAt(1);
  303.                 p2 = pl.GetPoint2dAt(0);
  304.                 p3 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);

  305.                 bulge = pl.GetBulgeAt(0);
  306.                 angle = 4 * Math.Atan(bulge);  // 計算出角度值  
  307.                 radius = (p1.GetDistanceTo(p2) / 2) / Math.Sin(angle / 2);  // 計算出半徑值
  308.                 b = (1 / bulge - bulge) / 2;
  309.                 x = 0.5 * ((p1.X + p2.X) - b * (p1.Y - p2.Y));
  310.                 y = 0.5 * ((p1.Y + p2.Y) + b * (p1.X - p2.X));
  311.                 center = new Point2d(x, y);

  312.                 bulge02 = pl.GetBulgeAt(pl.NumberOfVertices - 1);
  313.                 angle02 = 4 * Math.Atan(bulge02);  // 計算出角度值
  314.                 radius02 = (p2.GetDistanceTo(p3) / 2) / Math.Sin(angle02 / 2);  // 計算出半徑值
  315.                 b = (1 / bulge02 - bulge02) / 2;
  316.                 x = 0.5 * ((p2.X + p3.X) - b * (p2.Y - p3.Y));
  317.                 y = 0.5 * ((p2.Y + p3.Y) + b * (p2.X - p3.X));
  318.                 center02 = new Point2d(x, y);

  319.                 // 比對半徑及圓心座標值
  320.                 if (Math.Abs(radius - radius02) < tol && center.GetDistanceTo(center02) < tol)
  321.                 {
  322.                     pl.RemoveVertexAt(0); // 移除第2点
  323.                     angle += angle02;  // 計算出角度值
  324.                     bulge = Math.Tan(angle / 4);
  325.                     pl.SetBulgeAt(pl.NumberOfVertices - 1, bulge);
  326. #if Debug
  327.                     Env.Printl($"after 半徑{radius} ,{bulge}");
  328. #endif
  329.                 }
  330.             }
  331.         }

  332.         return num - pl.NumberOfVertices;
  333.     }

  334.     private static int SimplifyPolylineDemo(Polyline pl, double tol = 1e-6)
  335.     {
  336.         int num = pl.NumberOfVertices;
  337.         using (pl.ForWrite())
  338.         {
  339.             if (pl.NumberOfVertices < 3)
  340.                 return 0;

  341.             // 優化多段線相鄰兩點共點時
  342.             for (var i = 0; i < pl.NumberOfVertices; i++)
  343.             {
  344.                 var type = pl.GetSegmentType(i);
  345.                 if (type == SegmentType.Coincident)
  346.                     pl.RemoveVertexAt(i);
  347.             }

  348.             for (var i = pl.NumberOfVertices - 1; i >= 2; i--)
  349.             {
  350.                 var type = pl.GetSegmentType(i);
  351.                 Env.Printl(type.GetType().Name + "   " + type.ToString());
  352.                 // 優化多段線相鄰兩點共點時
  353.                 if (type == SegmentType.Coincident)
  354.                 {
  355.                     pl.RemoveVertexAt(i); // 移除第1点
  356.                     continue;
  357.                 }

  358.                 // 跳过非线段部分
  359.                 if (type != SegmentType.Line)
  360.                     continue;

  361.                 var type2 = pl.GetSegmentType(index: i - 1);
  362.                 Env.Printl(type2.GetType().Name + "   " + type2.ToString());
  363.                 if (type2 != SegmentType.Line)
  364.                     continue;
  365.                 // 獲取相鄰三点
  366.                 var p1 = pl.GetPoint2dAt(i);
  367.                 var p2 = pl.GetPoint2dAt(i - 1);
  368.                 var p3 = pl.GetPoint2dAt(i - 2);

  369.                 // 優化第1點至 第2點、第3點 向量.Normal相等時
  370.                 var bu = p1.GetArcBulge(p2, p3, tol);
  371.                 if (bu != 0)
  372.                     continue; //不共线跳过
  373.                 pl.SetPointAt(index: i - 1, p3); // 第2点移到第3点位置
  374.                 pl.RemoveVertexAt(index: i - 2); // 移除第3点
  375.             }
  376.         }

  377.         return num - pl.NumberOfVertices;
  378.     }


  379. }


備註: 感謝 小叶 Moy 的Demo!!!!

 楼主| 发表于 2024-8-10 01:49:16 | 显示全部楼层
本帖最后由 箭头_Row 于 2024-8-10 02:39 编辑

2024-8-10:BUG修復,直接判斷: i-1 點類型

  1.     /// <summary>
  2.     /// 優化多段線中重複點:清理共線的多余端點
  3.     /// 2024-8-10:BUG修復,直接判斷: i-1 點類型
  4.     /// </summary>
  5.     /// <param name="pl">多段線</param>
  6.     /// <param name="tol">容差值</param>
  7.     /// <returns>清理的端點數量</returns>
  8.     public static int SimplifyPolyline_Line(Polyline pl, double tol = 1e-6)
  9.     {

  10.         if (pl.NumberOfVertices < 3)
  11.             return 0;

  12.         int num = pl.NumberOfVertices;
  13.         using (pl.ForWrite())
  14.         {
  15.             var p1 = Point2d.Origin;
  16.             var p2 = Point2d.Origin;
  17.             var p3 = Point2d.Origin;

  18.             SegmentType type01;
  19.             SegmentType type02;
  20.             double bulge = default;

  21.             for (var i = pl.NumberOfVertices - 1; i >= 2; i--)
  22.             {
  23.                 if (pl.NumberOfVertices < 3)
  24.                     break;

  25.                 // 2024-8-10:BUG修復,直接判斷: i-1 點類型
  26.                 // 避免: i 點== Arc、i-1 點== Line、i -2 點== Line 時 直接跳過的情況。
  27.                 //var type01 = pl.GetSegmentType(i);
  28.                 //if (i != pl.NumberOfVertices - 1)
  29.                 //    if (type01 != SegmentType.Line)
  30.                 //        continue;
  31.                 //var type02 = pl.GetSegmentType(i - 1);
  32.                 //if (type02 != SegmentType.Line)
  33.                 //    continue;
  34.                 // 優化多段線:頂點去重,非直線跳過
  35.                 type01 = pl.GetSegmentType(i - 1);
  36.                 if (type01 != SegmentType.Line)
  37.                     continue;
  38.                 type02 = pl.GetSegmentType(i - 2);
  39.                 if (type02 != SegmentType.Line)
  40.                     continue;

  41.                 // 獲取相鄰三点
  42.                 p1 = pl.GetPoint2dAt(i);
  43.                 p2 = pl.GetPoint2dAt(i - 1);
  44.                 p3 = pl.GetPoint2dAt(i - 2);

  45.                 // 優化第1點至 第2點、第3點 向量.Normal相等時
  46.                 bulge = p1.GetArcBulge(p2, p3, tol);
  47.                 if (bulge == 0)
  48.                     pl.RemoveVertexAt(index: i - 1); // 移除第2点
  49.             }

  50.             // 判斷首尾如果閉合的部份
  51.             if (pl.Closed && pl.NumberOfVertices > 2)
  52.             {
  53.                 // 從結束-1點往前比對
  54.                 type01 = pl.GetSegmentType(pl.NumberOfVertices - 2);
  55.                 type02 = pl.GetSegmentType(pl.NumberOfVertices - 1);
  56.                 if (type01 == SegmentType.Line && type02 == SegmentType.Line)
  57.                 {
  58.                     p1 = pl.GetPoint2dAt(pl.NumberOfVertices - 2);
  59.                     p2 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);
  60.                     p3 = pl.GetPoint2dAt(0);

  61.                     bulge = p1.GetArcBulge(p2, p3, tol);
  62.                     if (bulge == 0)
  63.                         pl.RemoveVertexAt(index: pl.NumberOfVertices - 1); // 移除第2点
  64.                 }

  65.                 // 從結束點往前比對
  66.                 type01 = pl.GetSegmentType(pl.NumberOfVertices - 1);
  67.                 type02 = pl.GetSegmentType(0);
  68.                 if (type01 == SegmentType.Line && type02 == SegmentType.Line)
  69.                 {
  70.                     p1 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);
  71.                     p2 = pl.GetPoint2dAt(0);
  72.                     p3 = pl.GetPoint2dAt(1);
  73.                     bulge = p1.GetArcBulge(p2, p3, tol);
  74.                     if (bulge == 0)
  75.                         pl.RemoveVertexAt(index: 0); // 移除第2点
  76.                 }
  77.             }
  78.         }

  79.         return num - pl.NumberOfVertices;
  80.     }


備註:SimplifyPolyline_Arc 函數的修改邏輯同上。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-11-25 06:47 , Processed in 0.159053 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表