原贴:CAD.Net Bo边界算法 20200404更新
cad的低版本只能发送命令的方式获取边界信息,edata说这个bo是写在arx内的,不是接口,所以也不能反射用(没深究了)
到了2011,就可以通过以下语句获取:
Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor;
ed.TraceBoundary(........);
通过命令发送bo的方法:点我
Running boundary command through command line using .NET
By Virupaksha Aithal
Below code shows the procedure to run the boundary command through command line. Note, code uses ActiveX API SendCommand to run the command. To avoid the different interop’s issue, code uses late binding technique. Command is run in session mode, so that issued boundary command is run synchronously.Also, refer blog on identifying the boundary using API here
static ObjectIdCollection collection = new ObjectIdCollection();
static string commandName = "";
static bool IsCommandActive()
{
String str = (String)Application.GetSystemVariable("CMDNAMES");
if (String.Compare(commandName, str, true) != 0)
{
return true;
}
return false;
}
[CommandMethod("BoundaryCommandLine", CommandFlags.Session)]
static public void BoundaryCommandLine()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
PromptPointOptions options =
new PromptPointOptions("Pick a point");
PromptPointResult result = ed.GetPoint(options);
if (result.Status != PromptStatus.OK)
return;
double x = result.Value.X;
double y = result.Value.Y;
string strPt = x.ToString() + "," + y.ToString();
//put the database
Database db = doc.Database;
collection.Clear();
commandName = (String)Application.GetSystemVariable("CMDNAMES");
db.ObjectAppended +=
new ObjectEventHandler(Database_ObjectAppended);
//run the boundary command using ActiveX send command.
object[] dataArry = new object[1];
dataArry[0] = "-boundary " + strPt + " ";
doc.AcadDocument.GetType().InvokeMember("SendCommand",
BindingFlags.InvokeMethod,
null, doc.AcadDocument, dataArry);
if (IsCommandActive() == true)
{
dataArry[0] = "Yes ";
doc.AcadDocument.GetType().InvokeMember(
"SendCommand",
BindingFlags.InvokeMethod,
null, doc.AcadDocument, dataArry
);
}
db.ObjectAppended -=
new ObjectEventHandler(Database_ObjectAppended);
using (DocumentLock lock1 = doc.LockDocument())
{
using (Transaction ta =
db.TransactionManager.StartTransaction())
{
foreach (ObjectId id in collection)
{
if (id.IsErased == true)
{
continue;
}
DBObject obj = ta.GetObject(id,
OpenMode.ForRead);
if (obj is
Autodesk.AutoCAD.DatabaseServices.Polyline)
{
//
Autodesk.AutoCAD.DatabaseServices.Polyline pl
= (Autodesk.AutoCAD.DatabaseServices.Polyline)obj;
ed.WriteMessage("area is " +
pl.Area.ToString() + "\n");
}
else if (obj is
Autodesk.AutoCAD.DatabaseServices.Region)
{
Autodesk.AutoCAD.DatabaseServices.Region rg =
(Autodesk.AutoCAD.DatabaseServices.Region)obj;
ed.WriteMessage("area is "
+ rg.Area.ToString() + "\n");
}
}
ta.Commit();
}
}
}
static void Database_ObjectAppended(object sender, ObjectEventArgs e)
{
collection.Add(e.DBObject.ObjectId);
}
要注意的是,如果空格再次执行上次命令,你会执行到bo,而不是你的当前命令..而正常的cad用户都是要执行当前的命令.
解决方法:要解开文档锁的情况下发送一次当前命令,而且在命令外做一个立即结束的标记,在最前面写:
但是,要是自己能造一个边界算法,为什么要靠桌子的…..
打倒自动桌子!!!! 打倒Acad!!!
于是乎…………..
我做了一张图(拖拉图片看大图)
本来的初衷是觉得它的边界算法无法很好的将边界线给闭合,所以是想通过遍历数据库来实现对屏幕外的边界做补充,
但是了解了cad的bo算法之后,决定放弃……
因为桌子的是最佳实践来的..几乎没什么好改的….
桌子实现的方式是获取当前屏幕可视的图形,然后遍历它们的图元信息,而按照我的想法,将不可避免的很多次数据库….
下面是山人告诉我的bo算法思想… 它就是图上的拆解算法:
样图:
全部交点打断,然后bo到的边界就是我标数字的线段组成的.(其实不是打断,是求交点,我是便于大家理解)
然后,步骤如下:
&1、射线求交点
从A点做0°直线(射线),与所有的线只有4个交点,就是我画绿色圆的点;
选择一个离A点最近的点,并通过这个点得到1号直线:数学上来讲就是两个直线方程联立.
&2、获取下一段线段
得到1号直线的两个端点,选择相对A-1线角度逆时针方向的点C,
由C点做选择集,得到2号和1号直线,排除1号直线:可能C点相交有多个图元,存在和&3一样的情况,可能存在没有相交的图元,需要细节处理.
然后用2号直线的另一个交点d做选择集:非选择集需要轮询(所有图元?2号线包围盒范围内的?)与2号线来求交,得出最近交点,
得到2号、3号,21、22号直线.(如果是不打断的话,则没有21、22,但是,万一情况是图元接上去呢,则需要以相同条件来进行)
&3、逆时针选择的作用
这个时候,你要选择3号直线才是对的,
算法应选择直线的角度更靠近逆时针的那个: 也就是说d为基点,22号 在最右,21在中间,3在最左,那就选择最左的那个3
这时候的你轮廓集合里已经有3条直线了,分别为1 、2 、 3
&4、循环以上操作
然后就是不断的重复,直到走到12号线,又一次选择到了1号线,循环结束;
分析:
经过&1和&2的步骤可知,每次都需要轮询一次所有的图元,那么这个轮询就是数量就是线性递减的,因为已经分析过的图元,除了第一个需要保留以判断闭环之外,其他都可以抛弃.
这个是最简单的,没有内部孤岛,没有曲线,的bo原理.