后台选择集
我把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 葛立恒凸包算法和面积最小包围盒
(完)