明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 1377|回复: 12

[【IFoxCAD】] 查找相同的种子集-四叉树示例

[复制链接]
发表于 2024-8-19 11:57:50 | 显示全部楼层 |阅读模式
本帖最后由 wang2006zhi 于 2025-2-14 12:01 编辑
  1. [CommandMethod("W_TKPL")]
  2. public static void BlockSame()
  3. {
  4.     using var tr = new DBTrans();

  5.     if (!Env.Editor.GetSelRect(out Rect selRec))
  6.         return;
  7.     //过滤块实体
  8.     var fil = OpFilter.Build(e => e.Dxf(0) != "INSERT");
  9.     if (!Env.Editor.SelIds(out List<ObjectId> ids, selRec.GetPointCollection(), fil))
  10.         return;
  11.     //选取种子集
  12.     if (!Env.Editor.GetEnts(out List<Entity> entsTemp, msg: "请选择要基准集:", fil: fil))
  13.         return;

  14.     //种子集四叉树数据
  15.     var oneGrop = new List<CadEntity>();
  16.     for (int i = entsTemp.Count - 1; i >= 0; i--)
  17.     {
  18.         var ent = entsTemp;
  19.         var cadEnt = new CadEntity(ent.ObjectId, ent.GetRect());
  20.         oneGrop.Add(cadEnt);
  21.     }
  22.     // 种子集外包
  23.     var rectStart = oneGrop.GetCesRect();
  24.     // 参考种子
  25.     var ceSeed = oneGrop[0];
  26.     // 种子集相同的类似集
  27.     var oneSameGrop = new List<List<CadEntity>> { oneGrop };

  28.     //创建一个CAD进度条
  29.     using var pm = new ProgressMeter();
  30.     //添加提示语言
  31.     pm.Start($"正在构建{ids.Count}条四叉树数据...");
  32.     //设置总进度
  33.     pm.SetLimit(ids.Count);
  34.     //构造四叉树数据
  35.     var treeRoot = new QuadTree<CadEntity>(AppX.InfoRect);
  36.     //参考种子集
  37.     var cesSeed = new List<CadEntity>();

  38.     for (int i = ids.Count - 1; i >= 0; i--)
  39.     {
  40.         pm.MeterProgress();
  41.         var id = ids;
  42.         var ent = id.GetObject<Entity>();
  43.         if (ent==null)
  44.             continue;
  45.         var cadEnt = new CadEntity(id, ent.GetRect())
  46.         {
  47.             IsUsed = false,
  48.             IsSeed = false
  49.         };
  50.         if (ceSeed.IsSameEnt(cadEnt))
  51.         {
  52.             cadEnt.IsSeed = true;
  53.             cesSeed.Add(cadEnt);
  54.         }
  55.         treeRoot.Insert(cadEnt);
  56.     }
  57.     pm.Stop();
  58.    
  59.     // #region 原始方案
  60.     // //查找相同种子集
  61.     // treeRoot.ForEach(nodes =>
  62.     // {
  63.     //     foreach (var ceTemp in nodes.Contents)
  64.     //     {
  65.     //         if (!ceTemp.IsGetRect(out Rect recTemp,ceSeed,rectStart))
  66.     //             continue;
  67.     //         // 用相对外包搜索实体
  68.     //         var oneGropTemp = treeRoot.Query(recTemp.Expand(10), QuadTreeSelectMode.Contains);
  69.     //         if (!oneGropTemp.Any())
  70.     //             continue;
  71.     //         //过滤掉与种子集不同的实体
  72.     //         var cesSearchTemp = new List<CadEntity>();
  73.     //         foreach (var ceOld in oneGrop)
  74.     //         {
  75.     //             foreach (var ceNew in oneGropTemp)
  76.     //             {
  77.     //                 if (!ceNew.IsUsed && ceNew.IsSameEnt(ceOld))
  78.     //                     cesSearchTemp.Add(ceNew);
  79.     //             }
  80.     //         }
  81.     //
  82.     //         // 与种子集数目相同时看为一组
  83.     //         if (cesSearchTemp.Count != oneGrop.Count)
  84.     //             continue;
  85.     //         var oneSame = new List<CadEntity>();
  86.     //         foreach (var ce in cesSearchTemp)
  87.     //         {
  88.     //             oneSame.Add(ce);
  89.     //             ce.IsUsed = true;
  90.     //         }
  91.     //         oneSameGrop.Add(oneSame);
  92.     //     }
  93.     //     return
  94.     //         false;
  95.     // });
  96.     // #endregion

  97.     #region 新方案
  98.    
  99.     //添加提示语言
  100.     pm.Start($"正在查找{cesSeed.Count}条可能相同的数据...");
  101.     //设置总进度
  102.     pm.SetLimit(cesSeed.Count);
  103.     //根据种子ceSeed查找相同种子集
  104.     for (int i = cesSeed.Count - 1; i >= 0; i--)
  105.     {
  106.         pm.MeterProgress();
  107.         var ceTemp = cesSeed;
  108.         if (!ceTemp.IsGetRect(out Rect recTemp, ceSeed, rectStart))
  109.             continue;
  110.         // 用相对外包搜索实体
  111.         var oneGropTemp = treeRoot.Query(recTemp.Expand(10), QuadTreeSelectMode.Contains);
  112.         if (!oneGropTemp.Any())
  113.             continue;
  114.         //过滤掉与种子集不同的实体
  115.         //与种子集可能相同的集
  116.         var cesSearchTemp = new List<CadEntity>();
  117.         for (int j = oneGrop.Count - 1; j >= 0; j--)
  118.         {
  119.             var ceOld = oneGrop[j];
  120.             for (int k = oneGropTemp.Count - 1; k >= 0; k--)
  121.             {
  122.                 var ceNew = oneGropTemp[k];
  123.                 if (!ceNew.IsUsed && ceNew.IsSameEnt(ceOld))
  124.                     cesSearchTemp.Add(ceNew);
  125.             }
  126.         }
  127.         
  128.         // 与种子集数目相同时看为一组
  129.         if (cesSearchTemp.Count != oneGrop.Count)
  130.             continue;
  131.         var oneSame = new List<CadEntity>();
  132.         foreach (var ce in cesSearchTemp)
  133.         {
  134.             oneSame.Add(ce);
  135.             ce.IsUsed = true;
  136.         }
  137.         oneSameGrop.Add(oneSame);
  138.     }
  139.     pm.Stop();

  140.     #endregion
  141.    

  142.     //  种子集块
  143.     var blkName = "HesiBlk_" + DateTime.Now.ToString("yyyyMMdd_HHmmss");
  144.     var btrId = entsTemp.MakeBlock(blkName);
  145.     //插入块并删除相似集
  146.     //添加提示语言
  147.     pm.Start($"正在插入{oneSameGrop.Count}个图块...");
  148.     //设置总进度
  149.     pm.SetLimit(oneSameGrop.Count);
  150.     oneSameGrop.ForEach(oneSameEs =>
  151.     {
  152.         pm.MeterProgress();
  153.         var pt = GetCesRect(oneSameEs).CenterPoint.Point3d();
  154.         var bId = tr.CurrentSpace.InsertBlock(pt.Ucs2Wcs(), btrId);
  155.         if (bId.GetObject<Entity>() is not BlockReference brf)
  156.             return;
  157.         brf.Layer = "0";
  158.         brf.Draw();
  159.         oneSameEs.ForEach(x => x.ObjectId.GetObject<Entity>()?.ForWrite(e => e.Erase(true)));
  160.     });
  161.     pm.Stop();
  162.    
  163.     Env.Editor.WriteMessage($"\n共插入{oneSameGrop.Count}个图块!");
  164. }

  165. private class CadEntity(ObjectId objectId, Rect box) : QuadEntity(box)
  166. {
  167.     /// <summary>
  168.     /// ObjectId
  169.     /// </summary>
  170.     public readonly ObjectId ObjectId = objectId;

  171.     /// <summary>
  172.     /// 是否已经用过
  173.     /// </summary>
  174.     public bool IsUsed;

  175.     /// <summary>
  176.     /// 是否种子
  177.     /// </summary>
  178.     public bool IsSeed;

  179.     /// <summary>
  180.     /// 大小比对
  181.     /// </summary>
  182.     /// <param name="other"></param>
  183.     /// <returns></returns>
  184.     public int CompareTo(CadEntity? other)
  185.     {
  186.         if (other == null)
  187.             return -1;
  188.         return GetHashCode() ^ other.GetHashCode();
  189.     }

  190.     public override int GetHashCode()
  191.     {
  192.         return (base.GetHashCode(), objectId.GetHashCode()).GetHashCode();
  193.     }

  194.     /// <summary>
  195.     /// 判断外包大小是否相等
  196.     /// </summary>
  197.     /// <param name="other"></param>
  198.     /// <returns></returns>
  199.     private bool IsSameSize(CadEntity other)
  200.     {
  201.         if (ObjectId.Equals(other.ObjectId))
  202.             return false;
  203.         if (Width.IsEqual(other.Width) && Height.IsEqual(other.Height))
  204.             return true;
  205.         return false;
  206.     }

  207.     /// <summary>
  208.     /// 判断两实体是否相同
  209.     /// </summary>
  210.     /// <param name="other"></param>
  211.     /// <returns></returns>
  212.     public bool IsSameEnt(CadEntity other)
  213.     {
  214.         if (!IsSameSize(other))
  215.             return false;
  216.         var entL = ObjectId.GetObject<Entity>();
  217.         var entR = other.ObjectId.GetObject<Entity>();
  218.         if (entR != null && entL != null && entL.IsSameEnt(entR))
  219.             return true;
  220.         return false;
  221.     }
  222. }
  223. /// <summary>
  224. /// 获取集合的边界
  225. /// </summary>
  226. /// <param name="ces">四叉树数据集</param>
  227. /// <returns></returns>
  228. private static Rect GetCesRect(this List<CadEntity> ces)
  229. {
  230.     var xMin = ces.Min(x => x.X);
  231.     var yMin = ces.Min(x => x.Y);
  232.     var tMax = ces.Max(x => x.Top);
  233.     var rMax = ces.Max(x => x.Right);
  234.     return new Rect(xMin, yMin, rMax, tMax);
  235. }


  236. /// <summary>
  237. /// 根据nRect中的某一个种子nCe是否成功获取到新的相对Rect
  238. /// </summary>
  239. /// <param name="nCe">种子</param>
  240. /// <param name="nRect">新得的Rect</param>
  241. /// <param name="oCe">原始种子</param>
  242. /// <param name="oRect">原始Rect</param>
  243. /// <returns></returns>
  244. private static bool IsGetRect(this CadEntity nCe, out Rect nRect, CadEntity oCe, Rect oRect)
  245. {
  246.     nRect = oRect;
  247.     if (!nCe.IsSeed)
  248.         return false;
  249.     if (nCe.IsUsed)
  250.         return false;
  251.     var vec = nCe.CenterPoint - oCe.CenterPoint;
  252.     nRect = new Rect(oRect.MinPoint + vec, oRect.MaxPoint + vec);
  253.     return true;
  254. }

