using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; using Autodesk.Revit.DB.Electrical; using Autodesk.Revit.DB.Plumbing; using Autodesk.Revit.UI; using Autodesk.Revit.UI.Selection; namespace ShrlAlgo.Addin.Test { /// /// Revit执行命令 /// [Transaction(TransactionMode.Manual)] [Regeneration(RegenerationOption.Manual)] public class AlignMEPCurvesToSlabCmd : IExternalCommand { public void AlignMEPCurveToSlab(Document doc, MEPCurve mepCurve, Floor slab) { // 1. 获取管线的定位曲线 LocationCurve locationCurve = mepCurve.Location as LocationCurve; if (locationCurve == null) return; Curve curve = locationCurve.Curve; XYZ startPoint = curve.GetEndPoint(0); XYZ endPoint = curve.GetEndPoint(1); View3D view3D = doc.ActiveView as View3D; if (view3D == null) { // 尝试获取第一个非模板的3D视图 FilteredElementCollector collector = new FilteredElementCollector(doc); view3D = collector.OfClass(typeof(View3D)) .Cast() .FirstOrDefault(v => !v.IsTemplate); } // 注意:实际开发中最好手动指定一个确定的3D视图,这里为了演示简单取了第一个非模板视图 if (view3D == null) return; // 如果没有3D视图无法进行 // 2.2 直接传入 slab.Id,表示只与这个元素做碰撞检测 ReferenceIntersector intersector = new ReferenceIntersector(slab.Id, FindReferenceTarget.Element, view3D); intersector.FindReferencesInRevitLinks = false; var level= doc.GetElement(slab.LevelId) as Level; var offsetInFeet = ((Line)locationCurve.Curve).Origin.Z - level.Elevation; // 3. 计算新的起点和终点 XYZ newStart = GetProjectedPoint(intersector, startPoint, offsetInFeet); XYZ newEnd = GetProjectedPoint(intersector, endPoint, offsetInFeet); if (newStart == null || newEnd == null) return; // 投影失败 // 4. 更新管线位置 using (Transaction t = new Transaction(doc, "管线对齐楼板")) { t.Start(); //if (mepCurve is CableTray) //{ // DisconnectMEPCurve(mepCurve); //} doc.Regenerate(); // 断开连接的逻辑建议加上,避免报错 // 创建新直线 Line newLine = Line.CreateBound(newStart, newEnd); locationCurve.Curve = newLine; t.Commit(); } } // 辅助方法:获取投影后的点 private XYZ GetProjectedPoint(ReferenceIntersector intersector, XYZ point, double offset) { // 从高处向下投射,或者从点的位置向下/向上投射 // 这里假设管线在板上方,向下投射 (-Z) XYZ rayOrigin = new XYZ(point.X, point.Y, point.Z + 100); // 抬高起点确保能射到 XYZ rayDirection = XYZ.BasisZ.Negate(); // 向下 ReferenceWithContext context = intersector.FindNearest(rayOrigin, rayDirection); if (context != null) { Reference refer = context.GetReference(); XYZ intersectionPoint = refer.GlobalPoint; // 关键:新的Z值 = 板表面交点Z + 偏移量 return new XYZ(point.X, point.Y, intersectionPoint.Z + offset); } return null; // 未找到交点 } public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { var uiApp = commandData.Application; var UiDocument = uiApp.ActiveUIDocument; var Document = UiDocument.Document; try { var curves = UiDocument.Selection.PickElementsByRectangle(new MEPCurveFilter(), "请选择需要对齐的管道") .OfType() .ToList(); var reference = UiDocument.Selection.PickObject(ObjectType.Element, new FloorSelectionFilter(), "请选择楼板"); var slab = Document.GetElement(reference); using(TransactionGroup tg=new TransactionGroup(Document, "管线对齐楼板")) { tg.Start(); foreach (var curve in curves) { AlignMEPCurveToSlab(Document, curve, slab as Floor); } tg.Assimilate(); } return Result.Succeeded; } catch (Autodesk.Revit.Exceptions.OperationCanceledException) { // 用户取消选择,正常退出 return Result.Cancelled; } catch (Exception) { throw; } } /// /// 断开管线/风管的所有物理连接 /// /// 需要断开的管线 private void DisconnectMEPCurve(MEPCurve mepCurve) { // 获取连接管理器 ConnectorManager cm = mepCurve.ConnectorManager; // 遍历该管线自带的所有连接器 foreach (Connector connector in cm.Connectors) { // 如果该端口没有连接,则跳过 if (!connector.IsConnected) continue; // 获取该端口连接到的所有“其他连接器” // AllRefs 包含了与之连接的所有对象(包括物理连接和逻辑系统连接) ConnectorSet connectedRefs = connector.AllRefs; foreach (Connector otherConnector in connectedRefs) { // 排除自身(虽然很少见,但为了安全) if (otherConnector.Owner.Id == mepCurve.Id) continue; // 过滤:只断开物理连接(End或Curve类型),保留逻辑连接通常不影响移动, // 但为了防止移动报错,通常建议断开物理连接。 // 这里的判断可以根据需要放宽,直接断开所有也行。 if (otherConnector.ConnectorType == ConnectorType.End || otherConnector.ConnectorType == ConnectorType.Curve || otherConnector.ConnectorType == ConnectorType.Physical) { // 执行断开操作 connector.DisconnectFrom(otherConnector); } } } } } internal class MEPCurveFilter : ISelectionFilter { public bool AllowElement(Element elem) { return elem is MEPCurve; } public bool AllowReference(Reference reference, XYZ position) { return true; } } internal class FloorSelectionFilter : ISelectionFilter { public bool AllowElement(Element elem) { return elem is Floor; } public bool AllowReference(Reference reference, XYZ position) { return true; } } }