Slab Boundary Revisited

After

yesterday’s rejuvenation
of
the old wall footing

host reference relationship detection
,
today raises another old question prompting me to update and retest The Building Coder samples yet again:

Question: How can I obtain the boundary of a floor slab using the Revit API, please?

Answer: I implemented a CmdSlabBoundary external command to

determine the slab boundary
back
in the dawn of time, in 2008, in one of the first posts on this blog.

It determines the boundary edges of a floor slab, including holes, and creates a set of model curves along the bottom edges of the slab to highlight them.

Since then it has just been flat ported every year from one version to the next.

To ensure that it still works, I updated it slightly and tested in Revit 2013.
All I did this time around was to change the transaction mode from automatic to manual, since

automatic transaction mode is considered obsolete
nowadays.

Here is the updated implementation:


[Transaction( TransactionMode.Manual )]
class CmdSlabBoundary : IExternalCommand
{
  /// <summary>
  /// Offset the generated boundary polygon loop
  /// model lines downwards to separate them from
  /// the slab edge.
  /// </summary>
  const double _offset = 0.1;
 
  /// <summary>
  /// Determine the boundary polygons of the lowest
  /// horizontal planar face of the given solid.
  /// </summary>
  /// <param name="polygons">Return polygonal boundary
  /// loops of lowest horizontal face, i.e. profile of
  /// circumference and holes</param>
  /// <param name="solid">Input solid</param>
  /// <returns>False if no horizontal planar face was
  /// found, else true</returns>
  static 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 && 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>
  /// Return all floor slab boundary loop polygons
  /// for the given floors, offset downwards from the
  /// bottom floor faces by a certain amount.
  /// </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;
 
    // retrieve selected floors, or all floors, if nothing is selected:
 
    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;
  }
}

I ran this command on a minimal sample model containing just one floor slab with a couple of holes in it.
That produces the following result, in which I modified the model lines graphics display to distinguish them better – don’t know why the model ellipse is not picking up the dotted line as well as the straight segments – maybe the tessellation is chopping it up too finely:

Floor slab boundaries

Here is
version 2013.0.99.4 of
The Building Coder samples including the updated CmdSlabBoundary external command.

Programmatically Invoke Snoop Objects Dialogue

Yesterday I discovered an pretty cool post by Daren Thomas, the father of

RevitPythonShell
,
describing how you can

programmatically invoke the RevitLookup Snoop Objects dialogue
.

His code is in Python, though the concepts are equally valid in and port perfectly to any other .NET language as well.

Definitely worth taking a look at!

HTCPCP – Hyper Text Coffee Pot Control Protocol

Another little item that you absolutely must be aware of in the cloud and mobile arena is the

Hyper Text Coffee Pot Control Protocol standard HTCPCP
.
It is worthwhile noting the date of publication, also :-)

Closing Revit by Posting WM_CLOSE to its Main Window Handle

Before closing this post, one last Revit API related note on closing Revit itself…

Shutting down and exiting the Revit session is not supported by the official Revit API.

I discussed

closing the active document
and
why not to do so.
The same caveats obviosuly apply to shutting down the Revit session itself.

Still, Augusto Goncalves now presents a neat little solution to obtain the Revit main window handle and

close down Revit
by
using SendMessage to post a WM_CLOSE request to it.


Comments

10 responses to “Slab Boundary Revisited”

  1. Jeremy,
    is it possible to modify floor Opening boundary at all? I looked at most of your posts and API samples covering floors and their openings, but could not find a single mention of this. I am currently working on a piece of code which needs to update the slab/floor opening (non rectangular) profiles without deleting them but in my investigations i found that opening.BoundaryCurves property is ‘read only’ making it impossible to swap/add/delete curves in that curveArray. I wonder if anyone has tried any other approach to solve this?
    Adi

  2. Dear Adi,
    I think that currently there is no official way to achieve that, sorry to say.
    You may be able to get at the existing sketch and its model lines using undocumented element id relationships:
    http://thebuildingcoder.typepad.com/blog/2011/11/undocumented-elementid-relationships.html
    Then you might be able to modify the existing lines a little bit in place under certain circumstances.
    Another way to create an opening in a wall is to insert a custom dummy window instance. If you do that, you could later edit the window family to modify the shape.
    Please look at this discussion as well, and the comments on it:
    http://thebuildingcoder.typepad.com/blog/2013/07/create-a-floor-with-an-opening-or-complex-boundary.html
    No official standard approach, though.
    Cheers, Jeremy.

  3. Hi Jeremy.
    What about if you assume the slab is not horizontal but sloping like in a ramped car park?
    Russ

  4. Dear Russ,
    The same principles apply.
    One would obviously have to replace the call to Util.IsHorizontal by something very slightly more sophisticated, though.
    For instance, assuming that the ramp is more wide and long than high, one could first select the two faces with the largest areas, hoping that they would be the top and bottom ones, and then pick the lower of the two.
    Another approach would be to pick the two faces whose normal vectors have the smallest horizontal components, i.e. minimal X and Y components, or better still minimal xx+yy, and then again take the lower of those two.
    One could also go for the top face instead of the bottom one, if that is more suitable.
    Isn’t freedom a wonderful thing?
    Cheers, Jeremy.

  5. Thanks.
    I can detect my bottom face or top face but for some reason I’m really not finding it easing to draw the boundary. (trying to visually debug a problem) but the sloped edges don’t get drawn.
    Ah well

  6. Dear Russ,
    No idea what the problem might be. The Creator class should easy do what you need. Is your slab generated by code or manually? You can provide a minimal sample for us to take a look, if you like:
    http://thebuildingcoder.typepad.com/blog/about-the-author.html#1b
    Cheers, Jeremy.

  7. thanks Jeremy.
    Slab is generated manually. I’ll have another look at it this weekend when I’ll have a but of quiet time to concentrate.

  8. Hi @ All,
    I have a question about slab concrete cantilever for a floor. Is there a way to set these values inside the revit api. I looking for hours already to find a solution, but no luck so far. Would be very helpful if Jeremy or someone else could place a link or a hint.
    Thanks a lot in advance
    Christian

  9. I found a solution to this problem here. Hope it will help someone else.
    http://adndevblog.typepad.com/aec/2013/10/change-the-boundary-of-floorsslabs.html#comment-6a0167607c2431970b01b8d0d1a5ba970c
    Autodesk, please start making good software. Please.
    Christian

  10. Dear Christian,
    Congratulations on finding a solution for your issue and thank you for sharing the link to Joe’s post to change the boundary of floors and slabs:
    http://adndevblog.typepad.com/aec/2013/10/change-the-boundary-of-floorsslabs.html
    Sorry about the convoluted approach.
    We are doing our best, I assure you :-)
    Things like this happen when complex systems evolve. Thank you for your understanding.
    Cheers, Jeremy.

Leave a Reply to chrisCancel reply

Discover more from Autodesk Developer Blog

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

Continue reading