本帖子中包含更多资源

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

x
 楼主| 发表于 2024-8-28 12:24:47 | 显示全部楼层
采用线程查找计算相同样本集合,,与插块(费时较多)

[CommandMethod("W_TKPL")]
    public static void BlockSame()
    {
        using var tr = new DBTrans();

        if (!Env.Editor.GetSelRect(out Rect selRec))
            return;
        //过滤块实体
        var fil = OpFilter.Build(e => e.Dxf(0) != "INSERT");
        if (!Env.Editor.SelEnts(out List<Entity> ents, selRec.GetPointCollection(), fil))
            return;
        //选取种子集
        if (!Env.Editor.GetEnts(out List<Entity> entsTemp, msg: "请选择要基准集:", fil: fil))
            return;
        // 种子
        var ceSeed = new CadEntity(entsTemp[0].ObjectId, entsTemp[0].GetRect());
        // 样本集
        var oneGrop = new List<CadEntity>();
        foreach (var ent in entsTemp)
        {
            var cadEnt = new CadEntity(ent.ObjectId, ent.GetRect());
            cadEnt.IsUsed = true;
            oneGrop.Add(cadEnt);
        }
        
        //构造种子集
        var cesSeed = new List<CadEntity>();
        //构造四叉树
        var treeRoot = new QuadTree<CadEntity>(AppX.InfoRect);
        //获取四叉树数据及种子集
        for (int i = ents.Count - 1; i >= 0; i--)
        {
            var ent = ents;
            var cadEnt = new CadEntity(ent.ObjectId, ent.GetRect())
            {
                IsUsed = false,
            };
            if (cadEnt.IsSameEnt(ceSeed))
                cesSeed.Add(cadEnt);
            treeRoot.Insert(cadEnt);
        }
        
        //线程任务查找相同种子集合
        Task<List<List<CadEntity>>> runTask = Task.Run(() => cesSeed.FindGroup(ceSeed,treeRoot,oneGrop));
        var result = runTask.Result;
        InsertBlocks(result);
        Env.Editor.WriteMessage($"\n 已完成{result.Count}个块添加");
        
        void InsertBlocks(List<List<CadEntity>> list)
        {
            // 样本集块
            var btrId = entsTemp.MakeBlock("HesiBlk_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"));
            var max = list.Count;
            //创建一个CAD进度条
            var pm = new ProgressMeter();
            //添加提示语言
            pm.Start($"正在插入{max}图块...");
            //设置总进度
            pm.SetLimit(max);
            for (int i = max - 1; i >= 0; i--)
            {
                pm.MeterProgress();
                var oneSameEs = list;
                if (oneSameEs == null || !oneSameEs.Any())
                    return;
                var pt = GetCesRect(oneSameEs).CenterPoint.Point3d();
                var bId = DBTrans.GetTop(btrId.Database).CurrentSpace.InsertBlock(pt, btrId);
                if (bId.GetObject<BlockReference>() is not { } brf)
                    return;
                brf.Layer = "0";
                brf.Draw();
                oneSameEs.ForEach(x => x.ObjectId.GetObject<Entity>()?.ForWrite(e => e.Erase(true)));
            }
            pm.Stop();
        }
    }
   
    /// <summary>
    /// 查找相同样本
    /// </summary>
    /// <param name="cesTemp">种子集</param>
    /// <param name="ceSeed">参考种子</param>
    /// <param name="treeRoot">四叉树数据</param>
    /// <param name="oneGrop">样本集</param>
    /// <returns></returns>
    static List<List<CadEntity>> FindGroup(this List<CadEntity> cesTemp,
        CadEntity ceSeed,
        QuadTree<CadEntity> treeRoot,
        List<CadEntity> oneGrop)
    {
       //样本集一起外包。。相比与每个实体两点构造两个外包搜索快
        var oRect = oneGrop.GetCesRect();
        // 样本集相同的集
        var oneSameGropTemp = new List<List<CadEntity>>(){oneGrop};
        //遍历所有种子集合,不可避免的多重循环,小循环写在最外层相比与内部节约时间
        for (int i = cesTemp.Count - 1; i >= 0; i--)
        {
            //临时种子与种子相对关系
            var vec = cesTemp.CenterPoint - ceSeed.CenterPoint;
            var nRect = new Rect(oRect.MinPoint + vec, oRect.MaxPoint + vec);
            // 用相对外包搜索实体
            var cesSearchTemp = treeRoot.Query(nRect.Expand(2), QuadTreeSelectMode.Contains);
            if (!cesSearchTemp.Any())
                continue;
            //临时样本集
            var oneGropTemp = new List<CadEntity>();
            //过滤掉与样本集不同的实体
            //遍历样本集
            for (int k = oneGrop.Count - 1; k >= 0; k--)
            {
                var ceK = oneGrop[k];
                //遍历搜索集中的可能实体
                for (int j = cesSearchTemp.Count - 1; j >= 0; j--)
                {
                    var ceJ = cesSearchTemp[j];
                    if (!ceJ.IsUsed && ceJ.IsSameEnt(ceK))
                    {
                        oneGropTemp.Add(ceJ);
                        ceJ.IsUsed = true;
                    }
                }
            }
            // 与种子集数目相同时看为一组
            if (oneGropTemp.Count != oneGrop.Count)
                continue;
            oneSameGropTemp.Add(oneGropTemp);
        }
        return oneSameGropTemp;
    }
   
    /// <summary>
    /// 获取集合的边界
    /// </summary>
    /// <param name="ces">四叉树数据集</param>
    /// <returns></returns>
    private static Rect GetCesRect(this List<CadEntity> ces)
    {
        var xMin = ces.Min(x => x.X);
        var yMin = ces.Min(x => x.Y);
        var tMax = ces.Max(x => x.Top);
        var rMax = ces.Max(x => x.Right);
        return new Rect(xMin, yMin, rMax, tMax);
    }
