Developers have frequently asked how to determine the boundary polygons of a horizontal slab, e.g. the loops formed by the bottom edges of all the vertical or ‘side’ faces in this floor:

Floor Slab with Holes

In this case, there is one outer and three inner loops, one of which is circular.

There are several different possible approaches to obtain this information, and you will need to decide which is simplest or most effective or optimally suited to provide the data you require in each individual case. Here is a suggestion for a very straightforward algorithm: determine the slab solid, iterate over its faces, ignore all non-horizontal faces, and determine which of the horizontal ones has the lowest Z coordinate. Query the lowest face for its edge loops, and collect all its vertices into a structure for handling the polygon data.

I have implemented an external command CmdSlabBoundary realising this algorithm. For the polygon data, I use a list of lists of points. Each list of points represents one closed polygonal loop. This means that non-linear polygon edges will have to be approximated by straight segments. If you need to handle other curve types such as circular arcs or ellipses exactly, you would have to enhance this definition.
It currently only handles planar faces on horizontal slabs. It uses the Edge Tesselate method, which returns a polyline approximation to the edge, to extract the points to be added to the boundary polygon.

Here is the code for the mainline of the command. It implements some useful code to determine which floor elements to process. It first checks whether anything at all has been selected. If so, it extracts the floor elements from the selection and returns with an error message if none are found. If nothing has been preselected by the user before starting the command, it selects all floor elements from the model, and again returns with an error message if none are found. Then, for each floor, the geometry is extracted and the solid is passed into the GetBoundary() method for analysis:


public CmdResult Execute(
  ExternalCommandData commandData,
  ref string message,
  ElementSet elements )
{
  Application app = commandData.Application;
  Document doc = app.ActiveDocument;
 
  List<RvtElement> floors = new List<RvtElement>();
  Selection sel = doc.Selection;
  if( 0 < sel.Elements.Size )
  {
    foreach( RvtElement e in sel.Elements )
    {
      if( e is Floor )
      {
        floors.Add( e );
      }
    }
    if( 0 == floors.Count )
    {
      message = "Please select some floor elements.";
      return CmdResult.Failed;
    }
  }
  else
  {
    doc.get_Elements( typeof( Floor ), floors );
    if( 0 == floors.Count )
    {
      message = "No floor elements found.";
      return CmdResult.Failed;
    }
  }
 
  List<List<XYZ>> polygons = new List<List<XYZ>>();
  Options opt = app.Create.NewGeometryOptions();
 
  foreach( Floor floor in floors )
  {
    GeoElement geo = floor.get_Geometry( opt );
    GeometryObjectArray objects = geo.Objects;
    foreach( GeometryObject obj in objects )
    {
      Solid solid = obj as Solid;
      if( solid != null )
      {
        GetBoundary( polygons, solid );
      }
    }
  }
 
  int n = polygons.Count;
 
  Debug.WriteLine( string.Format(
    "{0} boundary loop{1} found.",
    n, Util.PluralSuffix( n ) ) );
 
  Creator creator = new Creator( app );
  creator.DrawPolygons( polygons );
 
  return CmdResult.Succeeded;
}

Here is the implementation of GetBoundary(); I added a constant value to offset the boundary polygons downward a bit, away from the slab edge, so that they are more clearly visible:


const double _offset = 0.1;
 
bool GetBoundary(
  List<List<XYZ>> polygons,
  Solid solid )
{
  PlanarFace lowest = null;
  FaceArray faces = solid.Faces;
  foreach( Face f in faces )
  {
    PlanarFace pf = f as PlanarFace;
    if( null != pf && 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 )
      {
        XYZArray points = e.Tessellate();
        p = points.get_Item( 0 );
        if( !first )
        {
          Debug.Assert( p.AlmostEqual( q ),
            "expected subsequent start point"
            + " to equal previous end point" );
        }
        n = points.Size;
        q = points.get_Item( n - 1 );
        for( i = 0; i < n - 1; ++i )
        {
          XYZ v = points.get_Item( i );
          v.Z -= _offset;
          vertices.Add( v );
        }
      }
      q.Z -= _offset;
      Debug.Assert( q.AlmostEqual( vertices[0] ),
        "expected last end point to equal"
        + " first start point" );
      polygons.Add( vertices );
    }
  }
  return null != lowest;
}

Here is the result displaying the model lines added after processing, with all lines offset downwards from the slab edge by 0.1 feet, and one of the edges approximating the bottom of the circular opening highlighted:

Slab polygonal boundary loops

Finally, here are the model lines isolated to distinguish them better from the slab:

Boundary loops isolated

I hope this provides a good geometrical analysis example and a starting point for further exploration. Obviously, I am interested in any improvements you may have or bugs that you find.

I am adding a new version 1.0.0.9 of the complete Visual Studio solution
here,
including the new CmdSlabBoundary class as well as all other commands discussed so far.


Comments

