Files

194 lines
7.4 KiB
C#
Raw Permalink Normal View History

2026-02-12 21:29:00 +08:00
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
{
/// <summary>
2026-02-22 20:03:42 +08:00
/// 对齐管线到楼板的命令
2026-02-12 21:29:00 +08:00
/// </summary>
[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<View3D>()
.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<MEPCurve>()
.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;
}
}
/// <summary>
/// 断开管线/风管的所有物理连接
/// </summary>
/// <param name="mepCurve">需要断开的管线</param>
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;
}
}
}