Finding geometry by ray projection

Finding geometry by ray projection

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, to find intersections which would not be found in the uncut and uncropped 3D model.

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.

There are two methods to project a ray, both of which take as input the origin of the ray and its direction. 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 return 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:
  • This method finds both References will be found and returned only for elements that are in front of the ray.
  • This method will not return intersections in linked files.
  • This method will not return intersections with elements which are not in the active design option.

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 = revit.Application.Application.Create.NewPlane(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