Using an Intersection Filter for Linked Elements

Intersecting elements has always been a hot topic, cf. various previous discussions on 3D Booleans, cutting and joining elements; intersecting with elements in a linked file is even more challenging.

Happily, the Revit API provides tools to support that as well:

Rebar intersecting column

Intersecting Linked Elements with Current Project Ones

Yongyu @wlmsingle Deng raised and answered an interesting question in the Revit API discussion forum thread on how to use the ElementIntersectsElementFilter from the RevitLinkInstance to retrieve MEP elements from a linked file and intersect them with structural elements in the current project:

Question: I need to get the MEP elements from a linked file and intersect them with some structural elements in the current project.

I succeeded with this using the ElementIntersectsElementFilter and the ElementIntersectsSolidFilter.

The filters are both works fine if there is no transform (i.e., identity) between the RevitLinkInstance and the current project.

However, if the RevitLinkInstance is moved or rotated after importing, the calculation result of the filters is incorrect.

Are there any tricks to solve a case like that?

For example, a method for passing a transform to the filter?

If not, please share a good algorithm to get the intersection result between two solid elements.

Answer: Retrieve solids from the elements of interest and use the SolidUtils.CreateTransformed method on them.

Response: Thanks a lot!

You inspired me to develop a new solution.

If there is a non-identity transform on the RevitLinkInstance, the key point to use the ElementIntersectsSolidFilter correctly is simply to transform the element in the current project to the linked file project coordinate system:

Here is a slightly cleaned up version of Yongyu Deng's code that I added to The Building Coder samples release 2019.0.144.4:

  /// <summary>
  /// Collect the element ids of all elements in the 
  /// linked documents intersecting the given element.
  /// </summary>
  /// <param name="e">Target element</param>
  /// <param name="links">Linked documents</param>
  /// <param name="ids">Return intersecting element ids</param>
  /// <returns>Number of intersecting elements found</returns>
  int GetIntersectingLinkedElementIds( 
    Element e,
    IList<RevitLinkInstance> links,
    List<ElementId> ids )
  {
    int count = ids.Count();
    Solid solid = GetSolid( e );

    foreachRevitLinkInstance i in links )
    {
      // GetTransform or GetTotalTransform or what?
      Transform transform = i.GetTransform(); 
      if( !transform.AlmostEqual( Transform.Identity) )
      {
        solid = SolidUtils.CreateTransformed( 
          solid, transform.Inverse );
      }
      ElementIntersectsSolidFilter filter 
        = new ElementIntersectsSolidFilter( solid );

      FilteredElementCollector intersecting 
        = new FilteredElementCollector( i.GetLinkDocument() )
          .WherePasses( filter );

      ids.AddRange( intersecting.ToElementIds() );
    }
    return ids.Count - count;
  }

Retrieving Rebars Intersecting a Structural Element

Another Revit API discussion forum thread deals with getting all associated rebars that attach to a structural element:

Question: How can I get all associated rebars which attach to a structural element such as a column by picking that?

Answer: Picking an element is described in the Revit API getting started material and also demonstrated in The Building Coder samples.

For instance, in the latter, you can check out the various element selection utility methods and examine how they are used in the sample commands.

Once you have found a usage pattern that you like in some sample command, search for the description of it in The Building Coder blog.

Once you have picked your structural element, use a filtered element collector to retrieve the intersecting rebar.

Set it up to retrieve rebar elements only, and add a filter for the column solid:

The Building Coder samples includes some examples of using a solid intersection filter, e.g., the GetInstancesIntersectingElement method showing how to retrieve family instances intersecting a given BIM element:

   /// <summary>
  /// Retrieve all family instances intersecting a
  /// given BIM element, e.g. all columns 
  /// intersecting a wall.
  /// </summary>
  void GetInstancesIntersectingElement( Element e )
  {
    Document doc = e.Document;

    Solid solid = e.get_Geometry( new Options() )
      .OfType<Solid>()
      .Where<Solid>( s => null != s && !s.Edges.IsEmpty )
      .FirstOrDefault();

    FilteredElementCollector intersectingInstances
      = new FilteredElementCollector( doc )
        .OfClass( typeofFamilyInstance ) )
        .WherePasses( new ElementIntersectsSolidFilter(
          solid ) );

    int n1 = intersectingInstances.Count<Element>();

    intersectingInstances
      = new FilteredElementCollector( doc )
        .OfClass( typeofFamilyInstance ) )
        .WherePasses( new ElementIntersectsElementFilter(
          e ) );

    int n = intersectingInstances.Count<Element>();

    Debug.Assert( n.Equals( n1 ),
      "expected solid intersection to equal element intersection" );

    string result = string.Format(
      "{0} family instance{1} intersect{2} the "
      + "selected element {3}{4}",
      n, Util.PluralSuffix( n ),
      ( 1 == n ? "s" : "" ),
      Util.ElementDescription( e ),
      Util.DotOrColon( n ) );

    string id_list = 0 == n
      ? string.Empty
      : string.Join( ", ",
          intersectingInstances
            .Select<Elementstring>(
              x => x.Id.IntegerValue.ToString() ) )
        + ".";

    Util.InfoMsg2( result, id_list );
  }

  /// <summary>
  /// Retrieve all beam family instances 
  /// intersecting two columns, cf.
  /// http://forums.autodesk.com/t5/revit-api/check-to-see-if-beam-exists/m-p/6223562
  /// </summary>
  FilteredElementCollector
    GetBeamsIntersectingTwoColumns(
      Element column1,
      Element column2 )
  {
    Document doc = column1.Document;

    if( column2.Document.GetHashCode() != doc.GetHashCode() )
    {
      throw new ArgumentException(
        "Expected two columns from same document." );
    }

    FilteredElementCollector intersectingStructuralFramingElements
      = new FilteredElementCollector( doc )
        .OfClass( typeofFamilyInstance ) )
        .OfCategory( BuiltInCategory.OST_StructuralFraming )
        .WherePasses( new ElementIntersectsElementFilter( column1 ) )
        .WherePasses( new ElementIntersectsElementFilter( column2 ) );

    int n = intersectingStructuralFramingElements.Count<Element>();

    string result = string.Format(
      "{0} structural framing family instance{1} "
      + "intersect{2} the two beams{3}",
      n, Util.PluralSuffix( n ),
      ( 1 == n ? "s" : "" ),
      Util.DotOrColon( n ) );

    string id_list = 0 == n
      ? string.Empty
      : string.Join( ", ",
          intersectingStructuralFramingElements
            .Select<Elementstring>(
              x => x.Id.IntegerValue.ToString() ) )
        + ".";

    Util.InfoMsg2( result, id_list );

    return intersectingStructuralFramingElements;
  }