1052 lines
37 KiB
C#
1052 lines
37 KiB
C#
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
|
||
using Autodesk.Revit.DB;
|
||
using Autodesk.Revit.UI;
|
||
|
||
using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
|
||
|
||
namespace Szmedi.Toolkit.RvExtensions;
|
||
|
||
public static class ElementExtensions
|
||
{
|
||
/// <summary>
|
||
/// 容差
|
||
/// </summary>
|
||
private const double Tolerance = 0.0001;
|
||
/// <summary>
|
||
/// 有类型定义的DirectShape
|
||
/// </summary>
|
||
/// <param name="doc"></param>
|
||
/// <param name="definitionId">定义名称,即是族名称、类型名称(强制一致),创建多个实例时,如果相同,则会合并在一个族中</param>
|
||
/// <param name="category">族类别</param>
|
||
/// <param name="geos">几何</param>
|
||
/// <returns></returns>
|
||
public static DirectShape CreateDirectShapeInstance(this Document doc, string definitionId, BuiltInCategory category, List<GeometryObject> geos)
|
||
{
|
||
DirectShapeLibrary directShapeLibrary = DirectShapeLibrary.GetDirectShapeLibrary(doc);
|
||
ElementId directShapeTypeId = directShapeLibrary.FindDefinitionType(definitionId);
|
||
if (doc.GetElement(directShapeTypeId) == null)
|
||
{
|
||
var directShapeType = DirectShapeType.Create(doc, definitionId, new ElementId(category));
|
||
directShapeTypeId = directShapeType.Id;
|
||
directShapeLibrary.AddDefinitionType(definitionId, directShapeTypeId);
|
||
}
|
||
//else
|
||
//{
|
||
// directShapeLibrary.AddDefinition(definitionId, geos);
|
||
//}
|
||
var shape = DirectShape.CreateElementInstance(doc, directShapeTypeId, new ElementId(category), definitionId, Transform.Identity);
|
||
//shape.SetTypeId(directShapeTypeId);
|
||
shape.SetName(definitionId);
|
||
shape.SetShape(geos);
|
||
return shape;
|
||
}
|
||
/// <summary>
|
||
/// 已经打开的视图中缩放视图,最大化图元
|
||
/// </summary>
|
||
/// <param name="view"></param>
|
||
/// <param name="uidoc"></param>
|
||
/// <param name="elementId"></param>
|
||
/// <param name="extend">扩宽范围</param>
|
||
public static void ZoomElement(this View view, UIDocument uidoc, ElementId elementId, int extend = 2)
|
||
{
|
||
var elem = uidoc.Document.GetElement(elementId);
|
||
|
||
if (!elem.IsValidObject || !elem.IsVisible(view)) return;
|
||
var box = elem.get_BoundingBox(view);
|
||
if (box == null) return;
|
||
box = box.Extend(extend);
|
||
var uiView = uidoc.GetOpenUIViews().FirstOrDefault(v => v.ViewId == uidoc.ActiveGraphicalView.Id);
|
||
uiView?.ZoomAndCenterRectangle(box.Min, box.Max);
|
||
}
|
||
public static bool IsVisible(this Element elem, View view) => FilteredElementCollector.IsViewValidForElementIteration(elem.Document, view.Id)
|
||
&& new FilteredElementCollector(elem.Document, view.Id).ToElementIds().Any(id => id == elem.Id);
|
||
/// <summary>
|
||
/// 无名称定义的DirectShape
|
||
/// </summary>
|
||
/// <param name="doc"></param>
|
||
/// <param name="geos"></param>
|
||
/// <param name="category"></param>
|
||
/// <returns></returns>
|
||
public static DirectShape CreateDirectShape(this Document doc, List<GeometryObject> geos, BuiltInCategory category)
|
||
{
|
||
var shape = DirectShape.CreateElement(doc, new ElementId(category));
|
||
shape.SetShape(geos);
|
||
return shape;
|
||
}
|
||
/// <summary>
|
||
/// 扩展包围盒的大小
|
||
/// </summary>
|
||
/// <param name="boundingBox"></param>
|
||
/// <param name="distance"></param>
|
||
/// <returns></returns>
|
||
public static BoundingBoxXYZ Extend(this BoundingBoxXYZ boundingBox, double distance = 2.0)
|
||
{
|
||
var transform = boundingBox.Transform;
|
||
var max = boundingBox.Max + distance * (transform.BasisX + transform.BasisY + transform.BasisZ);
|
||
var min = boundingBox.Min - distance * (transform.BasisX + transform.BasisY + transform.BasisZ);
|
||
return new BoundingBoxXYZ() { Max = max, Min = min, Transform = transform };
|
||
}
|
||
private static List<T> GetGeoObjects<T>(this GeometryObject geoObject)
|
||
where T : GeometryObject
|
||
{
|
||
List<T> geometryObjects = [];
|
||
|
||
if (geoObject is Solid instSolid)
|
||
{
|
||
if (typeof(T) == typeof(Solid))
|
||
{
|
||
geometryObjects.Add(instSolid as T);
|
||
}
|
||
if (typeof(T) == typeof(Face) && instSolid.Faces.Size > 0)
|
||
{
|
||
geometryObjects.AddRange(instSolid.Faces.Cast<T>());
|
||
}
|
||
else if (typeof(T) == typeof(Edge) && instSolid.Edges.Size > 0)
|
||
{
|
||
geometryObjects.AddRange(instSolid.Edges.Cast<T>());
|
||
}
|
||
}
|
||
|
||
return geometryObjects;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 主次分支连接
|
||
/// </summary>
|
||
/// <param name="mainMepCurve">主管</param>
|
||
/// <param name="branchMepCurve">分支</param>
|
||
public static void ConnectTo(this MEPCurve mainMepCurve, MEPCurve branchMepCurve)
|
||
{
|
||
var document = mainMepCurve.Document;
|
||
var unboundBranchCurve = branchMepCurve.GetLocCurve();
|
||
unboundBranchCurve.MakeUnbound();
|
||
var unboundMainCurve = mainMepCurve.GetLocCurve();
|
||
unboundMainCurve.MakeUnbound();
|
||
var intersection = unboundBranchCurve.IntersectionPoint(unboundMainCurve) ?? throw new InvalidOperationException("主次分支管线不存在交点");
|
||
var line = mainMepCurve.GetLocCurve() as Line;
|
||
//根据交点在管线内的位置,判断是形成弯头还是三通
|
||
if (line.IsInsideEx(intersection, 0.5)) //三通
|
||
{
|
||
var newMainMEPCurveId = mainMepCurve.BreakByPoint(intersection);
|
||
var newMainMEPCurve = document.GetElement(newMainMEPCurveId) as MEPCurve;
|
||
var mainConnectors = ConnectorExtensions.GetNearestConnectors(mainMepCurve, newMainMEPCurve);
|
||
var branchConnector = branchMepCurve.GetConnectors(true).GetNearestConnector(intersection);
|
||
|
||
/* 项目“Szmedi.Toolkit (net48)”的未合并的更改
|
||
在此之前:
|
||
branchConnector.ConnectByTee(mainConnectors[0], mainConnectors[1]);
|
||
在此之后:
|
||
branchConnector.ConnectByTee(mainConnectors[0], mainConnectors[1]);
|
||
*/
|
||
branchConnector.ConnectByTee(mainConnectors[0], mainConnectors[1]);
|
||
}
|
||
else //弯头
|
||
{
|
||
var conn = mainMepCurve.GetConnectors(true).GetNearestConnector(intersection);
|
||
var conn2 = branchMepCurve.GetConnectors(true).GetNearestConnector(intersection);
|
||
|
||
/* 项目“Szmedi.Toolkit (net48)”的未合并的更改
|
||
在此之前:
|
||
conn.ConnectByFitting(conn2);
|
||
在此之后:
|
||
conn.ConnectByFitting(conn2);
|
||
*/
|
||
conn.ConnectByFitting(conn2);
|
||
}
|
||
}
|
||
public static HermiteSpline ConverToHermiteSpline(this PolyLine polyLine)
|
||
{
|
||
var list = polyLine.GetCoordinates();
|
||
return HermiteSpline.Create(list, false);
|
||
}
|
||
|
||
/// <summary>
|
||
|
||
/* 项目“Szmedi.Toolkit (net48)”的未合并的更改
|
||
在此之前:
|
||
/// 分支连接
|
||
/// </summary>
|
||
/// <param name="branch"></param>
|
||
/// <param name="connector"></param>
|
||
/// <param name="connector1"></param>
|
||
/// <returns></returns>
|
||
public static FamilyInstance ConnectByTee(this Connector branch, Connector connector, Connector connector1)
|
||
{
|
||
doc doc = branch.Owner.doc;
|
||
//获取垂直主管的向量
|
||
XYZ normal = connector.CoordinateSystem.BasisZ.CrossProduct(branch.CoordinateSystem.BasisZ);
|
||
XYZ vertical = connector.CoordinateSystem.BasisZ.CrossProduct(normal);
|
||
|
||
Line mainLine = Line.CreateUnbound(connector.Origin, connector.CoordinateSystem.BasisZ);
|
||
Line branchLine = Line.CreateUnbound(branch.Origin, branch.CoordinateSystem.BasisZ);
|
||
//分支和主管的角度
|
||
double cos = branch.CoordinateSystem.BasisZ.DotProduct(connector.CoordinateSystem.BasisZ);
|
||
double angle = Math.Acos(cos);
|
||
//主管和分支管的交点
|
||
XYZ intersect = mainLine.IntersectionPoint(branchLine);
|
||
//垂直主管的分支管,为了创建默认三通
|
||
Line verticalLine = Line.CreateBound(intersect, intersect + (3.0 * vertical));
|
||
//延长管线到交点处
|
||
branch.Owner.SetLocationCurve((branch.Owner.GetLocCurve() as Line).ExtendLine(intersect));
|
||
connector.Owner.SetLocationCurve((connector.Owner.GetLocCurve() as Line).ExtendLine(intersect));
|
||
connector1.Owner.SetLocationCurve((connector1.Owner.GetLocCurve() as Line).ExtendLine(intersect));
|
||
//复制支管创建垂直主管的管线
|
||
ElementId verticalMEPCurveId = ElementTransformUtils.CopyElement(doc, branch.Owner.Id, XYZ.Zero).FirstOrDefault();
|
||
MEPCurve verticalMEPCurve = doc.GetElement(verticalMEPCurveId) as MEPCurve;
|
||
verticalMEPCurve.SetLocationCurve(verticalLine);
|
||
//获取垂直管的连接件
|
||
Connector tempBranchConnector = verticalMEPCurve.GetConnectors().GetNearestConnector(intersect);
|
||
//创建三通
|
||
FamilyInstance tee = doc.Create.NewTeeFitting(connector, connector1, tempBranchConnector);
|
||
if (tempBranchConnector.GetConnectedConnector().Owner.Id != tee.Id)
|
||
{
|
||
doc.Delete(tempBranchConnector.GetConnectedConnector().Owner.Id);
|
||
}
|
||
|
||
doc.Delete(verticalMEPCurveId);
|
||
//三通中未连接的连接件即分支连接件
|
||
Connector teeBranchConn = tee.GetConnectors(true).OfType<Connector>().FirstOrDefault();
|
||
try
|
||
{
|
||
//设置三通角度
|
||
teeBranchConn!.Angle = Math.PI - angle;
|
||
}
|
||
catch (Autodesk.Revit.Exceptions.InvalidOperationException)
|
||
{
|
||
Debug.WriteLine("无法修改管线角度");
|
||
}
|
||
|
||
doc.Regenerate();
|
||
//Connector branchFarConn = branch.Owner.GetConnectors().GetFarthestConnector(intersect);
|
||
//Line newBranch = Line.CreateBound(branchFarConn.Origin, teeBranchConn.Origin);
|
||
Line l = branch.Owner.GetLocCurve() as Line;
|
||
Line newBranchLine = l.ExtendLine(teeBranchConn!.Origin);
|
||
branch.Owner.SetLocationCurve(newBranchLine);
|
||
//Line line = branch.Owner.GetLocCurve() as Line;
|
||
//if (newBranch.Direction.IsAlmostEqualTo(line.Direction))
|
||
//{
|
||
// (branch.Owner.Location as LocationCurve).Curve = newBranch;
|
||
//}
|
||
//else if (newBranch.Direction.IsAlmostEqualTo(-line.Direction))
|
||
//{
|
||
// (branch.Owner.Location as LocationCurve).Curve = newBranch.CreateReversed();
|
||
//}
|
||
Connector newBranchConn = GetNearestConnector(branch.Owner.GetConnectors(), intersect);
|
||
newBranchConn.ConnectByFitting(teeBranchConn);
|
||
return tee;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新建视图并设置剖切框
|
||
在此之后:
|
||
/// 新建视图并设置剖切框
|
||
*/
|
||
|
||
/// <summary>
|
||
/// 新建视图并设置剖切框
|
||
/// </summary>
|
||
/// <param name="doc"></param>
|
||
/// <param name="view3D">复制的三维视图</param>
|
||
/// <param name="elementIds"></param>
|
||
/// <returns></returns>
|
||
public static View3D CreateSectionBox(this View3D view3D, IEnumerable<ElementId> elementIds, bool inCurrentView)
|
||
{
|
||
var doc = view3D.Document;
|
||
XYZ extendXyz = new(1, 1, 1);
|
||
BoundingBoxXYZ b;
|
||
if (elementIds.Count() == 1)
|
||
{
|
||
var e = doc.GetElement(elementIds.FirstOrDefault());
|
||
b = e.get_BoundingBox(view3D);
|
||
var max = b.Max + extendXyz;
|
||
var min = b.Min - extendXyz;
|
||
b = new BoundingBoxXYZ { Max = max, Min = min };
|
||
}
|
||
else
|
||
{
|
||
var f = doc.GetElement(elementIds.FirstOrDefault(elem => doc.GetElement(elem).get_BoundingBox(view3D) != null));
|
||
b = f.get_BoundingBox(view3D);
|
||
|
||
var maxX = b.Max.X;
|
||
var maxY = b.Max.Y;
|
||
var maxZ = b.Max.Z;
|
||
var minX = b.Min.X;
|
||
var minY = b.Min.Y;
|
||
var minZ = b.Min.Z;
|
||
foreach (var elementId in elementIds)
|
||
{
|
||
var e = doc.GetElement(elementId);
|
||
var bb = e.get_BoundingBox(doc.ActiveView);
|
||
|
||
if (bb.Max.X >= maxX)
|
||
{
|
||
maxX = bb.Max.X;
|
||
}
|
||
|
||
if (bb.Max.Y >= maxY)
|
||
{
|
||
maxY = bb.Max.Y;
|
||
}
|
||
|
||
if (bb.Max.Z >= maxZ)
|
||
{
|
||
maxZ = bb.Max.Z;
|
||
}
|
||
|
||
if (bb.Min.X <= minX)
|
||
{
|
||
minX = bb.Min.X;
|
||
}
|
||
|
||
if (bb.Min.Y <= minY)
|
||
{
|
||
minY = bb.Min.Y;
|
||
}
|
||
|
||
if (bb.Min.Z <= minZ)
|
||
{
|
||
minZ = bb.Min.Z;
|
||
}
|
||
}
|
||
|
||
var max = new XYZ(maxX, maxY, maxZ) + extendXyz;
|
||
var min = new XYZ(minX, minY, minZ) - extendXyz;
|
||
b = new BoundingBoxXYZ { Max = max, Min = min };
|
||
}
|
||
|
||
if (!view3D.IsSectionBoxActive)
|
||
{
|
||
view3D.IsSectionBoxActive = true;
|
||
}
|
||
|
||
if (inCurrentView)
|
||
{
|
||
view3D.SetSectionBox(b);
|
||
return view3D;
|
||
}
|
||
|
||
var viewCopyId = view3D.Duplicate(ViewDuplicateOption.WithDetailing);
|
||
var viewCopy = doc.GetElement(viewCopyId) as View3D;
|
||
viewCopy?.SetSectionBox(b);
|
||
return viewCopy;
|
||
}
|
||
|
||
public static double Distance(this Line line1, Line line2)
|
||
{
|
||
double distance;
|
||
|
||
var v1 = line1.Direction;
|
||
var p1 = line1.GetEndPoint(0);
|
||
var v2 = line2.Direction;
|
||
var p2 = line2.GetEndPoint(0);
|
||
|
||
var a = p1.X - p2.X;
|
||
var b = p1.Y - p2.Y;
|
||
var c = p1.Z - p2.Z;
|
||
var pq = v1.X * v2.Y - v2.X * v1.Y;
|
||
var qr = v1.Y * v2.Z - v2.Y * v1.Z;
|
||
var rp = v1.Z * v2.X - v2.Z * v1.X;
|
||
var dev = Math.Sqrt(Math.Pow(qr, 2) + Math.Pow(rp, 2) + Math.Pow(pq, 2));
|
||
|
||
if (dev < 1e-9)
|
||
{
|
||
var bp = b * v1.X;
|
||
var br = b * v1.Z;
|
||
var cp = c * v1.X;
|
||
var cq = c * v1.Y;
|
||
var aq = a * v1.Y;
|
||
var ar = a * v1.Z;
|
||
distance =
|
||
Math.Sqrt(Math.Pow(br - cq, 2) + Math.Pow(cp - ar, 2) + Math.Pow(aq - bp, 2))
|
||
/ Math.Sqrt(Math.Pow(v1.X, 2) + Math.Pow(v1.Y, 2) + Math.Pow(v1.Z, 2));
|
||
}
|
||
else
|
||
{
|
||
distance = Math.Abs((qr * a + rp * b + pq * c) / dev);
|
||
}
|
||
|
||
return distance;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 延长或缩短直线
|
||
/// </summary>
|
||
/// <param name="line"></param>
|
||
/// <param name="endPoint"></param>
|
||
/// <param name="startSection">保留部分,为空时,保留长的部分</param>
|
||
/// <returns></returns>
|
||
public static Line ExtendLine(this Line line, XYZ endPoint, bool? startSection = null)
|
||
{
|
||
var v = endPoint - line.GetEndPoint(0);
|
||
var v2 = endPoint - line.GetEndPoint(1);
|
||
//点刚好是直线端点
|
||
if (v.IsZeroLength() || v2.IsZeroLength())
|
||
{
|
||
return line;
|
||
}
|
||
//必须平行才能延伸
|
||
|
||
if (v.IsParallelTo(v2))
|
||
{
|
||
//点在直线外,同方向
|
||
if (line.Direction.DotProduct(v) > 0.0 && line.Direction.DotProduct(v2) > 0.0)
|
||
{
|
||
return Line.CreateBound(line.GetEndPoint(0), endPoint);
|
||
}
|
||
|
||
//点在直线外,反方向
|
||
if (line.Direction.DotProduct(v) < 0.0 && line.Direction.DotProduct(v2) < 0.0)
|
||
{
|
||
return Line.CreateBound(endPoint, line.GetEndPoint(1));
|
||
}
|
||
|
||
//点在直线内
|
||
return v.DotProduct(v2) < 0.0
|
||
? startSection == null
|
||
? v.GetLength() >= v2.GetLength()
|
||
? Line.CreateBound(line.GetEndPoint(0), endPoint)
|
||
: Line.CreateBound(endPoint, line.GetEndPoint(1))
|
||
: startSection == true
|
||
? Line.CreateBound(line.GetEndPoint(0), endPoint)
|
||
: Line.CreateBound(endPoint, line.GetEndPoint(1))
|
||
: null;
|
||
}
|
||
|
||
return line;
|
||
//throw new InvalidOperationException("不可延伸到该点");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 延长直线
|
||
/// </summary>
|
||
/// <param name="line"></param>
|
||
/// <param name="distance"></param>
|
||
/// <param name="isInteractive">是否双向延申,单向延长终点</param>
|
||
/// <returns></returns>
|
||
public static Line ExtendLine(this Line line, double distance, bool isInteractive = false)
|
||
{
|
||
var endPoint = line.GetEndPoint(1) + line.Direction * distance;
|
||
var startPoint = line.GetEndPoint(0);
|
||
if (isInteractive)
|
||
{
|
||
startPoint += line.Direction.Negate() * distance;
|
||
}
|
||
|
||
return Line.CreateBound(startPoint, endPoint);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 点拍平到xy平面
|
||
/// </summary>
|
||
/// <param name="xyz"></param>
|
||
/// <returns></returns>
|
||
public static XYZ Flatten(this XYZ xyz)
|
||
{
|
||
return xyz.Subtract(XYZ.BasisZ * xyz.Z);
|
||
}
|
||
/// <summary>
|
||
/// 点拍平到标高平面
|
||
/// </summary>
|
||
/// <param name="xyz"></param>
|
||
/// <returns></returns>
|
||
public static XYZ FlattenTo(this XYZ xyz, Level level)
|
||
{
|
||
var z = level.Elevation;
|
||
|
||
return xyz.Add(XYZ.BasisZ * (z - xyz.Z));
|
||
}
|
||
/// <summary>
|
||
/// 获取块参照实例
|
||
/// </summary>
|
||
/// <param name="dwg"></param>
|
||
/// <param name="reference"></param>
|
||
/// <returns></returns>
|
||
public static List<GeometryInstance> GetBlockInstance(this ImportInstance dwg, Reference reference)
|
||
{
|
||
var geoElem = dwg.get_Geometry(new Options());
|
||
|
||
var geoObj = dwg.GetGeometryObjectFromReference(reference);
|
||
GeometryInstance referBlock = null;
|
||
foreach (var obj in geoElem)
|
||
{
|
||
if (obj is GeometryInstance dwgGeo) //dwg几何实例
|
||
{
|
||
foreach (var block in dwgGeo.SymbolGeometry) //块
|
||
{
|
||
if (block is GeometryInstance ins)
|
||
{
|
||
if (ins.GetHashCode() == geoObj.GetHashCode())
|
||
{
|
||
referBlock = ins;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
foreach (var subBlock in ins.SymbolGeometry) //嵌套块/子块
|
||
{
|
||
if (subBlock is GeometryInstance subIns && subIns.GetHashCode() == geoObj.GetHashCode())
|
||
{
|
||
referBlock = ins;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (referBlock == null)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
List<GeometryInstance> blocks = [];
|
||
foreach (var obj in geoElem)
|
||
{
|
||
if (obj is GeometryInstance dwgGeo)
|
||
{
|
||
foreach (var block in dwgGeo.SymbolGeometry) //图元
|
||
{
|
||
if (block is GeometryInstance ins && ins.Symbol.Name == referBlock.Symbol.Name)
|
||
{
|
||
blocks.Add(ins);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return blocks;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 得到元素所有连接件
|
||
/// </summary>
|
||
/// <param name="element"></param>
|
||
/// <param name="isUnused">获取未使用的连接件,否则获取全部</param>
|
||
/// <returns></returns>
|
||
public static ConnectorSet GetConnectors(this Element element, bool isUnused = false)
|
||
{
|
||
var connectorSet = new ConnectorSet();
|
||
switch (element)
|
||
{
|
||
case FamilyInstance familyInstance:
|
||
connectorSet = isUnused
|
||
? familyInstance.MEPModel.ConnectorManager?.UnusedConnectors
|
||
: familyInstance.MEPModel?.ConnectorManager?.Connectors;
|
||
break;
|
||
case MEPCurve mepCurve:
|
||
connectorSet = isUnused ? mepCurve.ConnectorManager.UnusedConnectors : mepCurve.ConnectorManager.Connectors;
|
||
break;
|
||
case FabricationPart fabricationPart:
|
||
connectorSet = isUnused ? fabricationPart.ConnectorManager.UnusedConnectors : fabricationPart.ConnectorManager.Connectors;
|
||
break;
|
||
}
|
||
|
||
return connectorSet;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取dwg的曲线
|
||
/// </summary>
|
||
/// <param name="importInstance"></param>
|
||
/// <returns></returns>
|
||
public static List<Curve> GetCurves(this ImportInstance importInstance)
|
||
{
|
||
List<Curve> curves = [];
|
||
var geoElem = importInstance.get_Geometry(new Options());
|
||
foreach (var geomObj in geoElem)
|
||
{
|
||
var geomInstance = geomObj as GeometryInstance;
|
||
|
||
if (geomInstance != null)
|
||
{
|
||
foreach (var instObj in geomInstance.GetInstanceGeometry())
|
||
{
|
||
//polyLine拆成线
|
||
if (instObj is PolyLine line)
|
||
{
|
||
var poly = line.GetCoordinates();
|
||
for (var i = 0; i < poly.Count - 1; i++)
|
||
{
|
||
var l = Line.CreateBound(poly[i], poly[i + 1]);
|
||
curves.Add(l);
|
||
}
|
||
}
|
||
|
||
if (instObj is Arc arc)
|
||
{
|
||
curves.Add(arc);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return curves;
|
||
}
|
||
public static ElementId GetElementId(this BuiltInCategory builtInCategory, Document doc)
|
||
{
|
||
return Category.GetCategory(doc, builtInCategory).Id;
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 获取顶层的几何(非嵌套)
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="elem"></param>
|
||
/// <returns></returns>
|
||
public static List<GeometryObject> GetGeometryObjects(this Element elem)
|
||
{
|
||
List<GeometryObject> geometryObjects = [];
|
||
Options option = new() { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine };
|
||
var geometry = elem.get_Geometry(option);
|
||
|
||
foreach (var geomObj in geometry)
|
||
{
|
||
geometryObjects.Add(geomObj);
|
||
}
|
||
|
||
return geometryObjects;
|
||
}
|
||
/// <summary>
|
||
/// 获取几何对象
|
||
/// </summary>
|
||
/// <typeparam name="T">对应类型的几何对象,如Solid、Face、Edge</typeparam>
|
||
/// <param name="elem">获取的元素</param>
|
||
/// <returns></returns>
|
||
public static List<T> GetGeometryObjects<T>(this Element elem)
|
||
where T : GeometryObject
|
||
{
|
||
List<T> geometryObjects = [];
|
||
Options option = new() { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine };
|
||
var geometry = elem.get_Geometry(option);
|
||
|
||
foreach (var geomObj in geometry)
|
||
{
|
||
if (geomObj is GeometryInstance geomInstance)
|
||
{
|
||
var usesSymbolGeometry = elem is FamilyInstance instance && !instance.HasModifiedGeometry();
|
||
var geometryElement = usesSymbolGeometry ? geomInstance.GetSymbolGeometry() : geomInstance.GetInstanceGeometry();
|
||
|
||
foreach (var geoObject in geometryElement)
|
||
{
|
||
geometryObjects.AddRange(geoObject.GetGeoObjects<T>());
|
||
}
|
||
}
|
||
|
||
geometryObjects.AddRange(geomObj.GetGeoObjects<T>());
|
||
}
|
||
|
||
return geometryObjects;
|
||
}
|
||
/// <summary>
|
||
/// 获取参考所在的图层名称
|
||
/// </summary>
|
||
/// <param name="dwg"></param>
|
||
/// <param name="reference"></param>
|
||
/// <returns></returns>
|
||
public static string GetLayerName(this ImportInstance dwg, Reference reference)
|
||
{
|
||
var obj = dwg.GetGeometryObjectFromReference(reference);
|
||
var style = dwg.Document.GetElement(obj.GraphicsStyleId) as GraphicsStyle;
|
||
return style?.GraphicsStyleCategory.Name;
|
||
}
|
||
/// <summary>
|
||
/// 获取标高
|
||
/// </summary>
|
||
/// <param name="model"></param>
|
||
/// <returns></returns>
|
||
public static ElementId GetLevelId(this Element model)
|
||
{
|
||
// 定义需要检查的参数列表
|
||
var parametersToCheck = new BuiltInParameter[]
|
||
{
|
||
BuiltInParameter.WALL_BASE_CONSTRAINT, // 墙
|
||
BuiltInParameter.SCHEDULE_BASE_LEVEL_PARAM, // 柱子标高
|
||
BuiltInParameter.INSTANCE_REFERENCE_LEVEL_PARAM, // 梁标高
|
||
BuiltInParameter.STAIRS_BASE_LEVEL_PARAM, // 楼梯标高
|
||
BuiltInParameter.INSTANCE_ELEVATION_PARAM, // 族实例明细表标高
|
||
BuiltInParameter.ROOF_CONSTRAINT_LEVEL_PARAM,//屋顶
|
||
BuiltInParameter.INSTANCE_SCHEDULE_ONLY_LEVEL_PARAM,// 族实例明细表标高
|
||
BuiltInParameter.RBS_START_LEVEL_PARAM// 管线标高
|
||
};
|
||
// 依次检查每个参数
|
||
foreach (var param in parametersToCheck)
|
||
{
|
||
var baseLevelId = model.get_Parameter(param)?.AsElementId();
|
||
if (baseLevelId != ElementId.InvalidElementId && baseLevelId != null)
|
||
{
|
||
return baseLevelId;
|
||
}
|
||
}
|
||
|
||
//最后检查楼板或族基准标高
|
||
return model.LevelId;
|
||
}
|
||
/// <summary>
|
||
/// 获取元素定位线
|
||
/// </summary>
|
||
/// <param name="instance">以曲线定位的实例</param>
|
||
/// <returns></returns>
|
||
public static Curve GetLocCurve(this Element instance)
|
||
{
|
||
return instance.Location is LocationCurve lc ? lc.Curve : null;
|
||
}
|
||
/// <summary>
|
||
/// 获取元素定位点
|
||
/// </summary>
|
||
/// <param name="instance">以点定位的实例</param>
|
||
/// <returns></returns>
|
||
public static XYZ GetLocXYZ(this Element instance)
|
||
{
|
||
return instance.Location is LocationPoint lc ? lc.Point : null;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 同专业类型的最近连接件
|
||
/// </summary>
|
||
/// <param name="element1"></param>
|
||
/// <param name="element2"></param>
|
||
/// <param name="isUnused"></param>
|
||
/// <returns></returns>
|
||
public static List<Connector> GetNearestDomainConnectors(Element element1, Element element2, bool isUnused = true)
|
||
{
|
||
List<Connector> list = [];
|
||
Connector conn = null;
|
||
Connector conn2 = null;
|
||
var distance = double.MaxValue;
|
||
foreach (Connector connTemp in element1.GetConnectors(isUnused))
|
||
{
|
||
foreach (Connector connTemp2 in element2.GetConnectors(isUnused))
|
||
{
|
||
var tempDistance = connTemp.Origin.DistanceTo(connTemp2.Origin);
|
||
if (connTemp.Domain == connTemp2.Domain && distance > tempDistance)
|
||
{
|
||
distance = tempDistance;
|
||
conn = connTemp;
|
||
conn2 = connTemp2;
|
||
}
|
||
}
|
||
}
|
||
if (conn != null)
|
||
{
|
||
list.Add(conn);
|
||
}
|
||
if (conn2 != null)
|
||
{
|
||
list.Add(conn2);
|
||
}
|
||
return list;
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 计算两条线的交点
|
||
/// </summary>
|
||
/// <param name="curve1"></param>
|
||
/// <param name="curve2"></param>
|
||
/// <returns></returns>
|
||
public static XYZ IntersectionPoint(this Curve curve1, Curve curve2)
|
||
{
|
||
var comparisonR = curve1.Intersect(curve2, out var intersectionR);
|
||
XYZ intersectionResult = null;
|
||
if (SetComparisonResult.Disjoint != comparisonR) // Disjoint无交点
|
||
{
|
||
try
|
||
{
|
||
if (intersectionR != null && !intersectionR.IsEmpty)
|
||
{
|
||
intersectionResult = intersectionR.get_Item(0).XYZPoint;
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// ignored
|
||
}
|
||
}
|
||
|
||
return intersectionResult;
|
||
}
|
||
public static XYZ IntersectPoint(this Plane plane, Line line)
|
||
{
|
||
var startPoint = line.Origin;
|
||
|
||
var projectPoint = plane.ProjectOnto(startPoint);
|
||
if (Math.Abs(line.Direction.DotProduct(plane.Normal)) < 10E-8) //是否平行
|
||
{
|
||
return null;
|
||
}
|
||
|
||
var length = startPoint.DistanceTo(projectPoint);
|
||
if (length < 10E-8) //不平行,但刚好是Origin,即为交点
|
||
{
|
||
return projectPoint;
|
||
}
|
||
|
||
var vector = projectPoint - startPoint;
|
||
//var per = Line.CreateBound(startPoint, projectPoint);//垂线
|
||
if (vector.IsParallelTo(line.Direction))
|
||
{
|
||
return projectPoint;
|
||
}
|
||
|
||
var cos = plane.Normal.DotProduct(line.Direction);
|
||
if (cos < 0)
|
||
{
|
||
cos = plane.Normal.Negate().DotProduct(line.Direction);
|
||
}
|
||
|
||
var distance = length / cos;
|
||
return vector.Normalize().DotProduct(line.Direction) < 0
|
||
? startPoint - line.Direction * distance
|
||
: startPoint + line.Direction * distance;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 点是否在直线范围内
|
||
/// </summary>
|
||
/// <param name="line"></param>
|
||
/// <param name="point"></param>
|
||
/// <param name="endParameter">两边端点距离</param>
|
||
/// <returns></returns>
|
||
public static bool IsInsideEx(this Line line, XYZ point, double endParameter = 0.0)
|
||
{
|
||
var flag = line == null;
|
||
if (flag)
|
||
{
|
||
throw new ArgumentNullException(nameof(line));
|
||
}
|
||
|
||
var isBound = line.IsBound;
|
||
bool result;
|
||
if (isBound)
|
||
{
|
||
var d = point.DistanceTo(line.GetEndPoint(0));
|
||
var d2 = point.DistanceTo(line.GetEndPoint(1));
|
||
result = d + d2 - line.Length < 1E-05 && d >= endParameter && d2 >= endParameter;
|
||
}
|
||
else
|
||
{
|
||
result = false;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断直线是否平行或重合
|
||
/// </summary>
|
||
/// <param name="line"></param>
|
||
/// <param name="checkedLine"></param>
|
||
/// <returns></returns>
|
||
public static bool IsParallelTo(this Line line, Line checkedLine)
|
||
{
|
||
var a = line.Direction.CrossProduct(checkedLine.Direction).IsZeroLength();
|
||
var b = line.Direction.CrossProduct(checkedLine.Direction).IsAlmostEqualTo(XYZ.Zero);
|
||
var c = 1 - Math.Abs(line.Direction.DotProduct(checkedLine.Direction)) < Tolerance;
|
||
return a || b || c;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断方向是否平行
|
||
/// </summary>
|
||
/// <param name="direction1"></param>
|
||
/// <param name="direction2"></param>
|
||
/// <returns></returns>
|
||
public static bool IsParallelTo(this XYZ direction1, XYZ direction2)
|
||
{
|
||
return direction1.CrossProduct(direction2).IsZeroLength() || direction1.CrossProduct(direction2).IsAlmostEqualTo(XYZ.Zero);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断直线是否垂直
|
||
/// </summary>
|
||
/// <param name="line"></param>
|
||
/// <param name="checkedLine"></param>
|
||
/// <param name="tolerance"></param>
|
||
/// <returns></returns>
|
||
public static bool IsPerpendicularTo(this Line line, Line checkedLine, double tolerance = Tolerance)
|
||
{
|
||
return Math.Abs(line.Direction.DotProduct(checkedLine.Direction)) < tolerance; //点乘满足交换律不影响计算结果
|
||
}
|
||
|
||
/// <summary>
|
||
/// 直线在平面上的投影
|
||
/// </summary>
|
||
/// <param name="plane"></param>
|
||
/// <param name="line"></param>
|
||
/// <returns></returns>
|
||
public static Line ProjectOnto(this Plane plane, Line line)
|
||
{
|
||
return Line.CreateBound(plane.ProjectOnto(line.GetEndPoint(0)), plane.ProjectOnto(line.GetEndPoint(1)));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 点在几何面的投影
|
||
/// </summary>
|
||
/// <param name="plane"></param>
|
||
/// <param name="p"></param>
|
||
/// <returns></returns>
|
||
public static XYZ ProjectOnto(this PlanarFace plane, XYZ p)
|
||
{
|
||
var d = plane.SignedDistanceTo(p, out _);
|
||
var q = p - d * plane.FaceNormal;
|
||
|
||
return q;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 点在参照平面上的投影
|
||
/// </summary>
|
||
/// <param name="plane"></param>
|
||
/// <param name="p"></param>
|
||
/// <returns></returns>
|
||
public static XYZ ProjectOnto(this Plane plane, XYZ p)
|
||
{
|
||
var d = plane.SignedDistanceTo(p, out _);
|
||
var q = p - d * plane.Normal;
|
||
|
||
return q;
|
||
}
|
||
public static void SetBorderColor(this Element element, Color color, View view = null)
|
||
{
|
||
var doc = element.Document;
|
||
|
||
view ??= doc.ActiveView;
|
||
var overrideGraphicSettings = view.GetElementOverrides(element.Id);
|
||
var pat = new FilteredElementCollector(doc)
|
||
.OfClass(typeof(FillPatternElement))
|
||
.ToElements()
|
||
.Cast<FillPatternElement>()
|
||
.Where(pattern =>
|
||
{
|
||
var fill = pattern.GetFillPattern();
|
||
return fill.Target == FillPatternTarget.Drafting
|
||
&& fill.HostOrientation == FillPatternHostOrientation.ToView
|
||
&& fill.IsSolidFill;
|
||
})
|
||
.FirstOrDefault();
|
||
overrideGraphicSettings.SetProjectionLineColor(color);
|
||
overrideGraphicSettings.SetCutLineColor(color);
|
||
view.SetElementOverrides(element.Id, overrideGraphicSettings);
|
||
|
||
}
|
||
public static void SetFillColor(this Element element, Color color, View view = null)
|
||
{
|
||
var doc = element.Document;
|
||
|
||
view ??= doc.ActiveView;
|
||
var overrideGraphicSettings = view.GetElementOverrides(element.Id);
|
||
var pat = new FilteredElementCollector(doc)
|
||
.OfClass(typeof(FillPatternElement))
|
||
.ToElements()
|
||
.Cast<FillPatternElement>()
|
||
.Where(pattern =>
|
||
{
|
||
var fill = pattern.GetFillPattern();
|
||
return fill.Target == FillPatternTarget.Drafting
|
||
&& fill.HostOrientation == FillPatternHostOrientation.ToView
|
||
&& fill.IsSolidFill;
|
||
})
|
||
.FirstOrDefault();
|
||
#if REVIT2018
|
||
overrideGraphicSettings.SetProjectionFillPatternId(pat.Id);
|
||
overrideGraphicSettings.SetProjectionLineColor(color);
|
||
overrideGraphicSettings.SetCutFillPatternId(pat.Id);
|
||
overrideGraphicSettings.SetCutLineColor(color);
|
||
view.SetElementOverrides(element.Id, overrideGraphicSettings);
|
||
#elif REVIT2020
|
||
|
||
overrideGraphicSettings.SetSurfaceForegroundPatternColor(color);
|
||
overrideGraphicSettings.SetSurfaceForegroundPatternId(pat.Id); //实体填充
|
||
overrideGraphicSettings.SetCutForegroundPatternColor(color);
|
||
overrideGraphicSettings.SetCutForegroundPatternId(pat.Id); //实体填充
|
||
#endif
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置元素定位线
|
||
/// </summary>
|
||
/// <param name="instance"></param>
|
||
/// <param name="curve"></param>
|
||
/// <returns></returns>
|
||
public static void SetLocationCurve(this Element instance, Curve curve)
|
||
{
|
||
if (instance == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(instance));
|
||
}
|
||
|
||
if (curve == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(curve));
|
||
}
|
||
|
||
if (instance.Location is LocationCurve lc)
|
||
{
|
||
try
|
||
{
|
||
lc.Curve = curve;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
throw new InvalidOperationException("无法修改定位线");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 点与几何平面的距离(可正可负)
|
||
/// </summary>
|
||
/// <param name="plane"></param>
|
||
/// <param name="p"></param>
|
||
/// <param name="projectPoint"></param>
|
||
/// <returns></returns>
|
||
public static double SignedDistanceTo(this PlanarFace plane, XYZ p, out XYZ projectPoint)
|
||
{
|
||
var v = p - plane.Origin;
|
||
//垂向距离(可正可负,夹角钝角为负)
|
||
var perp = v.DotProduct(plane.FaceNormal);
|
||
projectPoint = p - perp * plane.FaceNormal;
|
||
return perp;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 点与平面的距离(可正可负)
|
||
/// </summary>
|
||
/// <param name="plane"></param>
|
||
/// <param name="p"></param>
|
||
/// <param name="projectPoint"></param>
|
||
/// <returns></returns>
|
||
public static double SignedDistanceTo(this Plane plane, XYZ p, out XYZ projectPoint)
|
||
{
|
||
var v = p - plane.Origin;
|
||
//垂向距离(可正可负,夹角钝角为负)
|
||
var perp = v.DotProduct(plane.Normal);
|
||
projectPoint = p - perp * plane.Normal;
|
||
return perp;
|
||
}
|
||
public static BuiltInCategory ToBuiltInCategory(this Category category)
|
||
{
|
||
var builtInCategory = (BuiltInCategory)category.Id.IntegerValue;
|
||
|
||
return Enum.IsDefined(typeof(BuiltInCategory), builtInCategory)
|
||
? builtInCategory
|
||
: throw new ArgumentNullException(nameof(category), "不存在该内建类别");
|
||
}
|
||
public static Element ToElement(this ElementId id, Document doc)
|
||
{
|
||
return doc.GetElement(id);
|
||
}
|
||
}
|