soso 发表于 2024-9-9 19:17:44

写了一个一键给分堆成块加框,但是处理速度太慢

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.EditorInput;
using System;



namespace MyAutoCADPlugin
{
    public class MyCommands
    {
      
      public void AddRedFrames()
      {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // 选择所有对象
                PromptSelectionResult selResult = doc.Editor.GetSelection();
                if (selResult.Status != PromptStatus.OK)
                {
                  doc.Editor.WriteMessage("\nSelection failed.");
                  return;
                }

                // 获取选择的实体
                SelectionSet selSet = selResult.Value;
                List<Entity> entities = new List<Entity>();
                foreach (SelectedObject selObj in selSet)
                {
                  Entity ent = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Entity;
                  if (ent != null)
                  {
                        entities.Add(ent);
                  }
                }

                // 计算所有对象的边界框
                if (entities.Count == 0)
                {
                  doc.Editor.WriteMessage("\nNo entities selected.");
                  return;
                }

                // 过滤掉多行文字
                entities = entities.Where(e => !(e is MText)).ToList();

                // 计算边界框
                Extents3d combinedExtents = new Extents3d();
                foreach (Entity ent in entities)
                {
                  combinedExtents.AddExtents(ent.GeometricExtents);
                }

                // 设置容差
                double tolerance = 7.0; // 调整容差值

                // 分块
                List<List<Entity>> groupedEntities = GroupEntitiesByProximity(entities, tolerance);

                // 添加红色外框
                foreach (var group in groupedEntities)
                {
                  Extents3d groupExtents = new Extents3d();
                  foreach (Entity ent in group)
                  {
                        groupExtents.AddExtents(ent.GeometricExtents);
                  }

                  // 绘制红色外框
                  AddRedFrame(db, tr, groupExtents);
                }

                tr.Commit();
            }
      }

      private void AddRedFrame(Database db, Transaction tr, Extents3d extents)
      {
            using (BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite))
            {
                Polyline poly = new Polyline();
                poly.AddVertexAt(0, new Point2d(extents.MinPoint.X, extents.MinPoint.Y), 0, 0, 0);
                poly.AddVertexAt(1, new Point2d(extents.MaxPoint.X, extents.MinPoint.Y), 0, 0, 0);
                poly.AddVertexAt(2, new Point2d(extents.MaxPoint.X, extents.MaxPoint.Y), 0, 0, 0);
                poly.AddVertexAt(3, new Point2d(extents.MinPoint.X, extents.MaxPoint.Y), 0, 0, 0);
                poly.Closed = true;

                poly.ColorIndex = 1; // Red color

                btr.AppendEntity(poly);
                tr.AddNewlyCreatedDBObject(poly, true);
            }
      }

      private List<List<Entity>> GroupEntitiesByProximity(List<Entity> entities, double tolerance)
      {
            List<List<Entity>> groupedEntities = new List<List<Entity>>();
            bool[] visited = new bool;

            for (int i = 0; i < entities.Count; i++)
            {
                if (visited) continue;

                List<Entity> group = new List<Entity>();
                Queue<Entity> queue = new Queue<Entity>();
                queue.Enqueue(entities);

                while (queue.Count > 0)
                {
                  Entity current = queue.Dequeue();
                  if (visited) continue;

                  visited = true;
                  group.Add(current);

                  foreach (Entity neighbor in entities)
                  {
                        if (!visited && AreEntitiesClose(current, neighbor, tolerance))
                        {
                            queue.Enqueue(neighbor);
                        }
                  }
                }

                groupedEntities.Add(group);
            }

            return groupedEntities;
      }

      private bool AreEntitiesClose(Entity e1, Entity e2, double tolerance)
      {
            // 检查两个实体是否相近的逻辑
            Extents3d ext1 = e1.GeometricExtents;
            Extents3d ext2 = e2.GeometricExtents;

            double distX = Math.Abs(ext1.MinPoint.X - ext2.MinPoint.X);
            double distY = Math.Abs(ext1.MinPoint.Y - ext2.MinPoint.Y);

            return distX < tolerance && distY < tolerance;
      }
    }
}


