Getting the Wall Elevation Profile

I am back from the vacation in Italy, which I enjoyed very much, especially another visit to the wonderful coast of Amalfi.

To quickly share an interesting new result using the Revit API, here is a solution by Katsuaki Takamizawa to retrieve the wall elevation profile.

It also provides a nice little example of using the

ExporterIFCUtils.SortCurveLoops method
that
we just recently documented.

Says Katsu:

I created some sample code for finding the wall elevation profile and determining its outer and inner loops.

The

wall elevation profile
topic
was already discussed way back in 2008, followed by determination of the related polygon areas.

This sample uses newer methods and the code is much simpler.

The sample draws model lines based on the existing wall elevation profile.

The outer edge loop is colored in red.

It uses the ExporterIFCUtils.SortCurveLoops method to sort the outer and inner loops.

The outer loop is always counter-clockwise oriented, so we can use the IsCounterclockwise method to detect it.

The result of running the command on a simple wall with a door and two windows looks like this:

Wall elevation profile

Here is the entire implementation of the external command Execute method:


  UIApplication uiapp = commandData.Application;
  UIDocument uidoc = uiapp.ActiveUIDocument;
  Application app = uiapp.Application;
  Document doc = uidoc.Document;
  View view = doc.ActiveView;
 
  Autodesk.Revit.Creation.Application creapp
    = app.Create;
 
  Autodesk.Revit.Creation.Document credoc
    = doc.Create;
 
  Reference r = uidoc.Selection.PickObject(
    ObjectType.Element, "Select a wall" );
 
  Element e = uidoc.Document.GetElement( r );
 
  Wall wall = e as Wall;
 
  using( Transaction tx = new Transaction( doc ) )
  {
    tx.Start( "Wall Profile" );
 
    // Get the external wall face for the profile
 
    IList<Reference> sideFaces
      = HostObjectUtils.GetSideFaces( wall,
        ShellLayerType.Exterior );
 
    Element e2 = doc.GetElement( sideFaces[0] );
 
    Face face = e2.GetGeometryObjectFromReference(
      sideFaces[0] ) as Face;
 
    // The normal of the wall external face.
 
    XYZ normal = face.ComputeNormal( new UV( 0, 0 ) );
 
    // Offset curve copies for visibility.
 
    Transform offset = Transform.CreateTranslation(
      5 * normal );
 
    // If the curve loop direction is counter-
    // clockwise, change its color to RED.
 
    Color colorRed = new Color( 255, 0, 0 );
 
    // Get edge loops as curve loops.
 
    IList<CurveLoop> curveLoops
      = face.GetEdgesAsCurveLoops();
 
    // ExporterIFCUtils class can also be used for 
    // non-IFC purposes. The SortCurveLoops method 
    // sorts curve loops (edge loops) so that the 
    // outer loops come first.
 
    IList<IList<CurveLoop>> curveLoopLoop
      = ExporterIFCUtils.SortCurveLoops(
        curveLoops );
 
    foreach( IList<CurveLoop> curveLoops2
      in curveLoopLoop )
    {
      foreach( CurveLoop curveLoop2 in curveLoops2 )
      {
        // Check if curve loop is counter-clockwise.
 
        bool isCCW = curveLoop2.IsCounterclockwise(
          normal );
 
        CurveArray curves = creapp.NewCurveArray();
 
        foreach( Curve curve in curveLoop2 )
        {
          curves.Append( curve.CreateTransformed( offset ) );
        }
 
        // Create model lines for an curve loop.
 
        Plane plane = creapp.NewPlane( curves );
 
        SketchPlane sketchPlane
          = SketchPlane.Create( doc, plane );
 
        ModelCurveArray curveElements
          = credoc.NewModelCurveArray( curves,
            sketchPlane );
 
        if( isCCW )
        {
          foreach( ModelCurve mcurve in curveElements )
          {
            OverrideGraphicSettings overrides
              = view.GetElementOverrides(
                mcurve.Id );
 
            overrides.SetProjectionLineColor(
              colorRed );
 
            view.SetElementOverrides(
              mcurve.Id, overrides );
          }
        }
      }
    }
    tx.Commit();
  }
  return Result.Succeeded;

I initially tried to move the model lines using the ElementTransformUtils.MoveElement method to offset them for better visibility.

That did not work.

It seems that these model lines are attached to the wall face, and the MoveElement method cannot move them away from the wall.

So I modified the underlying geometry curves first, before creating model lines from them.

Here is
GetWallProfile.zip containing
the entire Visual Studio solution, external command source code and add-in manifest.

Many thanks to Katsu-san for implementing and sharing this!


Comments

4 responses to “Getting the Wall Elevation Profile”

  1. Arif Hanif Avatar
    Arif Hanif

    Jeremy,
    Is there a way to create a filled region for room boundary?

  2. Dear Arif,
    I don’t know off-hand.
    What are the exact steps to create it manually, e.g. in an empty new project?
    What new elements are added? You can explore them using the element lister, RevitLookup, and the Revit Python and Ruby shells.
    Then you have three options to try to achieve the same programmatically: best: use the proper API call; second best: use PostCommand, which provides very limited programmatic control and may require user interaction; hardest: simulate the user interaction.
    Correction: of course it is possible, man!
    Look here:
    http://thebuildingcoder.typepad.com/blog/2013/07/create-a-filled-region-to-use-as-a-mask.html
    You should have started by looking in the help file for ‘filled’ and ‘region’, then you would have found it.
    So should I :-)
    I hope this helps.
    Cheers, Jeremy.

  3. Arif Hanif Avatar
    Arif Hanif

    I went through the same steps thanks Jeremy, found that post you refer to. In the process of crating the curve loop for the space.

  4. Dear Arif,
    Very cool!
    Good luck!
    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