Instances in Room and Need for Fuzz

Common tasks include finding instances with a given room, comparing real numbers, and sharing documentation online:

Get Family Instances Within Room

A common task is to get FamilyInstances within a room:

Question: I'm facing a small issue in filtering family instances within the room. A few families do not get filtered; for example, non-solid families are getting excluded.

Family instances in room

How to filter intersected 2D families also?

This my code:

  //Get Family Instance
  public List<FamilyInstance> GetFamilyInstance(
    Document revitDoc,
    Room room)
  {
    //Get Closed Shell
    GeometryElement geoEle = room.ClosedShell;
    GeometryObject geoObject = null;
    //Get Geometry Object From Geometry Element
    foreach (GeometryObject obj in geoEle)
    {
      if (obj is Solid)
      {
        geoObject = obj;
      }
    }

    ElementIntersectsSolidFilter elementIntersectsSolidFilter
      = new ElementIntersectsSolidFilter(geoObject as Solid);

    return new FilteredElementCollector(revitDoc)
      .OfClass(typeof(FamilyInstance))
      .WhereElementIsNotElementType().
      WherePasses(elementIntersectsSolidFilter).
      Cast<FamilyInstance>().
      ToList();
  }

Answer: The ElementIntersectsSolidFilter requires the filtered elements to have solid geometry and be of a category supported by interference checking. Your 2D instances do not fulfil this requirement.

You can try using the family instance Room property like this:

  bool IsInstanceInRoom(FamilyInstance instance, Room room)
  {
    var isInstanceInRoom = (instance.Room != null) 
      && instance.Room.Id == room.Id;
    return isInstanceInRoom;
  }

  public List<FamilyInstance> GetFamilyInstance(
    Document revitDoc,
    Room room)
  {
    var elements = new FilteredElementCollector(revitDoc)
      .OfClass(typeof(FamilyInstance))
      .WhereElementIsNotElementType()
      .Cast<FamilyInstance>()
      .Where(i => IsInstanceInRoom(i, room))
      .ToList();
    return elements;
  }

Another approach is to use the Room.IsPointInRoom predicate and check the family instance location point or constructing some other point based on geometry location. You may need to elevate it slightly off the floor to ensure it will be found within vertical limits of room.

You can also try to use the FromRoom and ToRoom properties. If you are using Phasing, you have to pass in the phase:

  instance.get_Room(phase)
  // or
  instance.get_FroomRoom(phase)
  //or
  instance.get_ToRoom(phase)

Response: So, I need to take a center point of the family instance and then use IsPointInRoom to ensure that it is within the room limits. Can you write some simple code for me?

Answer: The solution for that is very kindly given by EatRevitPoopCad explaining how to determine if a toilet belongs to a room. You could also simply search this forum for IsPointInRoom to find it yourself.

Many thanks to Sam Berk, Richard RPThomas108 Thomas and EatRevitPoopCad for their helpful advice!

Rooms in Linked Models

As Joshua Lumley points out in his comment below, IsPointInRoom works well for rooms in linked models as well; just remember to invert the total transform of the linked instance before doing it.

MEP Components Near Ceilings

Erik Falck Jørgensen adds in his comment on LinkedIn:

Great approach; however, as I see it, we still have the problem with ceilings cutting rooms off and issues capturing (MEP) components without the point option above ceiling level but still inside the constructive room. It would be a great help if either the room or the ceiling element could respect the need for working with elements inside this range.

Answer: Yup. That may require a different approach. Maybe those cases can be addressed by querying the room for its solid using its ClosedShell property and using a full 3D Boolean operation. Or, simpler, implementing a point-in-3D-polyhedron algorithm.

Again, the Need for Fuzz

We take yet another look at fuzz, required in order to deal with comparison of real numbers on digital computers.

The topic came up once again in the Revit API discussion forum thread on unit converted parameter value not matching parsed string value.

To skip the jabber and jump right to a solution, please refer to the StackOverflow discussion on comparing double values in C#.

Question: Strictly speaking, this is not a Revit API issue I'm facing, but after some research I couldn't find the answer here or anywhere else and it's still a challenge related to Revit.

I'm comparing a family parameter value with values in a spreadsheet so I can update the parameter accordingly if they don't match, but I'm getting weird results when comparing them. The snippet below shows how I'm extracting the double value of the WW_Width parameter and converting it to millimetres. Then, I compare it to a parsed string with the exact same value as a double, but I get a false result as to whether or not they match.

  var famPars = doc.FamilyManager.Parameters;
  var famTypes = doc.FamilyManager.Types;

  foreach (FamilyParameter param in famPars)
  {
    foreach (FamilyType famtype in famTypes)
    {
      if (param.Definition.Name == "WW_Width")
      {
        console.ShowBoldMessage("WW_Width");
        console.ShowMessage($"UnitType: {param.Definition.UnitType}");
        console.ShowMessage($"StorageType: {param.StorageType}\n");

        double valueInMM = UnitUtils.ConvertFromInternalUnits(
          (double) famtype.AsDouble(param),
          DisplayUnitType.DUT_MILLIMETERS);
        double parsedString = double.Parse("2448");

        console.ShowMessage($"Value: {valueInMM}");
        console.ShowMessage($"Parsed string: {parsedString}");
        console.ShowMessage($"Values match: {valueInMM == parsedString}");
      }
    }
  }

This is what my console shows as a result; I don't understand why I'm getting a mismatch:

  WW_Width
  UnitType: UT_Length
  StorageType: Double

  Value: 2448
  Parsed string: 2448
  Values match: False

Answer: You need to add some fuzz; you can search The Building Coder for 'fuzz'.

Response: Thanks for the steer in the right direction. I wasn't aware of that issue with doubles/floats. Instead of directly comparing the doubles, I'm subtracting one from the other and checking if the difference is under a certain tolerance (e.g. A - B < 0.001), and that works.

The StackOverflow article on comparing double values in C# explains it well in case anyone faces this issue in the future.

Avoid PDF for On-Screen Reading

I just read a piece of advice that I have not heeded so far, and am unsure whether to heed in the future.

However, I am very glad to be well informed about my bad manners.

The recommendation stands to avoid PDF for on-screen reading. It was voiced a long time ago and apparently still stands, cf. PDF: still unfit for human consumption, 20 years later. Instead, the author recommends using HTML gateway pages instead of PDFs, since gateway pages prevent pdf shock.

I fully agree with the latter, and mostly try to clearly mark links that lead to a PDF so that the unwary reader is well prepared for leaving the world of HTML before following the link.