明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 1852|回复: 6

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

[复制链接]
发表于 2024-9-9 19:17:44 | 显示全部楼层 |阅读模式
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.Runtime;
  4. using Autodesk.AutoCAD.Geometry;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using Autodesk.AutoCAD.EditorInput;
  8. using System;

  9. [assembly: CommandClass(typeof(MyAutoCADPlugin.MyCommands))]

  10. namespace MyAutoCADPlugin
  11. {
  12.     public class MyCommands
  13.     {
  14.         [CommandMethod("Add")]
  15.         public void AddRedFrames()
  16.         {
  17.             Document doc = Application.DocumentManager.MdiActiveDocument;
  18.             Database db = doc.Database;

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

  28.                 // 获取选择的实体
  29.                 SelectionSet selSet = selResult.Value;
  30.                 List<Entity> entities = new List<Entity>();
  31.                 foreach (SelectedObject selObj in selSet)
  32.                 {
  33.                     Entity ent = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Entity;
  34.                     if (ent != null)
  35.                     {
  36.                         entities.Add(ent);
  37.                     }
  38.                 }

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

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

  47.                 // 计算边界框
  48.                 Extents3d combinedExtents = new Extents3d();
  49.                 foreach (Entity ent in entities)
  50.                 {
  51.                     combinedExtents.AddExtents(ent.GeometricExtents);
  52.                 }

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

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

  57.                 // 添加红色外框
  58.                 foreach (var group in groupedEntities)
  59.                 {
  60.                     Extents3d groupExtents = new Extents3d();
  61.                     foreach (Entity ent in group)
  62.                     {
  63.                         groupExtents.AddExtents(ent.GeometricExtents);
  64.                     }

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

  68.                 tr.Commit();
  69.             }
  70.         }

  71.         private void AddRedFrame(Database db, Transaction tr, Extents3d extents)
  72.         {
  73.             using (BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite))
  74.             {
  75.                 Polyline poly = new Polyline();
  76.                 poly.AddVertexAt(0, new Point2d(extents.MinPoint.X, extents.MinPoint.Y), 0, 0, 0);
  77.                 poly.AddVertexAt(1, new Point2d(extents.MaxPoint.X, extents.MinPoint.Y), 0, 0, 0);
  78.                 poly.AddVertexAt(2, new Point2d(extents.MaxPoint.X, extents.MaxPoint.Y), 0, 0, 0);
  79.                 poly.AddVertexAt(3, new Point2d(extents.MinPoint.X, extents.MaxPoint.Y), 0, 0, 0);
  80.                 poly.Closed = true;

  81.                 poly.ColorIndex = 1; // Red color

  82.                 btr.AppendEntity(poly);
  83.                 tr.AddNewlyCreatedDBObject(poly, true);
  84.             }
  85.         }

  86.         private List<List<Entity>> GroupEntitiesByProximity(List<Entity> entities, double tolerance)
  87.         {
  88.             List<List<Entity>> groupedEntities = new List<List<Entity>>();
  89.             bool[] visited = new bool[entities.Count];

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

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

  96.                 while (queue.Count > 0)
  97.                 {
  98.                     Entity current = queue.Dequeue();
  99.                     if (visited[entities.IndexOf(current)]) continue;

  100.                     visited[entities.IndexOf(current)] = true;
  101.                     group.Add(current);

  102.                     foreach (Entity neighbor in entities)
  103.                     {
  104.                         if (!visited[entities.IndexOf(neighbor)] && AreEntitiesClose(current, neighbor, tolerance))
  105.                         {
  106.                             queue.Enqueue(neighbor);
  107.                         }
  108.                     }
  109.                 }

  110.                 groupedEntities.Add(group);
  111.             }

  112.             return groupedEntities;
  113.         }

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

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

  121.             return distX < tolerance && distY < tolerance;
  122.         }
  123.     }
  124. }



写了一个一键给分堆成块加框,但是处理速度太慢 ,求助大佬帮忙修改,怎么样子才能处理速度快一些,自定义容差没写进去,外框外扩%比也没加  
发表于 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>
    [CommandMethod(nameof(AddRedFrames))]
    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[entities.Count];

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

            // 入栈后弹栈,以此为基,查找最近
            queue.Enqueue(entities[j]);
            // 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[x]) continue;
                // 我贴心地帮你提取变量上去,不然此处又加时间复杂度了.
                visited[x] = true;
                group.Add(cEnt);

                // 检查其他实体是否与当前实体接近.累计O(n^4)
                foreach (Entity neighbor in entities)
                {
                    if (
//累计O(n^5)
!visited[entities.IndexOf(neighbor)]
&& 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);
    }
}
回复 支持 1 反对 0

使用道具 举报

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

真是诲人不倦
发表于 2024-9-12 08:58:54 | 显示全部楼层
提了问题都不露面呢?
发表于 2024-9-12 21:09:54 来自手机 | 显示全部楼层
你有种再说一遍 发表于 2024-9-9 20:45
你的代码风格不是很好,我修改了一下步骤.
因为论坛有问题,数组索引如果是i,需要改成j才能正确显示.
publi ...

真是诲人不倦,楷模
 楼主| 发表于 2024-9-22 14:56:52 | 显示全部楼层
你有种再说一遍 发表于 2024-9-9 20:45
你的代码风格不是很好,我修改了一下步骤.
因为论坛有问题,数组索引如果是i,需要改成j才能正确显示.

感谢大佬,提供,我试试, 最近一直在忙,没看论坛
 楼主| 发表于 2024-9-22 15:45:43 | 显示全部楼层
你有种再说一遍 发表于 2024-9-9 20:45
你的代码风格不是很好,我修改了一下步骤.
因为论坛有问题,数组索引如果是i,需要改成j才能正确显示.



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




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-11-22 19:54 , Processed in 0.171823 second(s), 25 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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