We already went into quite a bit of detail analysing the geometry of walls and other elements, but the topic keeps cropping up again anyway. An overview of the preceding posts on this topic is given in the discussion of cylindrical columns. Here is a case handled by Joe Ye which I thought might complement the previous posts on this topic, since it is so simple and minimal.
Question: How can I find the bottom face of a wall?
Answer: We can query the wall for its solid geometry using the Element.Geometry property, which is accessed by the get_Geometry method in C#. From the returned solid, all faces can be accessed. The bottom face of the wall has a normal vector equal to (0,0,-1), and in most cases this is the unique for the bottom face. So we just iterate over all faces, and stop when we find one whose normal vector is vertical and has a negative Z coordinate.
I implemented a new external command named CmdWallBottomFace to demonstrate this. Here is the code of its Execute method. As always, it returns Failed even if it did in fact succeed, so that no changes are registered in the BIM, since the command does nothing to modify the model:
Application app = commandData.Application; Document doc = app.ActiveDocument; string s = "a wall, to retrieve its bottom face"; Wall wall = Util.SelectSingleElementOfType( doc, typeof( Wall ), s ) as Wall; if( null == wall ) { message = "Please select a wall."; } else { Options opt = app.Create.NewGeometryOptions(); GeoElement e = wall.get_Geometry( opt ); foreach( GeometryObject obj in e.Objects ) { Solid solid = obj as Solid; if( null != solid ) { foreach( Face face in solid.Faces ) { PlanarFace pf = face as PlanarFace; if( null != pf ) { if( Util.IsVertical( pf.Normal, _tolerance ) && pf.Normal.Z < 0 ) { Util.InfoMsg( string.Format( "The bottom face area is {0}," + " and its origin is at {1}.", Util.RealString( pf.Area ), Util.PointString( pf.Origin ) ) ); break; } } } } } } return CmdResult.Failed;
When the command is executed and a wall selected, the area and origin point of its bottom face is reported in a message box and in the Visual Studio debug output window:
The bottom face area is 265.82, and its origin is at (30.76,20.57,-9.84).
Here is version 1.1.0.44 of the complete Visual Studio solution including the new command.
Thank you very much Joe for this answer!
Hi Jeremy,
I am using Revit since 7 version , but i never make some programming work in it.
Can you help me?
1.What language i must to use ?
2.What i need to start programming?
3.Some useful links for biginer!
I am interesting in FreeForm programming.
To make Revit do something like this (it was made in Maya (MEL):http://www.flickr.com/photos/daniloarsic/
http://www.flickr.com/photos/danielwidrig
Big Thanks
Posted by: Roman | August 18, 2009 at 00:43
Dear Roman,
I am glad to hear of your interest. Regarding your specific questions:
1. You can use any .NET language. C# or VB is easiest and recommended.
2. VSTA or Visual Studio Tools for Applications is built in to the Revit product. The recommended external development environment is Microsoft Visual Studio. You can also use the Express edition, which is free.
3. The Building Coder blog includes a category named 'Getting Started' which includes many topics that are useful for novice Revit API users. A succinct overview of helpful additional resources for getting started is provided in
http://thebuildingcoder.typepad.com/blog/2009/04/getting-started-with-the-revit-2009-api.html
It is written for the Revit 2009 API, but most of it applies equally well to Revit 2010.
Good luck and cheers, Jeremy.
Posted by: Jeremy Tammik | August 18, 2009 at 02:14
Hi, Jeremy,
I think this way to get bottom is not exact, if the wall has door, It will get two bottom faces which is the wall bottom face splitted by door.
thanks,
Jinshou.
Posted by: A Facebook User | September 05, 2012 at 05:38
Dear Jinshou,
You are absolutely right, and all kinds of exceptional and not so exceptional cases will need special handling for production use.
Above all, you need lots of testing, and automated testing, and an ever-expanding collection of test cases to insure against regression.
Cheers, Jeremy.
Posted by: Jeremy Tammik | September 05, 2012 at 06:07
Hi, Jeremy,
I found that the Revit 2012 API has some problems.Like that if the wall has one door, the bottom face should be split into two bottom faces by door, but by API get wall's solid and get solid's faces, only can get one bottom face, it should be two bottom faces. And if more than one door also can only get one bottom face.
thanks,
Jinshou.
Posted by: A Facebook User | September 24, 2012 at 22:53
Dear Jinshou,
I believe the Revit development team might disagree with you there.
Why should there be two faces? One single face may contain multiple loops, both disconnected outer loops and inner hole loops.
The bottom face of a wall is probably always just one single face, as long as the wall has a flat bottom at all, regardless of any doors it may contain.
That one face may have holes in it, if the door really breaks through the bottom of the wall.
Also, the door may be a little bit up in the wall, off the floor, with no break at all in the bottom face.
Check out that one face and see whether it contains multiple loops.
Cheers, Jeremy.
Posted by: Jeremy Tammik | September 26, 2012 at 03:20
Hi, Jeremy,
I'm sorry, I made a mistake.
As you said, after add a doors into the wall, it still one face, but the face contains multiple loops.
Thanks,
Jinshou.
Posted by: A Facebook User | September 26, 2012 at 22:26
Hi Jeremy,
I am trying to get the bottom face of a Beam (structural Framing). In some beams I am getting bottom face when I use Normal.Z0, it is giving me the required bottom face. Can you explain why this happens? kindly help me.
Here is my code:
//methdo to get the bottom face of element passed to it
public static List GetBottomFace(Element ele, Document doc)
{
try
{
List faces = new List();
if (ele is Floor)
{
IList bottomFaces = HostObjectUtils.GetBottomFaces((Floor)ele);
foreach (Reference refer in bottomFaces)
{
faces.Add((doc.GetElement(refer)).GetGeometryObjectFromReference(refer) as Face);
}
return faces;
}
Options opt = new Options();
opt.DetailLevel = ViewDetailLevel.Fine;
GeometryElement geoEle = ele.get_Geometry(opt);
IEnumerator enu = geoEle.GetEnumerator();
enu.Reset();
while (enu.MoveNext())
{
Solid solid = enu.Current as Solid;
if (null != solid)
{
foreach (Face face in solid.Faces)
{
PlanarFace pf = face as PlanarFace;
if (null != pf)
{
if (pf.Normal.Z < 0)
{
faces.Add(face);
return faces;
}
}
}
}
}
return null;
}
catch
{
throw new Exception();
}
}
Posted by: Dickins John | June 05, 2014 at 00:43