箭头_Row 发表于 2024-8-8 11:48:12

關於優化多段線中重複點

本帖最后由 箭头_Row 于 2024-8-8 23:29 编辑

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

namespace ArrowTools;
public class P_PolylineTest
{
   
    public void P_Polyline()
    {
      var p1 = new Point2d(5, 0);
      var p2 = new Point2d(10, 0);
      var p3 = new Point2d(15, 0);

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

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

   
    public void P_Polyline_02()
    {
      using DBTrans tr = new();
      var ents = Env.Editor.SSGet()?.Value?.GetEntities<Polyline>();
      if (ents == null)
            return;

      foreach (var pl in ents)
      {
            Env.Printl($"顶点个数:{pl.NumberOfVertices}");
            var deleteNum = SimplifyPolylineDemo(pl);
            Env.Printl($"优化点个数:{deleteNum}");
      }
    }
   
    public void P_Polyline_03()
    {
      using DBTrans tr = new();
      var ents = Env.Editor.SSGet()?.Value?.GetEntities<Polyline>();
      if (ents == null)
            return;

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

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

#if true
            var deleteNum_Coincident = SimplifyPolyline_Coincident(pl);
            Env.Printl($"优化点个数_Coincident: {deleteNum_Coincident} ");
#endif
#if true
            var deleteNum_Line = SimplifyPolyline_Line(pl);
            Env.Printl($"优化点个数_Line: {deleteNum_Line}");
#endif
#if true
            var deleteNum_Arc = SimplifyPolyline_Arc(pl);
            Env.Printl($"优化点个数_Arc: {deleteNum_Arc}");
#endif

      }


    }

   
    public void P_Polyline_05()
    {
      using DBTrans tr = new();
      var ents = Env.Editor.SSGet()?.Value?.GetEntities<Polyline>();
      if (ents == null)
            return;

      foreach (var pl in ents)
      {
            Env.Printl($"顶点个数:{pl.NumberOfVertices}");
            for (var i = pl.NumberOfVertices - 1; i >= 0; i--)
            {
                var type = pl.GetSegmentType(i);
                if (type == SegmentType.Arc)
                  Env.Printl(pl.GetBulgeAt(i));
            }
      }
    }

   
    public void P_Polyline_07()
    {
      using DBTrans tr = new();
      var ents = Env.Editor.SSGet()?.Value?.GetEntities<Polyline>();
      if (ents == null)
            return;

      foreach (var pl in ents)
      {
            Env.Printl($"顶点个数:{pl.NumberOfVertices}");
            var deleteNum = SimplifyPolyline(pl);
            Env.Printl($"优化端点个数: {deleteNum}");
      }
    }
    /// <summary>
    /// 優化多段線中重複點:清理多余端點
    /// </summary>
    /// <param name="pl">多段線</param>
    /// <param name="tol">容差值</param>
    /// <returns>清理的端點數量</returns>
    private static int SimplifyPolyline(Polyline pl, double tol = 1e-6)
    {
      if (pl.NumberOfVertices < 3)
            return 0;

      int num = pl.NumberOfVertices;
      SimplifyPolyline_Coincident(pl);
      SimplifyPolyline_Line(pl, tol);
      SimplifyPolyline_Arc(pl, tol);

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

      int num = pl.NumberOfVertices;
      using (pl.ForWrite())
      {
            for (var i = pl.NumberOfVertices - 1; i >= 0; i--)
            {
                if (pl.NumberOfVertices < 3)
                  break;

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

      return num - pl.NumberOfVertices;
    }

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

      int num = pl.NumberOfVertices;
      using (pl.ForWrite())
      {
            var p1 = Point2d.Origin;
            var p2 = Point2d.Origin;
            var p3 = Point2d.Origin;
            double bu = default;

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

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

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

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

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

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

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

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

      int num = pl.NumberOfVertices;
      using (pl.ForWrite())
      {
            double bulge = default;
            double bulge02 = default;
            double angle = default;
            double angle02 = default;
            double radius = default;
            double radius02 = default;

            var p1 = Point2d.Origin;
            var p2 = Point2d.Origin;
            var p3 = Point2d.Origin;

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

            double b = default;
            double x = default;
            double y = default;

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

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

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

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

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

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

            }

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

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

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

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

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

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

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

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

      return num - pl.NumberOfVertices;
    }

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

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

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

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

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

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

      return num - pl.NumberOfVertices;
    }


}


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

箭头_Row 发表于 2024-8-10 01:49:16

本帖最后由 箭头_Row 于 2024-8-10 02:39 编辑

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

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

      if (pl.NumberOfVertices < 3)
            return 0;

      int num = pl.NumberOfVertices;
      using (pl.ForWrite())
      {
            var p1 = Point2d.Origin;
            var p2 = Point2d.Origin;
            var p3 = Point2d.Origin;

            SegmentType type01;
            SegmentType type02;
            double bulge = default;

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

                // 2024-8-10:BUG修復,直接判斷: i-1 點類型
                // 避免: i 點== Arc、i-1 點== Line、i -2 點== Line 時 直接跳過的情況。
                //var type01 = pl.GetSegmentType(i);
                //if (i != pl.NumberOfVertices - 1)
                //    if (type01 != SegmentType.Line)
                //      continue;
                //var type02 = pl.GetSegmentType(i - 1);
                //if (type02 != SegmentType.Line)
                //    continue;
                // 優化多段線:頂點去重,非直線跳過
                type01 = pl.GetSegmentType(i - 1);
                if (type01 != SegmentType.Line)
                  continue;
                type02 = pl.GetSegmentType(i - 2);
                if (type02 != SegmentType.Line)
                  continue;

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

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

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

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

                // 從結束點往前比對
                type01 = pl.GetSegmentType(pl.NumberOfVertices - 1);
                type02 = pl.GetSegmentType(0);
                if (type01 == SegmentType.Line && type02 == SegmentType.Line)
                {
                  p1 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);
                  p2 = pl.GetPoint2dAt(0);
                  p3 = pl.GetPoint2dAt(1);
                  bulge = p1.GetArcBulge(p2, p3, tol);
                  if (bulge == 0)
                        pl.RemoveVertexAt(index: 0); // 移除第2点
                }
            }
      }

      return num - pl.NumberOfVertices;
    }

備註:SimplifyPolyline_Arc 函數的修改邏輯同上。

wang2006zhi 发表于 2024-12-10 17:55:19

本帖最后由 wang2006zhi 于 2024-12-10 18:01 编辑

    /// <summary>
    /// 简化多段线,仅考虑直线段
    /// </summary>
    /// <param name="pl"></param>
    public static void SimpPolyline(this Polyline pl)
    {
      var i = 0;
      var st = pl.Closed;
      while (true)
      {
            var flag = st ? pl.NumberOfVertices : pl.NumberOfVertices - 1;
            if (i == flag)
                break;
            var j = (i + 1) % pl.NumberOfVertices;
            var k = (i + 2) % pl.NumberOfVertices;

            var pt0 = pl.GetPoint3dAt(i);
            var pt1 = pl.GetPoint3dAt(j);
            var pt2 = pl.GetPoint3dAt(k);
            //若点在线上,移除中间点
            if (pt1.IsOnLine(pt0, pt2) == EnumOnline.OnLine)
                pl.RemoveVertexAt(j);
            else
                i++;
      }
    }
页: [1]
查看完整版本: 關於優化多段線中重複點