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:
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!
Jeremy,
Is there a way to create a filled region for room boundary?
Posted by: Arif Hanif | May 27, 2015 at 18:07
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.
Posted by: Jeremy Tammik | May 28, 2015 at 01:15
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.
Posted by: Arif Hanif | May 28, 2015 at 11:04
Dear Arif,
Very cool!
Good luck!
Cheers, Jeremy.
Posted by: Jeremy Tammik | May 29, 2015 at 04:45