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不再可见范围了,而是全图.(所以更是适用在后台)

因为本人很懒的关系,本例没有进行耗时操作测试,只是单纯想知道后台选择集怎么制作,所以就敲了以下的代码...
也没有进行w选和c选的区分...这个是c选...

代码

命令

public class 后台选择集
{
    [CommandMethod("Test_SsgetEntity")]
    public void Test_SsgetEntity()
    {
        Document doc = Application.DocumentManager.MdiActiveDocument;
        Editor ed = doc.Editor;
        Database db = doc.Database;//当前的数据库
        ed.WriteMessage("\n****{惊惊盒子}后台选择集:");

        // 获取当前空间块表记录
        List<ObjectId> ids = new();
        db.Action(tr =>
                  {
                      // 块表
                      var acBlkTbl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                      // 当前空间的块表记录
                      var acBlkTblRec = tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead) as BlockTableRecord;
                      ids = 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 idList = new List<ObjectId>();
        db.Ssget(pts, ids, idList);
        db.Action(tr =>
                  {
                      foreach (var id in idList)
                      {
                          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 GetEnttityTool
    {
        /// <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, List<ObjectId> idsInit, List<ObjectId> idsOut)
        {
            var ss = new JoinBoxSsget(db, pts, idsInit);
            idsOut.AddRange(ss.IdsOut);
        }
    }
}

   /*封装*/
    public class JoinBoxSsget
    {
        /// <summary>
        /// 用于输出的id
        /// </summary>
        public List<ObjectId> IdsOut { get; private set; }

        /// <summary>
        /// 边界点集
        /// </summary>
        Point3d[] PtsArr { get; set; }

        /// <summary>
        /// 后台选择集
        /// </summary>
        /// <param name="db">数据库</param>
        /// <param name="pts">边界点集</param>
        /// <param name="idsInit">输入需要过滤的id,一般为当前空间</param>
        public JoinBoxSsget(Database db, IEnumerable<Point3d> pts, IEnumerable<ObjectId> idsInit)
        {
            if (pts.IsNull())
            {
                throw new ArgumentNullException("SsgetRectangleEntity点集范围为空");
            }
            if (idsInit.IsNull())
            {
                throw new ArgumentNullException("SsgetRectangleEntity图元集合为空");
            }
            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 ptmove = MovePtsArr(brf, PtsArr);
                        var IdsOutBlock = new List<ObjectId>();
                        SetBlock(brf, tr, IdsOutBlock, ptmove);//进入递归,要把块id输出,而不是内部图元id输出
                        if (IdsOutBlock.Count > 0)
                        {
                            IdsOut.Add(brf.ObjectId);
                        }
                    }
                    else
                    {
                        SetEntity(ent, IdsOut, PtsArr);
                    }
                    ent.Dispose();
                }
            }, false);
        }

        /// <summary>
        /// 变换当前边界到块内
        /// </summary>
        /// <param name="brf">块参照</param>
        /// <param name="ptsArr">边界点集</param>
        /// <returns></returns>
        private Point3d[] MovePtsArr(BlockReference brf, Point3d[] ptsArr)
        {
            var ptmove = new List<Point3d>();
            foreach (var pt in ptsArr)
            {
                ptmove.Add((pt - brf.Position).ToPoint3d());
            }
            return ptmove.ToArray();
        }

        /// <summary>
        /// 嵌套块用递归处理
        /// </summary>
        void SetBlock(BlockReference brf, Transaction tr, List<ObjectId> idsOut, Point3d[] ptsArr)
        {
            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(brfItem, ptsArr);
                    SetBlock(brfItem, tr, idsOut, ptmove);//进入递归
                }
                else
                {
                    SetEntity(entItem, idsOut, ptsArr);
                    if (idsOut.Count > 0)//存在就表示这个块是选择集了
                    {
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// 非块处理
        /// </summary>
        void SetEntity(Entity ent, List<ObjectId> idsOut, Point3d[] ptsArr)
        {
            //进行采样处理
            if (ent is Xline || ent is Ray)//参照线 射线(没仔细处理开头没有交点的问题)....................
            {
                Pt2 xlPt1 = null;
                Pt2 xlPt2 = null;

                //利用反射获取
                var entType = ent.GetType();
                var entBasePoint = entType.GetProperty("BasePoint");//反射获取属性
                if (entBasePoint != null)
                {
                    var obj = (Point3d)entBasePoint.GetValue(ent, null);
                    xlPt1 = new Pt2(obj.ToArray());
                }
                var entSecondPoint = entType.GetProperty("SecondPoint");//反射获取属性
                if (entSecondPoint != null)
                {
                    var obj = (Point3d)entSecondPoint.GetValue(ent, null);
                    xlPt2 = new Pt2(obj.ToArray());
                }

                // 判断参照线与边界的交点,且交点落在边界上,则为选中
                for (int i = 0; i < ptsArr.Length - 1; i++)
                {
                    // 求两条直线的交点,如果平衡则是null
                    var jiaodian = JoinBoxCurrency.MathTool.GetIntersectionPoint(
                        xlPt1,
                        xlPt2,
                        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)
                        {
                            idsOut.Add(ent.ObjectId);
                            break;
                        }
                    }
                }
            }
            else if (ent is Curve curve)
            {
                try
                {
                    var cs = new CurveSample<Point3d>(curve, 300);
                    bool flag = false;
                    foreach (var item in cs.GetSamplePoints)
                    {
                        if (item.IsPointInPL(ptsArr))
                        {
                            flag = true;
                            break;
                        }
                    }
                    if (flag)
                    {
                        idsOut.Add(ent.ObjectId);
                    }
                }
                catch (System.Exception e)
                {
                    throw e;
                }
            }
        }
    }

缺省函数链接

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

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

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

(完)

cad.net 后台选择集

上一篇:CSS选择器


下一篇:Syntax Error: Error: PostCSS received undefined instead of CSS string