using System.Collections.Generic; using System.Diagnostics; using Autodesk.Revit.DB; using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; using Sai.Toolkit.Revit.Helpers; namespace Sai.Toolkit.Revit.Assist; public static class SpatialAssist { /// /// 容差 /// private const double VbTolerance = 10E-9; private static List _curvesSorted; /// /// /// /// /// /// private static List GetGeoObjects(this GeometryObject geoObject) where T : GeometryObject { List 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()); } else if (typeof(T) == typeof(Edge) && instSolid.Edges.Size > 0) { geometryObjects.AddRange(instSolid.Edges.Cast()); } } return geometryObjects; } public static XYZ ProjectOnPlane(this XYZ q, Plane plane) { //The projection of a point q = (x, y, z) onto a plane given by a point p = (a, b, c) and a normal n = (d1, e, f) is // q_proj = q - dot(q - p, n) * n var p = plane.Origin; var n = plane.Normal.Normalize(); var qProj = q - n.Multiply(n.DotProduct(q - p)); return qProj; } /// /// 连成线串 /// /// /// private static void SortOpenedContinuousCurves(Curve initCurve, List sourceCurves) { if (sourceCurves == null) { return; } //if (sourceCurves.Count <= 2) //{ // return; //} var start = initCurve.GetEndPoint(0); var end = initCurve.GetEndPoint(1); //curvesSorted.Add(initCurve); sourceCurves.Remove(initCurve); for (var i = sourceCurves.Count - 1; i >= 0; i--) { if (sourceCurves.Count == 0) { break; } if (i >= sourceCurves.Count) { i = sourceCurves.Count - 1; } //var anotherCurve = sourceCurves[i]; var comparisonR = initCurve.Intersect(sourceCurves[i], out var _); //圆 if (comparisonR == SetComparisonResult.Disjoint && initCurve is Arc && sourceCurves[i] is Arc) { var curves = new List() { initCurve, sourceCurves[i] }; try { var loop = CurveLoop.Create(curves);//如果报错,说明两个半圆没形成圆,是不连续的; _curvesSorted = curves; //移除当前的线段 sourceCurves.Remove(sourceCurves[i]); return; } catch (Exception) { } } if (SetComparisonResult.Overlap == comparisonR) { //XYZ point = null; //if (intersectionR != null && !intersectionR.IsEmpty) //{ // point = intersectionR.get_Item(0).XYZPoint; //} var start1 = sourceCurves[i].GetEndPoint(0); var end1 = sourceCurves[i].GetEndPoint(1); if (end.IsAlmostEqualTo(start1))//顺序连接 { _curvesSorted.Add(sourceCurves[i]); } if (end.IsAlmostEqualTo(end1))//终点一样,反向加到后面 { sourceCurves[i] = sourceCurves[i].CreateReversed();//替换掉,才能保证移除的对象是同一个 _curvesSorted.Add(sourceCurves[i]); } if (start.IsAlmostEqualTo(start1))//起点一样,反向插到前面 { sourceCurves[i] = sourceCurves[i].CreateReversed(); _curvesSorted.Insert(_curvesSorted.IndexOf(initCurve), sourceCurves[i]); } if (start.IsAlmostEqualTo(end1))//顺序连接,但是在前面 { _curvesSorted.Insert(_curvesSorted.IndexOf(initCurve), sourceCurves[i]); } SortOpenedContinuousCurves(sourceCurves[i], sourceCurves); } } } /// 创建一个新的实体,它是输入实体的副本 /// 要复制的输入实体 /// 新的实体 public static Solid Clone(this Solid solid) { return SolidUtils.Clone(solid); } /// 创建一个新的实体,它是输入实体的转换 /// 要转换的输入实体 /// 变换(必须是保角变换) /// 新的实体 /// /// 变换不保形。 /// 或变换的比例为负或零 /// public static Solid CreateTransformed(this Solid solid, Transform transform) { return SolidUtils.CreateTransformed(solid, transform); } /// /// 直线间距离,无边界的情况 /// /// /// /// 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; } public static List DrawMesh(this Sweep sweep) { Options option = new() { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine }; var geomElement = sweep.get_Geometry(option); List triangularPoints = []; foreach (var geomObj in geomElement) { if (geomObj is Solid geomSolid) { foreach (Face geomFace in geomSolid.Faces) { var mesh = geomFace.Triangulate(); for (var i = 0; i < mesh.NumTriangles; i++) { var triangular = mesh.get_Triangle(i); for (var n = 0; n < 3; n++) { var point = triangular.get_Vertex(n); triangularPoints.Add(point); } } } } } return triangularPoints; } /// /// 扩展包围盒的大小 /// /// /// /// 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 }; } /// /// 延长直线 /// /// /// /// 是否双向延申,单向延长终点 /// 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); } /// /// 延长或缩短直线 /// /// /// /// 点在直线内时,保留部分,为null时,保留长的部分 /// public static Line ExtendLine(this Line line, XYZ endPoint, bool? startSection = null) { if (line == null) { throw new ArgumentNullException(nameof(line), "延长线为空!"); } if (endPoint == null) { throw new ArgumentNullException(nameof(endPoint), "延长点为空!"); } 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("不可延伸到该点"); } /// /// 重心,质心 /// /// /// public static XYZ GetCentroid(this List points) { var sourceX = from XYZ point in points select point.X; var sourceY = from XYZ point in points select point.Y; var sourceZ = from XYZ point in points select point.Z; var count = points.Count; var x = sourceX.Sum() / count; var y = sourceY.Sum() / count; var z = sourceZ.Sum() / count; return new XYZ(x, y, z); } public static HermiteSpline ConverToHermiteSpline(this PolyLine polyLine) { var list = polyLine.GetCoordinates(); return HermiteSpline.Create(list, false); } /// /// 将几何实例的实例集合的PolyLine的线条转换成曲线组 /// /// /// public static List GetCurveArray(IEnumerable geometryObjects) { List curves = []; foreach (var geo in geometryObjects) { CurveArray lines = new(); if (geo is not PolyLine pl) { return curves; } var points = pl.GetCoordinates(); var n = 0; for (var i = 0; i < points.Count - 1; i++) { var dis = points[i].DistanceTo(points[points.Count - 1]); if (dis < 1 / 304.8) //重叠线段长度精度大于1mm,排除掉起点 { n = i; } } var pts = points.Skip(n).ToList(); var p0 = pts[0] - (pts[0].Z * XYZ.BasisZ); for (var i = 0; i < pts.Count - 1; i++) { var p1 = pts[i] - (pts[i].Z * XYZ.BasisZ); var p2 = pts[i + 1] - (pts[i + 1].Z * XYZ.BasisZ); if (i == pts.Count - 2 && !p2.IsAlmostEqualTo(p0)) { p2 = p0; } var l = Line.CreateBound(p1, p2); lines.Append(l); } curves.Add(lines); } return curves; } /// /// 获取所有线的交点 /// /// /// public static List GetCurvesIntersect(List curves) { List points = []; foreach (var grid in curves) { foreach (var grd in curves) { grid.Intersect(grd, out var ira); if (ira != null) { var ir = ira.get_Item(0); // 判断点是否重复 foreach (var p in points) { if (!p.IsAlmostEqualTo(ir.XYZPoint)) { points.Add(ir.XYZPoint); } } } } } return points; } public static List> ToCurveLoops(List curves) { List> list = []; while (curves.Any()) { _curvesSorted = [ curves[0] ]; if (!curves[0].IsBound) { curves.Remove(curves[0]); list.Add(_curvesSorted); continue; } SortOpenedContinuousCurves(curves[0], curves); list.Add(_curvesSorted); } //for (var i = 0; i < 100; i++) //{ // try // { // if (!curves.Any()) // { // break; // } // _curvesSorted = new() // { // curves[0] // }; // if (!curves[0].IsBound) // { // curves.Remove(curves[0]); // list.Add(_curvesSorted); // continue; // } // SortOpenedContinuousCurves(curves[0], curves); // } // catch (Exception ex) // { // Debug.Write(ex.StackTrace); // } // list.Add(_curvesSorted); // if (!curves.Any()) // { // break; // } //} return list; } /// /// 获取平面的边 /// /// /// public static List GetEdges(this PlanarFace face) { List list = []; var edgeLoops = face.EdgeLoops; foreach (var obj in edgeLoops) { var edgeArray = (EdgeArray)obj; list.AddRange(edgeArray.Cast()); } return list; } public static ElementOrientation GetElementOrientation(this Curve curve) { var num = 0.0001; var endPoint = curve.GetEndPoint(0); var endPoint2 = curve.GetEndPoint(1); var result = endPoint.IsAlmostEqualTo(endPoint2) ? ElementOrientation.Undefined : Math.Abs(endPoint.X - endPoint2.X) < num && Math.Abs(endPoint.Y - endPoint2.Y) > num ? ElementOrientation.Vertical : (Math.Abs(endPoint.X - endPoint2.X) > num && Math.Abs(endPoint.Y - endPoint2.Y) < num) ? ElementOrientation.Horizontal : Math.Abs(endPoint2.X - endPoint.X) >= Math.Abs(endPoint2.Y - endPoint.Y) ? ElementOrientation.CloseToHorizontal : ElementOrientation.CloseToVertical; return result; } /// /// 端点 /// /// /// public static List GetEndPoints(this Curve curve) { return [curve.GetEndPoint(0), curve.GetEndPoint(1)]; } /// /// 根据等分量等分点曲线,得到所有分割点,含起终点 /// /// /// 等分量 /// public static List GetEquivalentPoints(this Curve curve, int num) { List result = []; if (num <= 0) { throw new ArgumentException("分段数错误"); } for (var i = 0; i <= num; i++) { var parameter = (double)i / num; var p = curve.Evaluate(parameter, true); result.Add(p); } return result; } ///// ///// 步长划分线,含起终点 ///// ///// ///// ///// //public static IEnumerable Divide(this Curve curve, double step) //{ // if (curve.Length > step) // { // double startParameter = curve.GetEndParameter(0); // double endParameter = curve.GetEndParameter(1); // double parameterStep = (endParameter - startParameter) / (curve.Length / step); // double parameterSum = startParameter; // do // { // yield return curve.Evaluate(parameterSum, false); // parameterSum += parameterStep; // } while (parameterSum < endParameter); // if (Math.Abs(parameterSum - endParameter) > 0.0001) // { // yield return curve.GetEndPoint(1); // } // } // else // { // yield return curve.GetEndPoint(0); // yield return curve.GetEndPoint(1); // } //} /// /// 根据等距离量分割曲线,得到所有分割点,含起点,不含终点 /// /// /// 等距离量 /// public static List GetEquivalentPoints(this Curve curve, double step) { List result = []; var num = Math.Floor(curve.Length / step); for (var i = 0; i <= num; i++) { if (curve.IsInside(step * i)) { var p = curve.Evaluate(step * i, true); result.Add(p); } } return result; } /// /// 获取最远的两个点的扩展方法 /// /// /// public static Tuple GetFurthestPoints(this IList points) { Tuple result = default; var dist = double.NaN; for (var i = 0; i < points.Count; i++) { var pt1 = points[i]; for (var j = 0; j < points.Count; j++) { if (i == j) { continue; } var pt2 = points[j]; var d = pt1.DistanceTo(pt2); if (double.IsNaN(dist) || d > dist) { result = new Tuple(pt1, pt2); dist = d; } } } return result; } /// /// 获取几何对象,嵌套一次 /// /// 对应类型的几何对象,如Solid、Face、Edge /// 获取的元素 /// public static List GetAllGeometryObjects(this Element elem) where T : GeometryObject { List 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()); } } else { geometryObjects.AddRange(geomObj.GetGeoObjects()); } } return geometryObjects; } /// /// 获取顶层的几何(非嵌套) /// /// /// /// public static List GetGeometryObjects(this Element elem) { List 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; } /// /// 求面和线的交点 /// /// /// /// public static XYZ GetIntersectPoint(this Face face, Curve curve) { //求交点 XYZ intersection = null; var setComparisonResult = face.Intersect(curve, out var resultArray); if (SetComparisonResult.Disjoint != setComparisonResult) { if (!resultArray.IsEmpty) { intersection = resultArray.get_Item(0).XYZPoint; } } return intersection; } /// /// 获取元素定位线 /// /// 以点定位的实例 /// public static Curve GetCurve(this Element instance) { return instance.Location is LocationCurve lc ? lc.Curve : null; } /// /// 获取元素定位线 /// /// 以点定位的实例 /// public static LocationCurve GetLocationCurve(this Element instance) { return instance.Location is LocationCurve lc ? lc : null; } /// /// 获取元素定位线 /// /// 以点定位的实例 /// public static LocationPoint GetLocationPoint(this Element instance) { return instance.Location is LocationPoint lp ? lp : null; } /// /// 获取元素定位点 /// /// 以点定位的实例 /// public static XYZ GetLocXYZ(this Element instance) { return instance.Location is LocationPoint lp ? lp.Point : null; } public static double GetMaxX(this PlanarFace face) { List list = []; var edgeLoops = face.EdgeLoops; foreach (EdgeArray edgeArray in edgeLoops) { foreach (Edge edge in edgeArray) { list.Add(edge.AsCurve().GetEndPoint(0)); list.Add(edge.AsCurve().GetEndPoint(1)); } } list.Sort((p1, p2) => p1.X.CompareTo(p2.X)); return list.Last().X; } public static double GetMaxY(this PlanarFace face) { List list = []; var edgeLoops = face.EdgeLoops; foreach (EdgeArray edgeArray in edgeLoops) { foreach (Edge edge in edgeArray) { list.Add(edge.AsCurve().GetEndPoint(0)); list.Add(edge.AsCurve().GetEndPoint(1)); } } list.Sort((p1, p2) => p1.Y.CompareTo(p2.Y)); return list.Last().Y; } public static double GetMaxZ(this PlanarFace face) { List list = []; var edgeLoops = face.EdgeLoops; foreach (EdgeArray edgeArray in edgeLoops) { foreach (Edge edge in edgeArray) { list.Add(edge.AsCurve().GetEndPoint(0)); list.Add(edge.AsCurve().GetEndPoint(1)); } } list.Sort((p1, p2) => p1.Z.CompareTo(p2.Z)); return list.Last().Z; } public static double GetMinX(this PlanarFace face) { List list = []; var edgeLoops = face.EdgeLoops; foreach (EdgeArray edgeArray in edgeLoops) { foreach (Edge edge in edgeArray) { list.Add(edge.AsCurve().GetEndPoint(0)); list.Add(edge.AsCurve().GetEndPoint(1)); } } list.Sort((p1, p2) => p1.X.CompareTo(p2.X)); return list.Last().X; } public static double GetMinY(this PlanarFace face) { List list = []; var edgeLoops = face.EdgeLoops; foreach (EdgeArray edgeArray in edgeLoops) { foreach (var obj2 in edgeArray) { var edge = (Edge)obj2; list.Add(edge.AsCurve().GetEndPoint(0)); list.Add(edge.AsCurve().GetEndPoint(1)); } } list.Sort((p1, p2) => p1.Y.CompareTo(p2.Y)); return list.First().Y; } public static double GetMinZ(this PlanarFace face) { List list = []; var edgeLoops = face.EdgeLoops; foreach (EdgeArray edgeArray in edgeLoops) { foreach (Edge edge in edgeArray) { list.Add(edge.AsCurve().GetEndPoint(0)); list.Add(edge.AsCurve().GetEndPoint(1)); } } list.Sort((p1, p2) => p1.Z.CompareTo(p2.Z)); return list.Last().Z; } /// /// 两条异面直线的公垂线(两直线距离长度最短)与异面直线的交点(示例:管线碰撞翻弯) /// /// /// /// public static Dictionary GetNearestPoints(this Line line1, Line line2) { Dictionary dictionary = []; var a = line1.Direction.DotProduct(line2.Direction); var b = line1.Direction.DotProduct(line1.Direction); var c = line2.Direction.DotProduct(line2.Direction); var d = (line2.GetEndPoint(0) - line1.GetEndPoint(0)).DotProduct(line1.Direction); var e = (line2.GetEndPoint(0) - line1.GetEndPoint(0)).DotProduct(line2.Direction); double t1; double t2; if (a == 0) //表明原始两条直线互相垂直 { t1 = d / b; t2 = -e / c; } else if (Math.Abs((a * a) - (b * c)) < 0.0001f) //表明共线或平行,注意理想情况下应该是a * a - b * c == 0,才认为共线或平行,但在计算机世界里,有精度这个东西存在,所以我们近视的认为数值小于某一个值则认为等于0 { //说明共线或平行 return dictionary; } else { t1 = ((a * e) - (c * d)) / ((a * a) - (b * c)); t2 = (b / a * t1) - (d / a); } var p1 = line1.GetEndPoint(0) + (t1 * line1.Direction); var p2 = line2.GetEndPoint(0) + (t2 * line2.Direction); dictionary.Add(line1, p1); dictionary.Add(line2, p2); return dictionary; } /// /// 获取平行面分组 /// /// /// public static void GetParallelFaceGroups(this List faces, List> groups) { var face = faces.FirstOrDefault(); faces = faces.FindAll(f => f is PlanarFace); List group = [face]; groups.Add(group); var pf = face as PlanarFace; faces.Remove(face); for (var i = faces.Count - 1; i >= 0; i--) { if (faces[i] is not PlanarFace) { continue; } var tempFace = faces[i] as PlanarFace; if (pf != null && tempFace != null && tempFace.FaceNormal.CrossProduct(pf.FaceNormal).IsAlmostEqualTo(XYZ.Zero)) { group.Add(tempFace); faces.Remove(tempFace); //GetParallelFaceGroups(faces, tempFace, group, groups); } } if (faces.Count >= 1) { GetParallelFaceGroups(faces, groups); } } /// /// 获取平行面 /// /// /// public static void GetParallelFaces(this List faces, List> groups) { while (true) { var firstFace = faces.FirstOrDefault(); faces = faces.FindAll(face => face is PlanarFace); List group = [firstFace]; groups.Add(group); var pf = firstFace as PlanarFace; faces.Remove(firstFace); for (var i = faces.Count - 1; i >= 0; i--) { if (faces[i] is not PlanarFace) { continue; } var tempFace = faces[i] as PlanarFace; if (pf != null && tempFace != null && tempFace.FaceNormal.CrossProduct(pf.FaceNormal).IsAlmostEqualTo(XYZ.Zero)) { group.Add(tempFace); faces.Remove(tempFace); //GetParallelFaces(faces, tempFace, group, groups); } } if (faces.Count >= 1) { continue; } break; } } /// /// 递归找到所有长度相同的平行线集合 /// public static void GetParallelLineGroups(List lines, List> groups) { while (true) { var firstLine = lines.FirstOrDefault(); lines = lines.FindAll(curve => curve is Line); List group = [firstLine]; groups.Add(group); var line = firstLine as Line; lines.Remove(firstLine); for (var i = lines.Count - 1; i >= 0; i--) { if (lines[i] is Line tempLine) { if ( line != null && tempLine.Direction.CrossProduct(line.Direction).IsAlmostEqualTo(XYZ.Zero) && Math.Abs(tempLine.Length - line.Length) < 0.00001 ) { group.Add(tempLine); lines.Remove(tempLine); } } } if (lines.Count >= 1) { continue; } break; } } /// /// 投影到一个平面 /// /// /// /// public static XYZ GetProjectPoint(this Plane plane, XYZ point) { var tf = Transform.Identity; tf.BasisX = plane.XVec; tf.BasisY = plane.YVec; tf.BasisZ = plane.Normal; tf.Origin = plane.Origin; //把point转到plane的逆矩阵坐标系上 var p = tf.Inverse.OfPoint(point); //z为法向,等于0时,点在平面上 p = new XYZ(p.X, p.Y, 0); return tf.OfPoint(p); } /// /// 计算两条线的交点 /// /// /// /// public static XYZ IntersectionPoint(this Curve curve1, Curve curve2, bool makeUnbound = false) { var unboundCurve1 = curve1.Clone(); var unboundCurve2 = curve2.Clone(); if (makeUnbound && !curve1.IsBound) { unboundCurve1.MakeUnbound(); } if (makeUnbound && !curve2.IsBound) { unboundCurve2.MakeUnbound(); } var comparisonR = unboundCurve1.Intersect(unboundCurve2, 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 IntersectionTwoVectors(XYZ upstreamBranch, XYZ downstreamBranch, XYZ downstreamMain, XYZ upstreamMain) { var main = upstreamMain - downstreamMain; var branch = upstreamBranch - downstreamBranch; var xyz = downstreamMain - downstreamBranch; var num1 = main.DotProduct(main); //main*main var num2 = main.DotProduct(branch); //main*branch*cos α1 var num3 = branch.DotProduct(branch); //branch*branch var num4 = main.DotProduct(xyz); //main*xyz*cos α2 var num5 = branch.DotProduct(xyz); //branch*xyz*cos α3 var num6 = (num1 * num3) - (num2 * num2); //(1-cos α1*cos α1)*num1*num3 double num7; double num8; if (num6 < 1E-08) //main和branch同向 { num7 = 0.0; if (num2 > num3) //main>branch { num8 = num4 / num2; //xyz*cos α2/branch } else { num8 = num5 / num3; //xyz*cos α3/branch } } else { num7 = ((num2 * num5) - (num3 * num4)) / num6; //xyz*(cos α1*cos α3-cos α2)/((1-cos α1*cos α1)*main) num8 = ((num1 * num5) - (num2 * num4)) / num6; //xyz*(cos α3-cos α1*cos α2)/((1-cos α1*cos α1)*branch) } var xyz4 = downstreamMain + (num7 * main); var xyz5 = downstreamBranch + (num8 * branch); return (xyz5 + xyz4) / 2.0; } /// /// 平面与直线的交点,平行时为null /// /// /// /// public static XYZ IntersectPoint(this Plane plane, Line line) { var startPoint = line.Origin; var projectPoint = plane.ProjectOf(startPoint); if (Math.Abs(line.Direction.DotProduct(plane.Normal)) < VbTolerance) //是否平行 { return null; } var length = startPoint.DistanceTo(projectPoint); if (length < VbTolerance) //不平行,但刚好是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); } /// /// 是否水平 /// /// /// public static bool IsHorizontal(this PlanarFace face) { return Math.Abs(face.GetMinY() - face.GetMaxY()) < VbTolerance; } /// /// 点是否在直线范围内,且与端点距离大于等于一定距离 /// /// /// /// 两边端点距离 /// public static bool IsInsideEx(this Line line, XYZ point, double endParameter = 0.0) { if (line == null) { throw new ArgumentNullException(nameof(line)); } if (line.IsBound) { var d1 = point.DistanceTo(line.GetEndPoint(0)); var d2 = point.DistanceTo(line.GetEndPoint(1)); return d1 + d2 - line.Length < VbTolerance && d1 >= endParameter && d2 >= endParameter; } return false; } /// /// 判断方向是否平行 /// /// /// /// public static bool IsParallelTo(this XYZ direction1, XYZ direction2) { return direction1.CrossProduct(direction2).IsZeroLength() || direction1.CrossProduct(direction2).IsAlmostEqualTo(XYZ.Zero); } /// /// 判断直线是否平行或重合 /// /// /// /// 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)) < VbTolerance; return a || b || c; } /// /// 判断直线是否垂直 /// /// /// /// /// public static bool IsPerpendicularTo(this Line line, Line checkedLine, double tolerance = VbTolerance) { return Math.Abs(line.Direction.DotProduct(checkedLine.Direction)) < tolerance; //点乘满足交换律不影响计算结果 } /// 测试输入的实体或壳体是否可用于细分曲面 /// 固体或外壳 /// 如果实体或外壳对细分有效,则为 true,否则为 false public static bool IsValidForTessellation(this Solid solid) { return SolidUtils.IsValidForTessellation(solid); } /// /// 是否是垂直面 /// /// /// public static bool IsVertical(this PlanarFace face) { return Math.Abs(face.GetMinX() - face.GetMaxX()) < VbTolerance; } /// /// 得到垂足 /// /// /// /// 经过该点作与p0,p1所在直线的垂线 /// public static XYZ PerpendicularIntersection(this XYZ pX, XYZ p0, XYZ p1) { //直线向量和p0点到px点的点积 var v0 = pX - p0; var v1 = p1 - p0; var dotProduct = v0.DotProduct(v1); //|v0||v1|cos α var length = v1.GetLength(); //直线的长度|v1| var normalize = v1 / length; //直线单位向量 var m = dotProduct / length; //垂足的模 return (normalize * m) + p0; //var nu3 = nu / (nu2 * nu2); //XYZ result = p0 + v1 * nu3; //return result; //double num = (pX.X - p0.X) * (p1.X - p0.X) + (pX.Y - p0.Y) * (p1.Y - p0.Y) + (pX.Z - p0.Z) * (p1.Z - p0.Z); //double num2 = p0.DistanceTo(p1);//直线的长度 //num /= num2 * num2; //return new XYZ(p0.X + num * (p1.X - p0.X), p0.Y + num * (p1.Y - p0.Y), p0.Z + num * (p1.Z - p0.Z)); } /// /// 点在平面的投影 /// /// /// /// public static XYZ ProjectOf(this PlanarFace plane, XYZ p) { var v = p - plane.Origin; //垂向距离(可正可负,夹角钝角为负) var d = v.DotProduct(plane.FaceNormal); return p - (d * plane.FaceNormal); } /// /// 点在平面的投影 /// /// /// /// public static XYZ ProjectOf(this Plane plane, XYZ p) { var v = p - plane.Origin; //垂向距离(可正可负,夹角钝角为负) var d = v.DotProduct(plane.Normal); return p - (d * plane.Normal); } /// /// 直线在几何平面上的投影 /// /// /// /// public static Line ProjectOnto(this PlanarFace plane, Line line) { return Line.CreateBound(plane.ProjectOf(line.GetEndPoint(0)), plane.ProjectOf(line.GetEndPoint(1))); } /// /// 直线在平面上的投影 /// /// /// /// public static Line ProjectOnto(this Plane plane, Line line) { return Line.CreateBound(plane.ProjectOf(line.GetEndPoint(0)), plane.ProjectOf(line.GetEndPoint(1))); } ///// ///// 点在几何面的投影 ///// ///// ///// ///// //public static XYZ ProjectOnto(this PlanarFace plane, XYZ p) //{ // var d1 = plane.ProjectOf(p, out _); // var q = p - (d1 * plane.FaceNormal); // return q; //} /// /// 点拍平到xy平面 /// /// /// public static XYZ Flatten(this XYZ xyz) { return xyz.Subtract(XYZ.BasisZ * xyz.Z); } /// /// 设置元素定位线 /// /// 以点定位的实例 /// /// 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)); } //var sourceCurve=instance.GetCurve(); if (instance.Location is LocationCurve lc) { try { lc.Curve = curve; } catch (Exception) { throw new InvalidOperationException("无法修改定位线"); } } } /// /// 获取元素定位线 /// /// 以点定位的实例 /// /// public static void SetLocationPoint(this Element instance, XYZ xyz) { if (instance == null) { throw new ArgumentNullException(nameof(instance)); } if (xyz == null) { throw new ArgumentNullException(nameof(xyz)); } if (instance.Location is LocationPoint lp) { try { lp.Point = xyz; } catch (Exception) { throw new InvalidOperationException("无法修改定位点"); } } } /// 将实体几何分割成多个独立实体 /// /// 如果不进行拆分,输出数组将返回输入实体的副本 /// /// The solid /// 分割实体几何图形 /// /// 分割实体几何体失败 /// public static IList SplitVolumes(this Solid solid) { return SolidUtils.SplitVolumes(solid); } /// /// 此函数对实体或开口壳体进行切面(即三角化)。实体或外壳的每个边界 /// 实体或外壳的每个边界部分都由一个三角形结构来表示 /// /// /// 实体(或壳体)边界组件三角剖分上的每一点都应位于某个 "精度 "输入项指定的三维距离之内。 /// 壳体)上的每一点都应位于 "精确度 "输入所指定的三维距离内,反之亦然。 /// 三角剖面上的某一点的 "精度 "输入所指定的三维距离之内,反之亦然。在某些情况下,可以 /// 启发式地(而非严格地)执行 /// /// 要切面的固体或外壳 /// /// 该输入可控制三角测量的各个方面 /// /// /// 与输入实体的边界成分或输入壳体的成分相对应的三角形结构 /// 输入实体或输入壳体的分量 /// /// /// solidOrShell 对三角剖分无效(例如,它不包含任何面) /// /// /// 无法对实体或外壳进行三角测量 /// public static TriangulatedSolidOrShell TessellateSolidOrShell(this Solid solid, SolidOrShellTessellationControls tessellationControls) { return SolidUtils.TessellateSolidOrShell(solid, tessellationControls); } /// /// 变换坐标点等同于transform.OfPoint() /// /// 点,坐标值为在transform(相对坐标系)坐标系上的值 /// 用于变换的坐标系 /// 在transform坐标系上的点,在绝对坐标系上的值 public static XYZ TransformPoint(this XYZ point, Transform transform) { var x = point.X; var y = point.Y; var z = point.Z; var b0 = transform.get_Basis(0); var b1 = transform.get_Basis(1); var b2 = transform.get_Basis(2); var origin = transform.Origin; var xTemp = (x * b0.X) + (y * b1.X) + (z * b2.X) + origin.X; var yTemp = (x * b0.Y) + (y * b1.Y) + (z * b2.Y) + origin.Y; var zTemp = (x * b0.Z) + (y * b1.Z) + (z * b2.Z) + origin.Z; return new XYZ(xTemp, yTemp, zTemp); } #if REVIT2025 /// 查找输入 EdgeEndPoint 所标识顶点上的所有 EdgeEndPoints /// 标识顶点的输入 EdgeEndPoint /// 顶点上的所有边缘端点。输入的边缘端点也包括在内 /// /// 查找输入 EdgeEndPoint 所标识顶点上的所有 EdgeEndPoints失败 /// [Pure] public static IList FindAllEdgeEndPointsAtVertex(this EdgeEndPoint edgeEndPoint) { return SolidUtils.FindAllEdgeEndPointsAtVertex(edgeEndPoint); } #endif }