写了一个一键给分堆成块加框,但是处理速度太慢 ,求助大佬帮忙修改,怎么样子才能处理速度快一些,自定义容差没写进去,外框外扩%比也没加:(

你有种再说一遍 发表于 2024-9-9 20:45:12

本帖最后由 你有种再说一遍 于 2024-9-13 05:04 编辑

你的代码风格不是很好,我修改了一下步骤.
因为论坛有问题,数组索引如果是i,需要改成j才能正确显示.

public class MyCommands {
    // 设置容差值
    public double Tolerance { get; set; } = 1.0;

    /// <summary>
    /// 添加红色边框到选定对象周围
    /// </summary>
   
    public void AddRedFrames() {
      // 获取当前活动的文档和数据库
      Document doc = Application.DocumentManager.MdiActiveDocument;
      Database db = doc.Database;
      // 手选
      var selResult = doc.Editor.GetSelection();
      if (selResult.Status != PromptStatus.OK) {
            doc.Editor.WriteMessage("\n选择失败。");
            return;
      }

using (Transaction tr = db.TransactionManager.StartTransaction()) {

// 图元
List<Entity> ents = new();
// 最大包围盒
Extents3d combinedExtents = new Extents3d();
// 遍历用户选择的对象
foreach (SelectedObject selObj in selResult.Value) {
Entity ent = (Entity)tr.GetObject(selObj.ObjectId, OpenMode.ForRead);
// 过滤掉多行文字对象
if (ent is MText) continue;
ents.Add(ent);
// 通过并集计算出最大包围盒
combinedExtents.AddExtents(ent.GeometricExtents);
}

// 求邻近距离分组
vat glink = GroupEntitiesByProximity(ents);

// 当前空间的块表记录
using BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);

// 为每个分组添加红色外框
foreach (var group in glink) {
    Extents3d gExt = new();
    foreach (Entity ent in group){
      gExt.AddExtents(ent.GeometricExtents);
    }
    // 绘制红色外框
    AddRedFrame(btr, tr, groupExtents);
}
tr.Commit();
}
}

下面是一个坏函数,这个函数三层主循环,时间复杂度平均期望O(n^3),还有两次IndexOf造成,达到O(n^5).
相当于你为了找最近的一个人和全世界每个人都比较,而且是每个人都比较5次.
那么为什么不先分组,然后组内比较呢?
就只和你同省/城/村/建筑内比较而已.
建议改为四叉树或者哈希网格,不要自己写了,去用IFox吧,里面有四叉树调用例子.
    /// <summary>
    /// 根据接近度将实体分组
    /// </summary>
    /// <param name="entities">实体列表</param>
    /// <returns>分组后的实体列表</returns>
    private List<List<Entity>> GroupEntitiesByProximity(List<Entity> entities) {
      // 这种变量即使需要也写类字段上面...
      List<List<Entity>> groupedEntities = new();
      bool[] visited = new bool;

      // 对每个的实体进行分组O(n)
      for (int j = 0; j < entities.Count; j++) {
            if (visited) continue; // 访问过跳过,但是依然遍历问了一次全世界O(1)
            List<Entity> group = new();
            Queue<Entity> queue = new();

            // 入栈后弹栈,以此为基,查找最近
            queue.Enqueue(entities);
            // O(n)累计O(n^2)
            while (queue.Count > 0) {
                Entity cEnt = queue.Dequeue();
                // 你觉得这个indexof不是遍历吗?它又没有hash,又没有排序后二分,它当然是遍历啦.累计O(n^3)
                var x = entities.IndexOf(cEnt);
                if (visited) continue;
                // 我贴心地帮你提取变量上去,不然此处又加时间复杂度了.
                visited = true;
                group.Add(cEnt);

                // 检查其他实体是否与当前实体接近.累计O(n^4)
                foreach (Entity neighbor in entities)
                {
                  if (
//累计O(n^5)
!visited
&& AreEntitiesClose(cEnt, neighbor))
                  {//出栈
                        queue.Enqueue(neighbor);
                  }
                }
            }
            groupedEntities.Add(group);
      }
      return groupedEntities;
    }

    /// <summary>
    /// 检查两个实体是否在指定的容差范围内相近
    /// </summary>
    /// <param name="e1">第一个实体</param>
    /// <param name="e2">第二个实体</param>
    /// <returns>是否相近</returns>
    private bool AreEntitiesClose(Entity e1, Entity e2)
    {
      Extents3d ext1 = e1.GeometricExtents;
      Extents3d ext2 = e2.GeometricExtents;
      double distX = Math.Abs(ext1.MinPoint.X - ext2.MinPoint.X);
      double distY = Math.Abs(ext1.MinPoint.Y - ext2.MinPoint.Y);
      return distX < Tolerance && distY < Tolerance;
    }

    /// <summary>
    /// 创建一个多段线对象来表示红色外框
    /// </summary>
    /// <param name="btr">当前空间的BlockTableRecord对象</param>
    /// <param name="tr">当前事务</param>
    /// <param name="extents">实体的边界框</param>
    private void AddRedFrame(BlockTableRecord btr, Transaction tr, Extents3d extents)
    {
      Polyline poly = new Polyline();
      poly.AddVertexAt(0, new Point2d(extents.MinPoint.X, extents.MinPoint.Y), 0, 0, 0);
      poly.AddVertexAt(1, new Point2d(extents.MaxPoint.X, extents.MinPoint.Y), 0, 0, 0);
      poly.AddVertexAt(2, new Point2d(extents.MaxPoint.X, extents.MaxPoint.Y), 0, 0, 0);
      poly.AddVertexAt(3, new Point2d(extents.MinPoint.X, extents.MaxPoint.Y), 0, 0, 0);
      poly.Closed = true;
      // 设置多段线颜色为红色
      poly.ColorIndex = 1;
      // 将多段线添加到当前空间
      btr.AppendEntity(poly);
      tr.AddNewlyCreatedDBObject(poly, true);
    }
}

