using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
namespace CadTools
{
/// <summary>
/// 嵌套实体拾取结果
/// </summary>
public class NestedPickResult
{
/// <summary>
/// 最终命中的实体ID
/// </summary>
public ObjectId EntityId;
/// <summary>
/// 最终命中的实体
/// </summary>
public Entity Entity;
/// <summary>
/// 从外到内的块路径
/// </summary>
public List<ObjectId> BlockPath = new List<ObjectId>();
/// <summary>
/// 累计变换矩阵
/// </summary>
public Matrix3d Transform = Matrix3d.Identity;
}
public static class NestedEntityHelper
{
/// <summary>
/// 按点获取最深层嵌套实体
/// </summary>
/// <param name="db">数据库</param>
/// <param name="pt">世界坐标点</param>
/// <param name="tol">命中容差</param>
/// <returns></returns>
public static NestedPickResult 按点获取最深层嵌套实体(
this Database db,
Point3d pt,
double tol = 1.0)
{
if (db == null)
return null;
NestedPickResult result = null;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt =
(BlockTable)tr.GetObject(
db.BlockTableId,
OpenMode.ForRead);
BlockTableRecord ms =
(BlockTableRecord)tr.GetObject(
btBlockTableRecord.ModelSpace,
OpenMode.ForRead);
FindInBlock(
tr,
ms,
pt,
Matrix3d.Identity,
new List<ObjectId>(),
tol,
ref result);
tr.Commit();
}
return result;
}
/// <summary>
/// 递归扫描块
/// </summary>
private static bool FindInBlock(
Transaction tr,
BlockTableRecord btr,
Point3d worldPt,
Matrix3d currentTransform,
List<ObjectId> path,
double tol,
ref NestedPickResult result)
{
bool found = false;
foreach (ObjectId id in btr)
{
Entity ent =
tr.GetObject(id, OpenMode.ForRead) as Entity;
if (ent == null)
continue;
// 先处理块参照
BlockReference br = ent as BlockReference;
if (br != null)
{
Matrix3d newTransform =
currentTransform * br.BlockTransform;
BlockTableRecord childBtr;
try
{
ObjectId btrId =
br.IsDynamicBlock
? br.DynamicBlockTableRecord
: br.BlockTableRecord;
childBtr =
(BlockTableRecord)tr.GetObject(
btrId,
OpenMode.ForRead);
}
catch
{
continue;
}
if (!PointInsideEntityExtents(
br,
worldPt,
currentTransform,
tol))
{
continue;
}
List<ObjectId> newPath =
new List<ObjectId>(path);
newPath.Add(br.ObjectId);
if (FindInBlock(
tr,
childBtr,
worldPt,
newTransform,
newPath,
tol,
ref result))
{
found = true;
}
}
// 当前实体命中检测
if (EntityHitTest(
ent,
worldPt,
currentTransform,
tol))
{
result = new NestedPickResult();
result.EntityId = ent.ObjectId;
result.Entity = ent;
result.BlockPath =
new List<ObjectId>(path);
result.Transform =
currentTransform;
found = true;
}
}
return found;
}
/// <summary>
/// 实体命中测试
/// </summary>
private static bool EntityHitTest(
Entity ent,
Point3d worldPt,
Matrix3d transform,
double tol)
{
try
{
if (!PointInsideEntityExtents(
ent,
worldPt,
transform,
tol))
{
return false;
}
Curve curve = ent as Curve;
if (curve != null)
{
Curve clone =
curve.GetTransformedCopy(transform)
as Curve;
Point3d closest =
clone.GetClosestPointTo(
worldPt,
false);
double dist =
closest.DistanceTo(worldPt);
clone.Dispose();
return dist <= tol;
}
return true;
}
catch
{
return false;
}
}
/// <summary>
/// 包围盒快速过滤
/// </summary>
private static bool PointInsideEntityExtents(
Entity ent,
Point3d pt,
Matrix3d transform,
double tol)
{
try
{
Extents3d ext = ent.GeometricExtents;
ext.TransformBy(transform);
return
pt.X >= ext.MinPoint.X - tol &&
pt.X <= ext.MaxPoint.X + tol &&
pt.Y >= ext.MinPoint.Y - tol &&
pt.Y <= ext.MaxPoint.Y + tol &&
pt.Z >= ext.MinPoint.Z - tol &&
pt.Z <= ext.MaxPoint.Z + tol;
}
catch
{
return false;
}
}
}
}