### Filter for Touching Beams Using Solid Intersection

I discussed how I created my first sphere in the Revit API and displayed it using the analysis visualisation framework AVF. Now let's make more serious use of it.

As I mentioned, the transient solids created by the geometry creation utility class can be used to set up geometrical proximity filters.

This exploration was prompted by the following query:

Question: I would like to programmatically retrieve all touching beams, regardless of whether they are officially connected or not.

The user should pick one single beam, and all other beams touching it should be selected, recursively.

How can I achieve this, please?

Answer: First let's look at the case you are not interested in :-)

If the beams are properly connected, you can use the LocationCurve ElementsAtJoin property to determine the connected beams at each end of the first, manually selected one, and iterate through the connection elements as demonstrated by the TraverseSystem SDK sample for duct systems using the MEP connector manager.

If the beams are not properly connected, but just touching, as you say, you can use the ElementIntersectsSolidFilter instead. Such as filter detects elements within a given geometrical space defined by solid, which may come from the BIM model or be transient, i.e. generated in memory on the fly.

To recursively iterate through the beam system requires creating a new solid and a new filter for each beam you find, since each solid has its own location in space. If you are interested in beams touching anywhere, you could create an extruded shape matching the beam shape and detect all other beams that intersect it or come close to it anywhere at all along the length of the beam. If you are only interested in beams touching at the end points, you could set up a simple sphere at each end of the beam.

This process could be repeated recursively for all touching beams encountered. Of course you would have to skip beams that you have already found, or you would run into an infinite loop.

Here is the algorithm I am thinking of:

• Maintain three lists of beams:
• The current ones being analysed.
• The neighbours of the current ones.
• Select a starting beam, making it the current one.
• While the set of current beams is non-empty, repeat the following:
• Add the current to the list of visited beams.
• Clear the list of neighbours.
• For each of the current beams, add all beams touching it to the list of neighbours.
• Make the neighbours found in the previous step the new set of current beams.

Finding all touching neighbours for a given beam is implement in the method AddConnectedElements, which sets up a sphere at each end of the beam, creates a proximity detector based on it using an ElementIntersectsSolidFilter, and adds all beams retrieved by that, excluding the beams already visited and neighbours already found.

I already presented the sphere creation method CreateSphereAt.

Here is the AddConnectedElements method and its helper method AddElementsIntersectingSphereAt to detect touching elements:

```  /// <summary>
/// Determine all neighbouring elements connected
/// to the current element 'e', skipping all
/// previously visited ones.
/// </summary>
List<ElementId> neighbours,
XYZ p,
List<ElementId> visited,
Document doc )
{
Solid sphere = CreateSphereAt(

ElementIntersectsSolidFilter intersectSphere
= new ElementIntersectsSolidFilter( sphere );

FilteredElementCollector collector
= new FilteredElementCollector( doc )
.WhereElementIsCurveDriven() // we work with the location curve
.OfCategory( _bic )
.Excluding( visited.Union<ElementId>(
neighbours ).ToList<ElementId>() )
.WherePasses( intersectSphere );

}

/// <summary>
/// Determine all neighbouring elements close to
/// the two ends of the current element 'e',
/// skipping all previously visited ones.
/// </summary>
List<ElementId> neighbours,
Element e,
List<ElementId> visited )
{
Location loc = e.Location;

Debug.Print( string.Format(
"current element {0} has location {1}",
ElementDescription( e ),
null == loc ? "<null>" : loc.GetType().Name ) );

LocationCurve lc = loc as LocationCurve;

if( null != lc )
{
Document doc = e.Document;

Curve c = lc.Curve;

XYZ p = c.get_EndPoint( 0 );
XYZ q = c.get_EndPoint( 1 );

neighbours, p, visited, doc );

neighbours, q, visited, doc );
}
}
```

The lists of neighbours and visited elements are implemented as collections of ElementId instead of Element instances, since I do not trust the .NET comparison methods to work properly on Elements. Also, this aligns nicely with the Exclude method on the filtered element collector, which takes a collection of element ids as an argument.

I initially tried calling Exclude twice with separate calls for the lists of already visited elements and neighbours, both of which should be eliminated. In the first call, though, the list of neighbours is always empty. Providing an empty list to the Exclude method throws an exception. I could have called the Exclude method conditionally, checking first that the list of neighbours is non-empty. However, I found it more succinct to combine the two lists into a single one for the call.

With the AddConnectedElements method in place, the algorithm described above translates to the following C# implementation of the external command Execute mainline method:

```public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements )
{
UIApplication uiapp = commandData.Application;
UIDocument uidoc = uiapp.ActiveUIDocument;
Application app = uiapp.Application;
CreationApp creapp = app.Create;
Document doc = uidoc.Document;
Selection sel = uidoc.Selection;
Reference r = null;

try
{
r = sel.PickObject(
ObjectType.Element,
}
catch( RvtOperationCanceledException )
{
return Result.Cancelled;
}

// Starting element

Element start = doc.GetElement( r );

// The current elements whose neighbours
// we are seeking

List<ElementId> current = new List<ElementId>();

// List of elements already visited

List<ElementId> visited = new List<ElementId>();

// Continue as long as new connected
// elements are found

List<ElementId> neighbours = new List<ElementId>();

while( 0 < current.Count )
{
// Remember where we have been, add this to
// the result so far, and do not revisit these

// We found no new neighbours yet

neighbours.Clear();

// Search current elements for new connected

foreach( ElementId id in current )
{
Element e = doc.GetElement( id );

neighbours, e, visited );
}

// Done with the current elements, and the
// newly found become the next current ones

current.Clear();
}

foreach( ElementId id in visited )
{
doc.GetElement( id ) );
}
return Result.Succeeded;
}
```

Pretty neat, huh?