13 responses to “Slab Boundary”

  1. Thanks Jeremy, I have tested the code and it works. I’d like get the same result with a wall, so I have changed the GetBoundary code testing the face with IsVertical and checking the Y component of the face Origin.
    Clearly it works only for horizontal walls because the check is on the Y component. It is possible to check belong the wall’s direction or in its local coordinates?
    Thanks in advance
    Art

  2. Hi Art,
    I am glad that it works for you. Yes, of course you can use the same principle for a wall, just like you describe.
    When you say ‘horizontal wall’, I assume you mean parallel to the X axis … normally a horizontal wall would be called a floor :-)
    The solution to your question is just like you suggest, checking perpendicularly to the wall centre line. How can you determine this direction? There are several ways to achieve this. Just like for the slabs we discussed, let us assume that the wall is planar. In that case, the easiest way to obtain a vector perpendicular to the wall centre line is to use its location line, which we demonstrated in the post on Azimuth. Another way would be to query a face for its normal vector, but you might have trouble deciding which fase to ask.
    Say the location line start and end points are p and q, and the vector from p to q is v. Then the perpendicular to that is v cross Z axis. Since the Z axis is (0,0,1), and we can assume that v has a zero Z component, so is something like (vx,vy,0), the perpendicular vector is simply (-vy,vx,0). Simply determine the face with a minimum or maximum distance along that vector.
    If you hang on for a week or two, and a couple more people express interest in the topic, I’ll probably get around to discussing something like this in more detail.
    Best regards,
    Jeremy

  3. :) yes I mean parallel to the X axis …
    I have tried to create the sketchPlane passing the wall location curve to NewSketchPlanePassLine so I draw the boundary line of wall and its hosted elements correctly. (For wall planar). I’m trying to draw the boundary polygons in a drafting view creating a new sketch plane with XYZ.BasisY as normal, XYZ.Zero as origin. Same result: wall parallel to X axis is ok, parallel to Y axis nok. I think that I create a bad sketch plane.

  4. Hi Art,
    Yes, as said, you need to calculate a vector perpendicular to the wall line and use that. I will discuss this topic in a future post.
    Best regards,
    Jeremy

  5. Jeremy:
    I’ve a total noob to revit and it’s api. However, I’ve been tasked to accomplish a few things, mostly room data pulls and pushes to an external sql server database. I’ve inherited a bunch of projects and code from someone else, but one thing the code doesn’t do is pull room geometry and push it to the external db.
    My question is, is there a similar function to what you have posted here, but for an individual room? I guess the other thing that would help would be if there were a way to retrieve the geometry for an entire floor.
    Thanks.

  6. Hi Dave,
    As you may have guessed from the two examples of retrieving different flavours of geometry from a slab, there is an infinite number of different aspects of geometry that may be relevant to different applications. From your question, I have no idea what exact geometry you wish to retrieve from the rooms. The two prime candidates are the room boundary, i.e. basically two-dimensional polygonal data, or the three-dimensional faces bounding the room volume. It would not surprise me at all, though, if you in fact need something completely or even just slightly different. If you are looking for the room boundary polygons, you are in luck, because that is demonstrated by the Revit SDK sample RoomViewer in the subdirectory Viewers, based on the Room Boundary property. Retrieval of the 3D faces bounding the room volume is more complex. The Room ClosedShell property, returning a geometry element, might be helpful. So would the RoofsRooms sample, which determines which roof elements bounding a room from above. Determining these elements is harder than obtaining the walls bounding it from the side, which is why the sample was recently added, in the middle of the Revit 2009 release cycle. I know of no built-in API features for retrieve geometry for an entire floor, and I do not think that such a feature would ever be very useful, either, given the many different aspects that are important for different applications. I hope this helps.
    Best regards, Jeremy.

  7. Jeremy:
    Thanks for your response. The room boundary polygons was what I was after.
    Thanks!

  8. Cosmas Avatar
    Cosmas

    Jeremy,
    Can you help me with some VB code to get the Slab corners or edges in Revit 2011 and associate them with supporting Beams? In structural design slabs are usually supported by Beams. It seems that Slabs in Revit are only graphical elements and are not assocuated with underlying Beams
    Than you for your time

  9. Dear Cosmas,
    Sorry, I do not have anything like that here ready at hand. All I can suggest at the moment is to analyse the slab and beam geometry and determine their proximity that way, e.g. as described for walls and columns in
    http://thebuildingcoder.typepad.com/blog/2010/01/findreferencesbydirection.html
    There are probably better ways to do it, though. Does the structural analytical support information not help you there?
    Cheers, Jeremy.

  10. Than you Jeremy.
    On another note, is there any way to get the centroid of a footing?

  11. Dear Cosmas,
    Thank you very much for your appreciation.
    Sure thing, depending on whether you want the centroid of (i) the plan view 2D, (ii) the real 3D solid centroid, or an approximate result.
    You can either:
    (i) Determine the top and bottom faces of the footing and ensure that they are identical in plan view (if that is appropriate at all in your case), and then determine the centroid of the resulting polyhedron. You might be able to make use of
    http://thebuildingcoder.typepad.com/blog/2009/08/bottom-face-of-a-wall.html
    (ii) Determine the Revit solid faces, use a solid modeller to reconstruct a real solid model from that, and then ask it for its centroid:
    http://thebuildingcoder.typepad.com/blog/2009/06/convex-hull-and-volume-computation.html
    (iii) For an initial much simpler approximate solution, I would simply ask the footing for its solid, iterate over and triangulate all its faces, and determine the average value of all those points, avoiding duplicates.
    I would assume that solution (iii) would give you a reasonable result in most of the occurring reasonable situations.
    Cheers, Jeremy.

  12. Ola! jeremy tammik,fiquei muito satisfeito com o seu blog e vejo que encontrei a solução para o meu problema,trabalho com projeto de pre fabricado de concreto e tenho dificuldade para fazer as juntas demontagem de 1cm e os furos nas peças, se voce poder me ajudar.me passa seu e-mail que eu anexo o arquivo com os dados que eu preciso.
    desde já agradeço a atenção
    laercio dos santos – brasil

  13. Dear Laercio,
    Thank you very much for your appreciation and I am glad that the blog is useful for you.
    My email is available from the about the author section, and I will happily take a look at the problem description, but I definitely cannot promise that I will have any possibility at all to look at it in any depth personally.
    If you are an ADN member, I would strongly suggest that you submit an ADN DevHelp Online case instead.
    Cheers, Jeremy.

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading