194 lines
7.4 KiB
C#
194 lines
7.4 KiB
C#
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>
|
||
/// Revit执行命令
|
||
/// </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;
|
||
}
|
||
}
|
||
} |