发表于 2024-8-20 18:12:32 | 显示全部楼层
dcl1214 发表于 2024-8-20 10:18
Lisp没法多线程,但是,lisp可以借助其他语言多线程,lisp将整个dwg上传上去,服务器端启动多线程,比如说1 ...

多线程要注意很多东西的,
基本需要推翻原本的单线程方案的众多想法,
着重注意锁粒度/cache miss/偷懒线程等等,
如果是发送文件这种多线程还在IO密集,
而图形学上面的处理才是CPU密集计算,
可谓是性能的两道线,
还是换到c#/c++干吧,不然还是缺少适当的训练.
别的不说,难道处理十万图元不想跑到毫秒级吗?
发表于 2024-8-20 10:18:05 | 显示全部楼层
本帖最后由 dcl1214 于 2024-8-20 10:23 编辑

Lisp没法多线程,但是,lisp可以借助其他语言多线程,lisp将整个dwg上传上去,服务器端启动多线程,比如说100个人同时上传dwg,服务器端每个dwg启动10个线程,瞬间就是200个线程跑,负责将100个用户的dwg分析并返回给lisp,我们经常做类似的开发,我们客户都是集团的,下属很多分公司同时请求服务器,lisp只是负责发送dxf或者是数据,或者是dwg,都可以,具体看如何开发了

