Revit API: 光线追踪 ReferenceIntersector |
您所在的位置:网站首页 › revit光线追踪怎么用 › Revit API: 光线追踪 ReferenceIntersector |
前言
作为三维软件的使用者,有的时候是很好奇它们是如何做到选中一个物体的。可以想象一下,电脑屏幕是一个二维的,当我们点击鼠标的时候的,它就像是射出一条光线,然后选中的物体就是和这条光线相交的。 Revit 提供了一个类 ReferenceIntersector,它的功能和上面的描述非常接近,不过它并不局限于从屏幕出发的一条的光线,可以是任意的位置和方向。 ReferenceIntersector两行代码就可以找到从 center 出发,rayDirection 为方向的射线遇到的第一个构件。实际上,难点其实也不在于这个类怎么用。而是光线的位置如何设定,怎么理解 Revit 的坐标系。 ReferenceIntersector refIntersector = new ReferenceIntersector(view3D); ReferenceWithContext referenceWithContext = refIntersector.FindNearest(center, rayDirection); 例子来源:官方文档,有修改,该例子没有 Transaction,无法运行,需要加上。 这个例子的目的是假设有一个天窗(skyLight),从这个天窗向下找到的第一个楼板。 步骤: 通过 UI 选中天窗 计算从天窗到楼板的距离,并用一条线表示 用 ModelCurve 来表示这条线效果:
关键逻辑: 得到选中的物体,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 |