Wall Face Bounding room

Wall Face Bounding room

harilalmnCBXDY
Enthusiast Enthusiast
313 Views
4 Replies
Message 1 of 5

Wall Face Bounding room

harilalmnCBXDY
Enthusiast
Enthusiast

Hi All,
Wish you all a very Happy new year!

I have this extension method for Room, in which I am trying to return the face of a given wall which is defining the boundary of the room.

 

public static Face GetBoundingFace(this Room room, Wall wall)
{
    if (room == null || wall == null)
        return null;

    Document doc = room.Document;

    try
    {
        SpatialElementGeometryCalculator calculator = new SpatialElementGeometryCalculator(doc);

        SpatialElementGeometryResults results = calculator.CalculateSpatialElementGeometry(room);

        Solid spaceSolid = results.GetGeometry();

        foreach (Face face in spaceSolid.Faces)
        {
            IList<SpatialElementBoundarySubface> spatialElementBoundarySubfaceList = results.GetBoundaryFaceInfo(face);

            if (spatialElementBoundarySubfaceList.Count == 0)
                continue;

            ElementId elementId = spatialElementBoundarySubfaceList[0].SpatialBoundaryElement.HostElementId;
            if (elementId == wall.Id)
            {
                return face;
            }
        }
    }
    catch (Exception ex)
    {
        // Handle or log the exception as needed
        TaskDialog.Show("Error", $"Error getting bounding face: {ex.Message}");
    }
    return null;
}

 

 

However, I get the error saying;

The Reference of the input face is null. If the face was obtained from Element.Geometry, make sure to turn on the option 'ComputeReferences'.

Parameter name: face

What is going wrong?

0 Likes
314 Views
4 Replies
Replies (4)
Message 2 of 5

jeremy_tammik
Alumni
Alumni

Yes. I can see why that error message might be a bit confusing. It does explain the problem, though. At some point in your code, you are querying a face for its associated element id. That element id would be stored in a reference on the face. However, the face just contains the pure geometry information, not the required reference. That is what your error message is saying:

  

  • The Reference of the input face is null. If the face was obtained from Element.Geometry, make sure to turn on the option 'ComputeReferences'.

  

If the face had been obtained from a call to the Element.GetGeometry method, you would have been able to add the ComputeReferences option to the call. However, it is not, so you need to find some other way to obtain the face (that you already have) equipped with the required reference (which is missing). Maybe you can determine the element some other way (is it the wall?), call Element.GetGeometry on it, with ComputeReferences set to true, and iterate over all the geometry that you obtained from that call to find out which of the faces you need. Sorry that I am explaining this so badly. I am in a hurry. Maybe you get the gist anyway. Otherwise, please ask. Good luck.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 3 of 5

harilalmnCBXDY
Enthusiast
Enthusiast

Thanks for the advice @jeremy_tammik . I tried the code below, which strangely, is working for (only) one wall of a rectangular room.

public static Face GetBoundingFace(this Room room, Wall wall)
{
    if (room == null || wall == null)
        return null;

    Document doc = room.Document;

    try
    {
        SpatialElementGeometryCalculator calculator = new SpatialElementGeometryCalculator(doc);
        SpatialElementBoundaryOptions spatialElementBoundaryOptions = calculator.GetOptions();
        spatialElementBoundaryOptions.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish;
        SpatialElementGeometryResults results = calculator.CalculateSpatialElementGeometry(room);

        Options options = new Options();
        options.ComputeReferences = true;

        GeometryElement geometryElement = room.get_Geometry(options);
        Solid roomSolid = null;
        foreach (GeometryObject geomObj in geometryElement)
        {
            if (geomObj is Solid solid)
            {
                roomSolid = solid;
                break;
            }
        }

        foreach (Face face in roomSolid.Faces)
        {
            IList<SpatialElementBoundarySubface> spatialElementBoundarySubfaceList = results.GetBoundaryFaceInfo(face);

            if (spatialElementBoundarySubfaceList.Count == 0)
                continue;

            for (int i = 0; i < spatialElementBoundarySubfaceList.Count; i++)
            {
                ElementId elementId = spatialElementBoundarySubfaceList[i].SpatialBoundaryElement.HostElementId;
                if (elementId == wall.Id)
                {
                    return face;
                }
            }
        }
    }
    catch (Exception ex)
    {
        // Handle or log the exception as needed
        TaskDialog.Show("Error", $"Error getting bounding face: {ex.Message}");
    }
    return null;
}


Just to visualize the two points on the faces of opposite walls, I am trying to draw a ModelCurve connecting the points like this;

public void Run()
{
    Room room = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element, new RoomSelectionFilter(), "Select Room")) as Room;
    List<Wall> walls = room.GetBoundaryWalls();

    Face face1 = room.GetBoundingFace(walls[0]);
    Face face2 = room.GetBoundingFace(walls[2]);

    XYZ p1 = face1.Evaluate(new UV(0.5, 0.5));
    XYZ p2 = face2.Evaluate(new UV(0.5, 0.5));
    
    using (Transaction t = new Transaction(doc, "Visualize Face Center"))
    {
        t.Start();
        // Create a ModelLine from p1 to p2
        Line l = Line.CreateBound(p1, p2);
        ModelCurve modelLine = doc.Create.NewModelCurve(l,doc.ActiveView.SketchPlane);
        t.Commit();
    }
}

 

face1 is picked properly. However, face2 is null...

0 Likes
Message 4 of 5

jeremy_tammik
Alumni
Alumni

Glad to hear that one of the cases works. The GetBoundingFace method is quite expensive to call, so I would avoid calling it twice, for the wall indices 0 and 2. By the way, there is no guarantee that the room boundaries are ordered. Hey, where is the GetBoundaryWalls method defined? It is not provided by the Revit API. You are assuming that walls 0 and 2 are opposite. Is that guaranteed? The Revit API method room.GetBoundarySegments does not guarantee any such thing.

    

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 5 of 5

harilalmnCBXDY
Enthusiast
Enthusiast

I take a very basic approach to get the walls;

public static List<Wall> GetBoundaryWalls(this Room room)
{
    List<Wall> walls = new List<Wall>();
    SpatialElementBoundaryOptions options = new SpatialElementBoundaryOptions();
    options.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish;
    IList<IList<BoundarySegment>> boundaries = room.GetBoundarySegments(options);
    foreach (IList<BoundarySegment> boundary in boundaries)
    {
        foreach (BoundarySegment segment in boundary)
        {
            ElementId elementId = segment.ElementId;
            Element e = room.Document.GetElement(elementId);
            if (e is Wall)
            {
                walls.Add((Wall)e);
            }
        }
    }
    return walls;
}
0 Likes