zoukankan      html  css  js  c++  java
  • cad.net 后台选择集

    后台选择集

    我把20210510日志贴到此处了,并且删除了日志(些许编辑):

    需要1,cad自带的函数ssget如果进行矩形范围选择,那么需要设置屏幕范围可见,API上面提供了w选和c选.
    需求2,后台开图没有ssget.

    这个时候不妨制作一个自己的选择模式,不用可见:
    遍历模型块表记录,图元采样点集,有一点在矩形中就是c选,全部在矩形中就是w选.

    你可能认为cad的图元显示应该有一个图元四叉树,显示切换通过树快速拿到对应图元.
    但是我都用可行性来说话,首先出库入库经过的管理器和那棵树没开放API,其次每次调用显示就会经过很多层设备IO,
    所以这些都是导致了设置可见反而比较慢,
    那么就测试一下看看数据,e大的22万级图元遍历也就0.024秒,看这个以偏概全的测试,所以要相信大力也能出奇迹!

    说不定cad真就没有这颗树,因为桌子考虑问题的方式一般都是,可能不是最快的结果,但是这个demo最小,能跑就行,否则复杂度上去了就会有连锁反应...(谁要优化小数点后面的秒数呢,其实可以从一些开源cad看这方面的显示处理...
    如果要认真处理这个问题,要测试不同量级,计算IO的消耗和遍历比较.
    因为遍历可以一次就可以分析多个矩形范围,所以越是多个矩形,那么它效率越高,相反多个IO和多次从树中取果,也增耗时.
    例如
    10万图元,五个区域(内部要放固定数量的图元),切换ssget的c模式和w模式
    30万级.
    50万级.
    100万级.

    从Acad2018版起,ssget不再可见范围了,而是全图.
    所以本例更是适用在后台.

    代码

    本例没有进行耗时操作测试,只是单纯想知道后台选择集怎么制作,所以就敲了以下的代码...
    也仅仅做了c选,没有w选...日后再仔细完成...不过能看懂的应该都可以自己完善了...
    主要完成了核心: 多边形选择范围/嵌套块/参照线

    命令

    namespace JoinBox
    {
        public class 后台选择集
        {
            [CommandMethod("Test_SsgetEntity")]
            public void Test_SsgetEntity()
            {
                Document doc = Application.DocumentManager.MdiActiveDocument;
                Editor ed = doc.Editor;
                Database db = doc.Database;//当前的数据库
                ed.WriteMessage("
    ****{惊惊盒子}后台选择集:");
    
                // 获取当前空间块表记录
                List<ObjectId> idsInit = new();
                db.Action(tr =>
                {
                    // 块表
                    var acBlkTbl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                    // 当前空间的块表记录
                    var acBlkTblRec = tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead) as BlockTableRecord;
                    idsInit = acBlkTblRec.Toids();
                }, false);
    
                //选择集矩形范围
                var pts = new List<Point3d>()
                {
                    new Point3d(0, 0, 0),
                    new Point3d(0, 1000, 0) ,
                    new Point3d(1000, 1000, 0) ,
                    new Point3d(1000, 0, 0) ,
                };
                var idsOut = new List<ObjectId>();
                db.Ssget(pts, idsInit, idsOut);
                db.Action(tr =>
                {
                    foreach (var id in idsOut)
                    {
                        var ent = id.ToEntity(tr);
                        ent.UpgradeOpen();
                        ent.ColorIndex = 11;
    
                        if (ent is BlockReference brf)
                        {
                            ChangColorIndex(brf, tr);
                        }
                        ent.DowngradeOpen();
                    }
                });
            }
    
            // 递归块改颜色..
            void ChangColorIndex(BlockReference brf, Transaction tr)
            {
                var btRec = tr.GetObject(brf.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
                foreach (var item in btRec)
                {
                    var entItem = item.ToEntity(tr);
                    if (entItem is BlockReference brfItem)
                    {
                        ChangColorIndex(brfItem, tr);//进入递归
                    }
                    entItem.UpgradeOpen();
                    entItem.ColorIndex = 0;//随块
                    entItem.DowngradeOpen();
                    entItem.Dispose();
                }
            }
        }
    }
    

    函数

    /*静态调用*/
    namespace JoinBox
    {
        public static partial class GetEntityTool
        {
            /// <summary>
            /// 后台选择集
            /// </summary>
            /// <param name="db">数据库</param>
            /// <param name="pts">选择集的多段线范围</param>
            /// <param name="idsInit">输入来筛选的ids,来自于当前空间</param>
            /// <param name="idsOut">输出筛选后的id</param>
            public static void Ssget(this Database db, IEnumerable<Point3d> pts, IEnumerable<ObjectId> idsInit, List<ObjectId> idsOut)
            {
                var ss = new JoinBoxSsget(db, idsInit, pts);
                idsOut.AddRange(ss.IdsOut);
            }
        }
    }
    
    /*封装*/
    namespace JoinBox
    {
        public class JoinBoxSsget
        {
            /// <summary>
            /// 用于输出的id
            /// </summary>
            public List<ObjectId> IdsOut { get; private set; }
    
            /// <summary>
            /// 后台选择集
            /// </summary>
            /// <param name="db">数据库</param>
            /// <param name="pts">边界点集</param>
            /// <param name="idsInit">输入需要过滤的id,一般为当前空间</param>
            public JoinBoxSsget(Database db, IEnumerable<ObjectId> idsInit, IEnumerable<Point3d> pts)
            {
                if (idsInit.IsNull())
                {
                    throw new ArgumentNullException("SsgetRectangleEntity图元集合为空");
                }
                if (pts.IsNull())
                {
                    throw new ArgumentNullException("SsgetRectangleEntity点集范围为空");
                }
                var ptsArr = pts.ToArray();
                IdsOut = new List<ObjectId>();
    
                db.Action(tr =>
                {
                    foreach (var id in idsInit)
                    {
                        var ent = id.ToEntity(tr);
                        if (ent is BlockReference brf)
                        {
                            // 由于块外无法曲线取样,所以只能遍历块内图元,而遍历时候的块基点是0,0开始,
                            // 所以需要将[边界点集]从[块基点]平移到0,0
                            var ptsMove = MovePtsArr(ptsArr, brf);
                            var blockInternalIdsOut = new List<ObjectId>();//储存块内图元id
                            SetBlock(ptsMove, brf, tr, blockInternalIdsOut);//进入递归,要把块id输出,而不是块内图元id
                            if (blockInternalIdsOut.Count > 0)
                            {
                                IdsOut.Add(brf.ObjectId);//块id输出
                            }
                        }
                        else
                        {
                            SetEntity(ptsArr, ent, IdsOut);
                        }
                        ent.Dispose();
                    }
                }, false);
            }
    
            /// <summary>
            /// 边界点集变换到块内
            /// </summary>
            /// <param name="pts">边界点集</param>
            /// <param name="brf">块参照</param>
            /// <returns></returns>
            private List<Point3d> MovePtsArr(IEnumerable<Point3d> pts, BlockReference brf)
            {
                var ptsMove = new List<Point3d>();
                var enume = pts.GetEnumerator();
                while (enume.MoveNext())
                {
                    ptsMove.Add((enume.Current - brf.Position).ToPoint3d());
                }
                return ptsMove;
            }
    
            /// <summary>
            /// 嵌套块用递归处理
            /// </summary>
            void SetBlock(IEnumerable<Point3d> pts, BlockReference brf, Transaction tr, List<ObjectId> idsOut)
            {
                var btRec = tr.GetObject(brf.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
                foreach (var item in btRec)
                {
                    var entItem = item.ToEntity(tr);
                    if (entItem is BlockReference brfItem)
                    {
                        var ptmove = MovePtsArr(pts, brfItem);
                        SetBlock(ptmove, brfItem, tr, idsOut);//进入递归
                    }
                    else
                    {
                        SetEntity(pts, entItem, idsOut);
                        if (idsOut.Count > 0)//存在就表示这个块是选择集了
                        {
                            return;
                        }
                    }
                }
            }
    
            /// <summary>
            /// 非块处理,曲线采样
            /// </summary>
            void SetEntity(IEnumerable<Point3d> pts, Entity ent, List<ObjectId> idsOut)
            {
                if (ent is DBPoint bPoint)
                {
                    if (new Point3d(bPoint.Position.X, bPoint.Position.Y, 0).IsPointInPL(pts))
                    {
                        idsOut.Add(ent.ObjectId);
                    }
                }
                else if (ent is Region region)  //面域...还有一些非继承曲线的:遮罩/3D图元
                {
                    //不是很想写了....
                }
                else if (ent is Xline xline)//参照线
                {
                    // 判断参照线与边界的交点,且交点落在边界上,则为选中
                    bool yes = Intersection(pts, xline.BasePoint, xline.SecondPoint, false);
                    if (yes)
                    {
                        idsOut.Add(ent.ObjectId);
                    }
                }
                else if (ent is Ray ray)//射线
                {
                    // 射线也是直线方程,只是交点位置如果是逆向量的话,就为不存在
                    bool yes = Intersection(pts, ray.StartPoint, ray.SecondPoint, true);
                    if (yes)
                    {
                        idsOut.Add(ent.ObjectId);
                    }
                }
                else if (ent is Curve curve)
                {
                    try
                    {
                        var cs = new CurveSample<Point3d>(curve, 300);
                        foreach (var pt in cs.GetSamplePoints)
                        {
                            if (pt.IsPointInPL(pts))
                            {
                                idsOut.Add(ent.ObjectId);
                                break;
                            }
                        }
                    }
                    catch { }
                }
            }
    
            /// <summary>
            /// 直线方程判断参照线和射线是否在内部
            /// </summary>
            /// <param name="pts">边界点集</param>
            /// <param name="xlPt1">线点</param>
            /// <param name="xlPt2">线点</param>
            /// <param name="isRay">是否射线</param>
            /// <returns></returns>
            private static bool Intersection(IEnumerable<Point3d> pts, Point3d xlPt1, Point3d xlPt2, bool isRay)
            {
                var ptsArr = pts.ToArray();//因为这里要取两个,就不获取枚举了
                for (int i = 0; i < ptsArr.Length - 1; i++)
                {
                    // 求两条直线的交点,如果平衡则是null
                    var jiaodian = JoinBoxCurrency.MathTool.GetIntersectionPoint(
                        new Pt2(xlPt1.ToArray()),
                        new Pt2(xlPt2.ToArray()),
                        new Pt2(ptsArr[i].ToArray()),
                        new Pt2(ptsArr[i + 1].ToArray()));
                    if (jiaodian != null)
                    {
                        // 在判断交点在选择集边线子段之间,用凸度0判断
                        var pt_jiaodian = new Point3d(jiaodian.X, jiaodian.Y, 0);
                        var bulge = MathTool.GetBulge(ptsArr[i], pt_jiaodian, ptsArr[i + 1]);
                        if (bulge == 0)
                        {
                            if (isRay)
                            {
                                // 求两射线交点是否成立(起点->尾点)与(起点->交点)的单位向量是同一个方向就加入,否则就是逆向,不加入
                                var rayPt1 = new Point2d(xlPt1.ToArray());
                                var rayPt2 = new Point2d(xlPt2.ToArray());
                                var rayVe1 = rayPt1.GetVectorTo(rayPt2).GetNormal();
                                var rayVe2 = rayPt1.GetVectorTo(new Point2d(jiaodian.ToArray())).GetNormal();
                                return rayVe1 == rayVe2;//这里精度居然是对的
                            }
                            else
                            {
                                return true;
                            }
                        }
                    }
                }
                return false;
            }
        }
    }
    

    缺省函数链接

    JoinBoxCurrency.MathTool.GetIntersectionPoint 数学篇 求两条直线的交点,说明过程

    MathTool.GetBulge 数学篇 cad.net 向量+点乘+叉乘+矩阵+凸度

    CurveSample 数学篇 cad.net 葛立恒凸包算法和面积最小包围盒

    IsPointInPL 射线法

    (完)

  • 相关阅读:
    HDU4628+状态压缩DP
    Javascript 去掉字符串前后空格的五种方法
    Javascript 数组之判断取值和数组取值
    ASP.NET MVC 出现错误 “The view 'XXX' or its master was not found or no view engine support”
    ASP.NET MVC 页面调整并传递参数
    ASP.NET MV3 部署网站 报"Could not load file or assembly ' System.Web.Helpers “ 错的解决方法
    ASP.NET MVC 控制器向View传值的三种方法
    CSharp 如何通过拼接XML调用存储过程来查询数据
    SQLServer : EXEC和sp_executesql的区别
    关于SQLServer2005的学习笔记—异常捕获及处理
  • 原文地址:https://www.cnblogs.com/JJBox/p/14766834.html
Copyright © 2011-2022 走看看