软件的注册授权,也顺带就做好了,非法用户,不返回数据给lisp
发表于 2024-8-19 12:59:59 | 显示全部楼层
本帖最后由 dcl1214 于 2024-8-19 13:02 编辑

直线端点在相同点上,根据这个直接分组即可

几年前随手写的代码 相同项分组 - AutoLISP/Visual LISP 编程技术 - AutoCAD论坛 - 明经CAD社区 - Powered by Discuz! (mjtd.com)


如果直线端点处有误差,构建数据的时候加容差即可,这个代码是随便写的,应该还能提速300%甚至更高
发表于 2024-8-19 13:20:05 | 显示全部楼层
能不能改改 代码的背景色,这是编译器,弄个深色背景啥都看不清楚
 楼主| 发表于 2024-8-19 15:36:08 来自手机 | 显示全部楼层
本帖最后由 wang2006zhi 于 2024-8-19 21:01 编辑
dcl1214 发表于 2024-8-19 12:59
直线端点在相同点上,根据这个直接分组即可

几年前随手写的代码 相同项分组 - AutoLISP/Visual LISP 编 ...

种子集随意。。不一定线相连,可以任意实体
发表于 2024-8-19 19:41:07 | 显示全部楼层
没看到速度有什么差异啊,怎么不去想并行化呢?
 楼主| 发表于 2024-8-19 21:02:50 | 显示全部楼层
你有种再说一遍 发表于 2024-8-19 19:41
没看到速度有什么差异啊,怎么不去想并行化呢?

不知道并行是否你口中的多线程,还不会,最好来一段Demo
发表于 2024-8-19 21:21:13 | 显示全部楼层
wang2006zhi 发表于 2024-8-19 21:02
不知道并行是否你口中的多线程,还不会,最好来一段Demo

并行必然多线程...去搜搜吧
发表于 2024-8-21 07:22:00 | 显示全部楼层
你有种再说一遍 发表于 2024-8-20 18:12
多线程要注意很多东西的,
基本需要推翻原本的单线程方案的众多想法,
着重注意锁粒度/cache miss/偷懒线 ...

我们用go语言很多年了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-2-22 05:31 , Processed in 0.175483 second(s), 25 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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