Finding geometry by ray projection

The ReferenceIntersector class can be used to find elements that are intersected by a given ray.

ReferenceIntersector

This class allows an application to use Revit’s picking tools to find elements and geometry. This class uses a ray from a point in a specified direction to find the geometry that is hit by the ray.

The class intersects 3D geometry only and requires a 3D view on creation. It is possible to use a 3D view which has been cut by a section box, or which has view-specific geometry and graphics options set. The visibility settings on the input view will determine if a particular element is returned (for example, hidden elements will never be returned by this tool, nor will elements whose geometry is outside the section box of the view).

The ReferenceIntersector class supports filtering the output based on element or reference type. The output can be customized based on which constructor is used or by using methods and properties of the class prior to calling a method to perform the ray projection.

There are 4 constructors.

Name

Description

ReferenceIntersector(View3D)

Constructs a ReferenceIntersector which is set to return intersections from all elements and representing all reference target types.

ReferenceIntersector(ElementFilter, FindReferenceTarget, View3D)

Constructs a ReferenceIntersector which is set to return intersections from any element which passes the filter.

ReferenceIntersector(ElementId, FindReferenceTarget, View3D)

Constructs a ReferenceIntersector which is set to return intersections from a single target element only.

ReferenceIntersector(ICollection<ElementId>, FindReferenceTarget, View3D)

Constructs a ReferenceIntersector which is set to return intersections from any of a set of target elements.

The FindReferenceTarget enumeration includes the options: Element, Mesh, Edge, Curve, Face or All.

Finding elements

There are two methods to project a ray, both of which take as input the origin of the ray and its direction. Only references for elements that are in front of the ray will be returned. The Find() method returns a collection of ReferenceWithContext objects which match the ReferenceIntersector’s criteria. This object contains the intersected reference, which can be both the elements and geometric references which intersect the ray. Some element references returned will have a corresponding geometric object which is also intersected (for example, rays passing through openings in walls will intersect the wall and the opening element). If interested only in true physical intersections an application should discard all references whose Reference is of type Element.

The FindNearest() method behaves similarly to the Find() method, but only returns the intersected reference nearest to the ray origin.

The ReferenceWithContext returned includes a proximity parameter. This is the distance between the origin of the ray and the intersection point. An application can use this distance to exclude items too far from the origin for a particular geometric analysis. An application can also use this distance to take on some interesting problems involving analyzing the in place geometry of the model.

Note: These methods will not return intersections with elements which are not in the active design option.

Elements in linked files

The FindReferencesInRevitLinks property offers an option to return element results encountered in Revit Links. If set to false, no Reference to any Element from a Revit Link will be found by the ReferenceIntersector, and all References returned will be to an element in the host document only. If set to true, the results may include both References to Elements in hosts and References to Elements from a link instance.

If a list of target ElementIds is set in the ReferenceIntersector, references will be returned only if the ElementId matches the id of the intersected RevitLinkInstance. If there is a match, any intersecting elements in the link will be returned (their ids will not be compared with the target ids list).

If there is an ElementFilter applied, the elements in the link will be evaluated against the stored ElementFilter. Note that results may not be as expected if the filter applied is geometric (such as a BoundingBox filter or ElementIntersects filter). This is because the filter will be evaluated for linked elements in the coordinates of the linked model, which may not match the coordinates of the elements as they appear in the host model. Also, ElementFilters that accept a Document and/or ElementId as input during their instantiation will not correctly pass elements that appear in the link, because the filter will not be able to match link elements to the filter's criteria.

Find elements near elements

One major use for this tool is to find elements in close proximity to other elements. This allows an application to use the tool as its “eyes” and determine relationships between elements which don’t have a built-in relationship already.

For example, the ray-tracing capability can be used to find columns embedded in walls. As columns and walls don’t maintain a relationship directly, this class allows us to find potential candidates by tracing rays just outside the extents of the wall, and looking for intersections with columns.

Example: Find columns embedded in walls

Measure distances

This class could also be used to measure the vertical distance from a skylight to the nearest floor.

Example: measure with ReferenceIntersector.FindNearest()

Code Region: Measuring Distance using Ray Projection

public class RayProjection : IExternalCommand
{
    public Result Execute(ExternalCommandData revit, ref string message, ElementSet elements)
    {
        Document doc = revit.Application.ActiveUIDocument.Document;

        ICollection<ElementId> selectedIds = revit.Application.ActiveUIDocument.Selection.GetElementIds();

        // If skylight is selected, process it.
        FamilyInstance skylight = null;
        if (selectedIds.Count == 1)
        {
            foreach (ElementId id in selectedIds)
            {
                Element e = doc.GetElement(id);
                if (e is FamilyInstance)
                {
                    FamilyInstance instance = e as FamilyInstance;
                    bool isWindow = (instance.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Windows);
                    bool isHostedByRoof = (instance.Host.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Roofs);

                    if (isWindow && isHostedByRoof)
                    {
                        skylight = instance;
                    }
                }
            }
        }

        if (skylight == null)
        {
            message = "Please select one skylight.";
            return Result.Cancelled;
        }

        // Calculate the height
        Line line = CalculateLineAboveFloor(doc, skylight);

        // Create a model curve to show the distance
        Plane plane = Plane.CreateByNormalAndOrigin(new XYZ(1, 0, 0), line.GetEndPoint(0));
        SketchPlane sketchPlane = SketchPlane.Create(doc, plane);

        ModelCurve curve = doc.Create.NewModelCurve(line, sketchPlane);

        // Show a message with the length value
        TaskDialog.Show("Distance", "Distance to floor: " + String.Format("{0:f2}", line.Length));

        return Result.Succeeded;
    }
       
    /// <summary>
    /// Determines the line segment that connects the skylight to the nearest floor.
    /// </summary>
    /// <returns>The line segment.</returns>
    private Line CalculateLineAboveFloor(Document doc, FamilyInstance skylight)
    {
        // Find a 3D view to use for the ReferenceIntersector constructor
        FilteredElementCollector collector = new FilteredElementCollector(doc);
        Func<View3D, bool> isNotTemplate = v3 => !(v3.IsTemplate);
        View3D view3D = collector.OfClass(typeof(View3D)).Cast<View3D>().First<View3D>(isNotTemplate);

        // Use the center of the skylight bounding box as the start point.
        BoundingBoxXYZ box = skylight.get_BoundingBox(view3D);
        XYZ center = box.Min.Add(box.Max).Multiply(0.5);

        // Project in the negative Z direction down to the floor.
        XYZ rayDirection = new XYZ(0, 0, -1);

        ElementClassFilter filter = new ElementClassFilter(typeof(Floor));

        ReferenceIntersector refIntersector = new ReferenceIntersector(filter, FindReferenceTarget.Face, view3D);
        ReferenceWithContext referenceWithContext = refIntersector.FindNearest(center, rayDirection);

        Reference reference = referenceWithContext.GetReference();
        XYZ intersection = reference.GlobalPoint;
           
        // Create line segment from the start point and intersection point.
        Line result = Line.CreateBound(center, intersection);
        return result;
    }
}

Ray bouncing/analysis

The references returned by ReferenceIntersector.Find() include the intersection point on the geometry. Knowing the intersection point on the face, the face’s material, and the ray direction allows an application to analyze reflection and refraction within the building. The following image demonstrates the use of the intersection point to reflect rays intersected by model elements; model curves were added to represent the path of each ray.

Example: Rays bouncing off intersected faces

Find intersections/collisions

Another use of the ReferenceIntersector class would be to detect intersections (such as beams or pipes) which intersect/interference with the centerline of a given beam or pipe.

Example: Reroute elements around interferences