tiancao100 发表于 2024-9-10 12:14:08

你有种再说一遍 发表于 2024-9-9 20:45
你的代码风格不是很好,我修改了一下步骤.
因为论坛有问题,数组索引如果是i,需要改成j才能正确显示.
publi ...

真是诲人不倦

jltx123456 发表于 2024-9-12 08:58:54

提了问题都不露面呢?

伍星 发表于 2024-9-12 21:09:54

你有种再说一遍 发表于 2024-9-9 20:45
你的代码风格不是很好,我修改了一下步骤.
因为论坛有问题,数组索引如果是i,需要改成j才能正确显示.
publi ...

真是诲人不倦,楷模

soso 发表于 2024-9-22 14:56:52

你有种再说一遍 发表于 2024-9-9 20:45
你的代码风格不是很好,我修改了一下步骤.
因为论坛有问题,数组索引如果是i,需要改成j才能正确显示.



感谢大佬,提供,我试试, 最近一直在忙,没看论坛

soso 发表于 2024-9-22 15:45:43

你有种再说一遍 发表于 2024-9-9 20:45
你的代码风格不是很好,我修改了一下步骤.
因为论坛有问题,数组索引如果是i,需要改成j才能正确显示.





论坛有点卡,打开好慢
我测试了速度是优化了很多,但是 识别不精准
https://wwui.lanzouj.com/iAED72al984f
这个是项目 如果有时间的话帮我看看,不急的,感谢大佬




页: [1]
查看完整版本: 写了一个一键给分堆成块加框,但是处理速度太慢