Files
2026-02-21 16:31:24 +08:00

273 lines
11 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Diagnostics;
using Autodesk.Revit.DB;
namespace ShrlAlgoToolkit.RevitCore.Assists
{
public class GeometryAssist
{
/// <summary>
/// 获取多个元素的合并包围盒
/// </summary>
/// <param name="elements">元素集合</param>
/// <returns>合并后的 BoundingBoxXYZ如果集合为空或无有效包围盒则返回 null</returns>
public static BoundingBoxXYZ GetCombinedBoundingBox(IEnumerable<Element> elements)
{
if (elements == null) return null;
double minX = double.MaxValue;
double minY = double.MaxValue;
double minZ = double.MaxValue;
double maxX = double.MinValue;
double maxY = double.MinValue;
double maxZ = double.MinValue;
bool hasValidBox = false;
foreach (var elem in elements)
{
// 传入 null 表示获取模型坐标系下的包围盒
BoundingBoxXYZ box = elem.get_BoundingBox(null);
// 注意:有些元素(如某些基准面或未放置的族实例)可能返回 null
if (box == null) continue;
// 比较并更新最小值
if (box.Min.X < minX) minX = box.Min.X;
if (box.Min.Y < minY) minY = box.Min.Y;
if (box.Min.Z < minZ) minZ = box.Min.Z;
// 比较并更新最大值
if (box.Max.X > maxX) maxX = box.Max.X;
if (box.Max.Y > maxY) maxY = box.Max.Y;
if (box.Max.Z > maxZ) maxZ = box.Max.Z;
hasValidBox = true;
}
if (!hasValidBox) return null;
// 构造新的包围盒
BoundingBoxXYZ combinedBox = new BoundingBoxXYZ();
combinedBox.Min = new XYZ(minX, minY, minZ);
combinedBox.Max = new XYZ(maxX, maxY, maxZ);
return combinedBox;
}
/// <summary>
/// 对曲线列表进行排序,使其正确排序和定向,形成线串。
/// </summary>
public static void SortCurvesContiguous(IList<Curve> curves, bool debug_output = false)
{
const double inch = 1.0 / 12.0;
const double sixteenth = inch / 16.0;
int n = curves.Count;
for (int i = 0; i < n; ++i)
{
Curve curve = curves[i];
XYZ endPoint = curve.GetEndPoint(1);
XYZ p;
// 查找起点 = 终点的曲线
bool found = (i + 1 >= n);
for (int j = i + 1; j < n; ++j)
{
p = curves[j].GetEndPoint(0);
// 如果匹配 end->start、
// 这是下一条曲线
if (sixteenth > p.DistanceTo(endPoint))
{
if (debug_output)
{
Debug.Print("{0} 起始点, 换成 {1}", j, i + 1);
}
if (i + 1 != j)
{
Curve tmp = curves[i + 1];
curves[i + 1] = curves[j];
curves[j] = tmp;
}
found = true;
break;
}
p = curves[j].GetEndPoint(1);
// 如果有匹配结果 end->end、
// 反转下一条曲线
if (sixteenth > p.DistanceTo(endPoint))
{
if (i + 1 == j)
{
if (debug_output)
{
Debug.Print("{0} 终点, 反向 {1}", j, i + 1);
}
curves[i + 1] = curves[j].CreateReversed();
}
else
{
if (debug_output)
{
Debug.Print("{0} 终点, 倒换 {1}", j, i + 1);
}
Curve tmp = curves[i + 1];
curves[i + 1] = curves[j].CreateReversed();
curves[j] = tmp;
}
found = true;
break;
}
}
if (!found)
{
throw new Exception("SortCurvesContiguous:" + " 非连续输入曲线");
}
}
}
public static void CreateDirectShape(
Document doc,
List<XYZ> vertices,
List<Face> faces,
ElementId graphicsStyleId,
ElementId categoryId,
string appGuid,
string shapeName
)
{
var nFaces = 0;
var builder = new TessellatedShapeBuilder { LogString = shapeName };
var corners = new List<XYZ>();
builder.OpenConnectedFaceSet(false);
foreach (var f in faces)
{
builder.LogInteger = nFaces;
//if (corners.Capacity < f.Indices.Count)//设置corner的Capacity值减少内存开销
//{
// corners = new ErrorModels<XYZ>(f.Indices.Count);
//}
//corners.Clear();
//foreach (Index i in f.Indices)
//{
// if (i.vertex >= vertices.Count)
// {
// return;
// }
// corners.Add(vertices[i.vertex]);
//}
try
{
builder.AddFace(new TessellatedFace(corners, ElementId.InvalidElementId));
}
catch (Autodesk.Revit.Exceptions.ArgumentException) { }
}
builder.CloseConnectedFaceSet();
builder.Target = TessellatedShapeBuilderTarget.AnyGeometry;
builder.Fallback = TessellatedShapeBuilderFallback.Mesh;
builder.GraphicsStyleId = graphicsStyleId;
builder.Build();
var ds = DirectShape.CreateElement(doc, categoryId);
ds.ApplicationId = appGuid;
ds.ApplicationDataId = shapeName;
ds.SetShape(builder.GetBuildResult().GetGeometricalObjects());
ds.Name = shapeName;
}
//public BRepBuilder CreateGeo()
//{
//BRepBuilder bRepBuilder=new BRepBuilder(BRepType.Solid);
//BRepBuilderEdgeGeometry.Create();
//BRepBuilderSurfaceGeometry.Create();
//BRepBuilderSurfaceGeometry.CreateNURBSSurface();
//}
public static void CreateDirectShapeFromCylinder(Document doc)
{
// Naming convention for faces and edges: we assume that x is to the left and pointing down, y is horizontal and pointing to the right, z is up
var brepBuilder = new BRepBuilder(BRepType.Solid);
// The surfaces of the four faces.
var basis = new Frame(new XYZ(50, -100, 0), new XYZ(0, 1, 0), new XYZ(-1, 0, 0), new XYZ(0, 0, 1));
var cylSurf = CylindricalSurface.Create(basis, 50);
var top = Plane.CreateByNormalAndOrigin(new XYZ(0, 0, 1), new XYZ(0, 0, 100)); // normal points outside the cylinder
var bottom = Plane.CreateByNormalAndOrigin(new XYZ(0, 0, 1), new XYZ(0, 0, 0)); // normal points inside the cylinder
// Add the four faces
var frontCylFaceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(cylSurf, null), false);
var backCylFaceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(cylSurf, null), false);
var topFaceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(top, null), false);
var bottomFaceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(bottom, null), true);
// Geometry for the four semi-circular edges and two vertical linear edges
var frontEdgeBottom = BRepBuilderEdgeGeometry.Create(Arc.Create(new XYZ(0, -100, 0), new XYZ(100, -100, 0), new XYZ(50, -50, 0)));
var backEdgeBottom = BRepBuilderEdgeGeometry.Create(Arc.Create(new XYZ(100, -100, 0), new XYZ(0, -100, 0), new XYZ(50, -150, 0)));
var frontEdgeTop = BRepBuilderEdgeGeometry.Create(Arc.Create(new XYZ(0, -100, 100), new XYZ(100, -100, 100), new XYZ(50, -50, 100)));
var backEdgeTop = BRepBuilderEdgeGeometry.Create(Arc.Create(new XYZ(0, -100, 100), new XYZ(100, -100, 100), new XYZ(50, -150, 100)));
var linearEdgeFront = BRepBuilderEdgeGeometry.Create(new XYZ(100, -100, 0), new XYZ(100, -100, 100));
var linearEdgeBack = BRepBuilderEdgeGeometry.Create(new XYZ(0, -100, 0), new XYZ(0, -100, 100));
// Add the six edges
var frontEdgeBottomId = brepBuilder.AddEdge(frontEdgeBottom);
var frontEdgeTopId = brepBuilder.AddEdge(frontEdgeTop);
var linearEdgeFrontId = brepBuilder.AddEdge(linearEdgeFront);
var linearEdgeBackId = brepBuilder.AddEdge(linearEdgeBack);
var backEdgeBottomId = brepBuilder.AddEdge(backEdgeBottom);
var backEdgeTopId = brepBuilder.AddEdge(backEdgeTop);
// Loops of the four faces
var loopIdTop = brepBuilder.AddLoop(topFaceId);
var loopIdBottom = brepBuilder.AddLoop(bottomFaceId);
var loopIdFront = brepBuilder.AddLoop(frontCylFaceId);
var loopIdBack = brepBuilder.AddLoop(backCylFaceId);
// Add coedges for the loop of the front face
brepBuilder.AddCoEdge(loopIdFront, linearEdgeBackId, false);
brepBuilder.AddCoEdge(loopIdFront, frontEdgeTopId, false);
brepBuilder.AddCoEdge(loopIdFront, linearEdgeFrontId, true);
brepBuilder.AddCoEdge(loopIdFront, frontEdgeBottomId, true);
brepBuilder.FinishLoop(loopIdFront);
brepBuilder.FinishFace(frontCylFaceId);
// Add coedges for the loop of the back face
brepBuilder.AddCoEdge(loopIdBack, linearEdgeBackId, true);
brepBuilder.AddCoEdge(loopIdBack, backEdgeBottomId, true);
brepBuilder.AddCoEdge(loopIdBack, linearEdgeFrontId, false);
brepBuilder.AddCoEdge(loopIdBack, backEdgeTopId, true);
brepBuilder.FinishLoop(loopIdBack);
brepBuilder.FinishFace(backCylFaceId);
// Add coedges for the loop of the top face
brepBuilder.AddCoEdge(loopIdTop, backEdgeTopId, false);
brepBuilder.AddCoEdge(loopIdTop, frontEdgeTopId, true);
brepBuilder.FinishLoop(loopIdTop);
brepBuilder.FinishFace(topFaceId);
// Add coedges for the loop of the bottom face
brepBuilder.AddCoEdge(loopIdBottom, frontEdgeBottomId, false);
brepBuilder.AddCoEdge(loopIdBottom, backEdgeBottomId, false);
brepBuilder.FinishLoop(loopIdBottom);
brepBuilder.FinishFace(bottomFaceId);
brepBuilder.Finish();
using var tr = new Transaction(doc, "Create a DirectShape");
tr.Start();
var ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
ds.SetShape(brepBuilder);
tr.Commit();
}
}
}