• 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 射线法

    (完)

  • 相关阅读:
    Cmder安装和设置
    php7.x版本的新特性
    【Luogu】P4916 [MtOI2018]魔力环 题解
    Codeforces 1530E Minimax 题解
    昭阳E42-80 屏幕不亮,风扇狂转
    iPad宽高像素值
    关于UIView的autoresizingMask属性的研究
    判断单链表中是否存在环及查找环的入口点
    网络编程
    事件响应者链的工作原理
  • 原文地址:https://www.cnblogs.com/JJBox/p/14766834.html
Copyright © 2020-2023  润新知