Revit API: 光线追踪 ReferenceIntersector

您所在的位置:网站首页 revit光线追踪怎么用 Revit API: 光线追踪 ReferenceIntersector

Revit API: 光线追踪 ReferenceIntersector

2024-07-17 13:45| 来源: 网络整理| 查看: 265

前言

作为三维软件的使用者,有的时候是很好奇它们是如何做到选中一个物体的。可以想象一下,电脑屏幕是一个二维的,当我们点击鼠标的时候的,它就像是射出一条光线,然后选中的物体就是和这条光线相交的。 Revit 提供了一个类 ReferenceIntersector,它的功能和上面的描述非常接近,不过它并不局限于从屏幕出发的一条的光线,可以是任意的位置和方向。

ReferenceIntersector

两行代码就可以找到从 center 出发,rayDirection 为方向的射线遇到的第一个构件。实际上,难点其实也不在于这个类怎么用。而是光线的位置如何设定,怎么理解 Revit 的坐标系。

ReferenceIntersector refIntersector = new ReferenceIntersector(view3D); ReferenceWithContext referenceWithContext = refIntersector.FindNearest(center, rayDirection); 例子

来源:官方文档,有修改,该例子没有 Transaction,无法运行,需要加上。 这个例子的目的是假设有一个天窗(skyLight),从这个天窗向下找到的第一个楼板。 步骤:

通过 UI 选中天窗 计算从天窗到楼板的距离,并用一条线表示 用 ModelCurve 来表示这条线

效果: 在这里插入图片描述

通过 UI 选中天窗

关键逻辑:

得到选中的物体,revit.Application.ActiveUIDocument.Selection.GetElementIds(); 代码仅仅处理你只选中一个天窗的情况,不要多选; 天窗的类型是 FamilyInstance,它自身的类型是 BuiltInCategory.OST_Windows,它的 host 必须是 BuiltInCategory.OST_Roofs。

代码:

Document doc = revit.Application.ActiveUIDocument.Document; ICollection 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; } 计算从天窗到楼板的距离,并用一条线表示

这个逻辑被包在函数 CalculateLineAboveFloor 里。

第一步,找到第一个不是模板的三维视图。

// Find a 3D view to use for the ReferenceIntersector constructor FilteredElementCollector collector = new FilteredElementCollector(doc); Func isNotTemplate = v3 => !(v3.IsTemplate); View3D view3D = collector.OfClass(typeof(View3D)).Cast().First(isNotTemplate);

第二步,计算光线的方向和位置。起点是天窗几何位置的中心,方向是垂直向下。

BoundingBoxXYZ box = skylight.get_BoundingBox(view3D); XYZ center = box.Min.Add(box.Max).Multiply(0.5); 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; Line result = Line.CreateBound(center, intersection); 用 ModelCurve 来表示这条线

步骤: 第一步,创建 SketchPlane 。

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); 整体代码: public class RayProjection : IExternalCommand { public Result Execute(ExternalCommandData revit, ref string message, ElementSet elements) { Document doc = revit.Application.ActiveUIDocument.Document; ICollection 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; } Transaction tran = new Transaction(doc, "Projection"); tran.Start(); // 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); tran.Commit(); // Show a message with the length value TaskDialog.Show("Distance", "Distance to floor: " + String.Format("{0:f2}", line.Length)); return Result.Succeeded; } /// /// Determines the line segment that connects the skylight to the nearest floor. /// /// The line segment. private Line CalculateLineAboveFloor(Document doc, FamilyInstance skylight) { // Find a 3D view to use for the ReferenceIntersector constructor FilteredElementCollector collector = new FilteredElementCollector(doc); Func isNotTemplate = v3 => !(v3.IsTemplate); View3D view3D = collector.OfClass(typeof(View3D)).Cast().First(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; } }


【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3