Room and Wall Adjacency

Question: How can I obtain the part of a wall that touches a room, especially its face area? The wall can be adjacent to more than one room.

Answer: You will probably find the Room object's Boundary property useful. It returns the boundary of the room in a BoundarySegmentArrayArray object, i.e. an array of boundaries or loops. A room may have more than one boundary loop if its floor surface contains holes, which is why this returns an array of arrays. Each boundary consists of a BoundarySegmentArray item, which is a list of BoundarySegment elements. Each such element represents a segment of a room boundary and has a property Element, which provides access to the element that is responsible for producing this boundary segment. That means that from the room, you can figure out what part of its boundary is generated by which wall. If you then go to each of these bounding walls, you can ask it for further information to discover the surface area that it generates adjacent to the given room.

The use and analysis of the Room Boundary property is demonstrated by the RoomViewer SDK sample.

I implemented a new external command class CmdRoomWallAdjacency to demonstrate the principle. It analyses the currently selected rooms in the model, or all rooms if none have been selected. For each room, it iterates over all its boundary segments and lists the bounding elements and their adjacent length, i.e. the length of the curve that they generate which forms part of the room boundary. This may well be less than the total length of the element. For all wall elements, its total length and its total area are also listed. The total area of a wall is queried from the built-in parameter HOST_AREA_COMPUTED, as discussed in our early post on selecting all walls.

We have already discussed more complex ways of calculating areas for 2D and 3D polygons and their transformations directly from the underlying wall geometry, instead of relying on the black box result provided by the built-in parameter. The plan topology object provided by the Revit API may also be useful in this context.

Here is the implementation of the command mainline:

Application app = commandData.Application;
Document doc = app.ActiveDocument;
 
List<Element> rooms = new List<Element>();
if( !Util.GetSelectedElementsOrAll(
  rooms, doc, typeof( Room ) ) )
{
  Selection sel = doc.Selection;
  message = ( 0 < sel.Elements.Size )
    ? "Please select some room elements."
    : "No room elements found.";
  return CmdResult.Failed;
}
foreach( Room room in rooms )
{
  DetermineAdjacentElementLengthsAndWallAreas(
    room );
}
return CmdResult.Failed;

It retrieves the currently selected rooms in the model, or all rooms if none have been selected, using the utility method GetSelectedElementsOrAll, and then iterates over each wall, applying the worker method DetermineAdjacentElementLengthsAndWallAreas to it. Here is the implementation of the worker method:

void DetermineAdjacentElementLengthsAndWallAreas(
  Room room )
{
  BoundarySegmentArrayArray boundaries
    = room.Boundary;
 
  int n = boundaries.Size;
 
  Debug.Print(
    "{0} has {1} boundar{2}{3}",
    Util.ElementDescription( room ),
    n, Util.PluralSuffixY( n ),
    Util.DotOrColon( n ) );
 
  int iBoundary = 0, iSegment;
 
  foreach( BoundarySegmentArray b in boundaries )
  {
    ++iBoundary;
    iSegment = 0;
    foreach( BoundarySegment s in b )
    {
      ++iSegment;
      Element neighbour = s.Element;
      Curve curve = s.Curve;
      double length = curve.Length;
 
      Debug.Print(
        "  Neighbour {0}:{1} {2} has {3}"
        + " feet adjacent to room.",
        iBoundary, iSegment,
        Util.ElementDescription( neighbour ),
        Util.RealString( length ) );
 
      if( neighbour is Wall )
      {
        Wall wall = neighbour as Wall;
 
        Parameter p = wall.get_Parameter(
          BuiltInParameter.HOST_AREA_COMPUTED );
 
        double area = p.AsDouble();
 
        LocationCurve lc
          = wall.Location as LocationCurve;
 
        double wallLength = lc.Curve.Length;
 
        Debug.Print(
          "    This wall has a total length"
          + " and area of {0} feet and {1}"
          + " square feet.",
          Util.RealString( wallLength ),
          Util.RealString( area ) );
      }
    }
  }
}

It retrieves the room boundaries, prints a report on their number and the room identity, iterates over the boundaries and their segments, and checks each segment for the adjacent generating elements. If the neighbouring element is a wall, its total length and area are also listed.

Here is the result of running this command on a simple square house containing one single room:

Rooms <127284 Room 1> has 1 boundary:
  Neighbour 1:1 Walls <127248 Generic - 200mm> has 22.31 feet adjacent to room.
    This wall has a total length and area of 22.97 feet and 283.65 square feet.
  Neighbour 1:2 Walls <127249 Generic - 200mm> has 12.47 feet adjacent to room.
    This wall has a total length and area of 13.12 feet and 172.22 square feet.
  Neighbour 1:3 Walls <127250 Generic - 200mm> has 22.31 feet adjacent to room.
    This wall has a total length and area of 22.97 feet and 301.39 square feet.
  Neighbour 1:4 Walls <127251 Generic - 200mm> has 12.47 feet adjacent to room.
    This wall has a total length and area of 13.12 feet and 163.61 square feet.

The reason for the varying surfaces reported for the walls is due to the way their connections and intersections in the corners of the house have been generated by Revit.

Here is the output when a room is bounded by some walls that extend out beyond the room, so that only part of them are actually adjacent to it:

Rooms <128358 Room 2> has 1 boundary:
  Neighbour 1:1 Walls <128146 Generic - 200mm> has 5.91 feet adjacent to room.
    This wall has a total length and area of 42.65 feet and 1119.45 square feet.
  Neighbour 1:2 Walls <128348 Generic - 200mm> has 4.27 feet adjacent to room.
    This wall has a total length and area of 4.92 feet and 111.94 square feet.
  Neighbour 1:3 Walls <128302 Generic - 200mm> has 5.91 feet adjacent to room.
    This wall has a total length and area of 19.69 feet and 499.45 square feet.
  Neighbour 1:4 Walls <128256 Generic - 200mm> has 4.27 feet adjacent to room.
    This wall has a total length and area of 9.84 feet and 241.11 square feet.

As you can see, the room boundary curve only reports the adjacent length, whereas the total wall length for some of the walls is much larger.

To determine the adjacent surface area is trivial if the wall has a constant height, since in that case it is a constant factor multiplied by the wall length. If the height varies, it becomes more complex.

Many thanks to Massimiliano Revelli of Informatica System srl for raising this issue.