原文链接:
Slab Boundary Revisited
问题
Jeremy
问题
如何使用 Revit API 获取楼板边界?
Jeremy
我2008年时就实现了一个名为 CmdSlabBoundary 的外部命令用于获取楼板边界。这个命令识别一块楼板的边界(包括门洞),然后沿着边界创建一组高亮的模型曲线。
针对你的问题,我重新检查了这个命令,并将其代码更新到 Revit 2013。[Transaction( TransactionMode.Manual )]
class CmdSlabBoundary : IExternalCommand
{
/// <summary>
/// 新创建的边界多边形曲线和楼板边缘之间的偏移
/// </summary>
const double _offset = 0.1;
/// <summary>
/// 获取指定实体的最下方水平表面的边界多边形
/// </summary>
/// <param name="polygons">最下方水平表面的边界多边形(包括孔洞)</param>
/// <param name="solid">实体(例如:楼板 Floor)</param>
/// <returns>最下方水平表面是否能找到</returns>
static bool GetBoundary(
List<List<XYZ>> polygons,
Solid solid )
{
PlanarFace lowest = null;
FaceArray faces = solid.Faces;
foreach( Face f in faces )
{
// 比较表面原点的 Z 坐标来确定最下方的表面
PlanarFace pf = f as PlanarFace;
if( null != pf && Util.IsHorizontal( pf ) )
{
if( ( null == lowest ) || ( pf.Origin.Z < lowest.Origin.Z ) )
{
lowest = pf;
}
}
}
if( null != lowest )
{
XYZ p, q = XYZ.Zero;
bool first;
int i, n;
EdgeArrayArray loops = lowest.EdgeLoops;
foreach( EdgeArray loop in loops )
{
List<XYZ> vertices = new List<XYZ>();
first = true;
foreach( Edge e in loop )
{
IList<XYZ> points = e.Tessellate();
p = points[0];
if( !first )
{
Debug.Assert( p.IsAlmostEqualTo( q ), "expected subsequent start point to equal previous end point" );
}
n = points.Count;
q = points[n - 1];
for( i = 0; i < n - 1; ++i )
{
XYZ v = points[i];
v -= _offset * XYZ.BasisZ; // 使用楼板边界的所有顶点加上偏移创建曲线顶点
vertices.Add( v );
}
}
// 比较最后一个顶点和第一个顶点是否相等来判断是否是闭合多边形
q -= _offset * XYZ.BasisZ;
Debug.Assert( q.IsAlmostEqualTo( vertices[0] ), "expected last end point to equal first start point" );
polygons.Add( vertices );
}
}
return null != lowest;
}
/// <summary>
/// 返回所有楼板边界多边形
/// </summary>
static public List<List<XYZ>> GetFloorBoundaryPolygons(
List<Element> floors,
Options opt )
{
List<List<XYZ>> polygons = new List<List<XYZ>>();
foreach( Floor floor in floors )
{
GeometryElement geo = floor.get_Geometry( opt );
//GeometryObjectArray objects = geo.Objects; // 2012
//foreach( GeometryObject obj in objects ) // 2012
foreach( GeometryObject obj in geo ) // 2013
{
Solid solid = obj as Solid;
if( solid != null )
{
GetBoundary( polygons, solid );
}
}
}
return polygons;
}
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements )
{
UIApplication app = commandData.Application;
UIDocument uidoc = app.ActiveUIDocument;
Document doc = uidoc.Document;
//
// 获取指定的楼板(使用适合你的场景的方式)
List<Element> floors = new List<Element>();
if( !Util.GetSelectedElementsOrAll(
floors, uidoc, typeof( Floor ) ) )
{
Selection sel = uidoc.Selection;
message = ( 0 < sel.Elements.Size )
? "Please select some floor elements."
: "No floor elements found.";
return Result.Failed;
}
Options opt = app.Application.Create.NewGeometryOptions();
List<List<XYZ>> polygons = GetFloorBoundaryPolygons( floors, opt );
int n = polygons.Count;
Debug.Print( "{0} boundary loop{1} found.", n, Util.PluralSuffix( n ) );
Creator creator = new Creator( doc );
using( Transaction t = new Transaction( doc ) )
{
t.Start( "Draw Slab Boundaries" );
creator.DrawPolygons( polygons );
t.Commit();
}
return Result.Succeeded;
}
}
我使用了一块包含两个孔洞的楼板做了测试,结果如下图:
这里有一个我还没搞清楚的问题:椭圆形的孔洞边界曲线没有显示为虚线。我猜测是 Revit 的显示机制将虚线优化成实线了。
包含更新后的 CmdSlabBoundary 命令的 Building Coder 例程可以在这里下载: version 2013.0.99.4