Files
Shrlalgo.RvKits/ShrlAlgoToolkit.Revit/Extensions/SpatialExtensions.cs
2026-02-17 22:17:13 +08:00

1595 lines
54 KiB
C#
Raw 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 Autodesk.Revit.DB;
using ShrlAlgoToolkit.Revit.Assists;
namespace ShrlAlgoToolkit.Revit.Extensions;
public static class SpatialExtensions
{
private static readonly double CurveTolerance = 0.0025;
/// <summary>
/// 容差
/// </summary>
private const double VbTolerance = 10E-6;
/// <summary>
/// 按距离获取点
/// </summary>
/// <param name="curve">原曲线</param>
/// <param name="startParameter">拆分的起点长度,超出曲线终点长度时,返回原曲线长度</param>
/// <param name="spacing">间距</param>
/// <param name="retainingLength">如果不足以等分,则等于原曲线长度</param>
/// <returns></returns>
public static Dictionary<XYZ, XYZ> EvalutePointByFixedLength(this Curve curve, double startParameter, double spacing, out double startLength, out double retainingLength)
{
Dictionary<XYZ, XYZ> segments = new Dictionary<XYZ, XYZ>();
if (spacing <= 0)
{
throw new InvalidOperationException("分割距离不能小于等于0");
}
if (curve is Arc arc && arc.IsBound)
{
curve = HermiteSpline.Create(arc.Tessellate(), false);
}
if (curve is Ellipse ellipse && ellipse.IsBound)
{
curve = HermiteSpline.Create(ellipse.Tessellate(), false);
}
var originParameter = startParameter;
//startParameter += curve.GetEndParameter(0);//拖动曲线时,起点参数会发生变化
var param1 = curve.GetEndParameter(0);
var param2 = curve.GetEndParameter(1);
startParameter += param1;
if (curve.IsInside(startParameter))
{
startLength = originParameter;
}
else
{
startLength = curve.Length;
}
if (curve.IsInside(startParameter))
{
var acutallLength = param2 - startParameter;
double count = Math.Floor(acutallLength / spacing);
double remainder = acutallLength % spacing;
for (double i = 0; i <= count; i++)
{
var param = i * spacing + startParameter;
//if (curve is Arc || curve is Ellipse)
//{
// param = curve.ComputeRawParameter(param);
//}
//if (param > param2)
//{
// break;
//}
var point = curve.Evaluate(param, false);
//var point1 = curve.GetEndPoint(0);
var transform = curve.ComputeDerivatives(i * spacing + startParameter, false);
var tangent = transform.BasisX;
var orientation = tangent.CrossProduct(XYZ.BasisZ);
segments.Add(point, orientation);
}
if (remainder < 10e-6)//没有余数,完全等分
{
retainingLength = 0;
}
else
{
retainingLength = curve.Length - count * spacing - startLength;
}
}
else
{
retainingLength = curve.Length;
}
return segments;
}
/// <summary>
/// 按距离拆分
/// </summary>
/// <param name="curve">原曲线</param>
/// <param name="startParameter">拆分的起点长度计算曲线时会换算成实际的参数因为曲线可能被伸缩导致起点不是0超出曲线终点长度时返回原曲线需要大于最短曲线容差0.0025,否则返回空</param>
/// <param name="length"></param>
/// <param name="retainingCurve">如果刚好等分,那么就为空,如果不足以等分,那么就等于原曲线</param>
/// <returns></returns>
public static List<Curve> SplitByFixedLength(this Curve curve, double startParameter, double length, out Curve startCurve, out Curve retainingCurve)
{
List<Curve> segments = new List<Curve>();
if (length <= 0)
{
throw new InvalidOperationException("分割距离不能小于等于0");
}
if (curve is Arc arc && arc.IsBound)
{
curve = HermiteSpline.Create(arc.Tessellate(), false);
}
if (curve is Ellipse ellipse && ellipse.IsBound)
{
curve = HermiteSpline.Create(ellipse.Tessellate(), false);
}
var originParameter = startParameter;
//startParameter += curve.GetEndParameter(0);//但你拖动曲线时,起点参数会发生变化
var param1 = curve.GetEndParameter(0);
var param2 = curve.GetEndParameter(1);
startParameter += param1;
if (curve.IsInside(startParameter))
{
if (startParameter < CurveTolerance || originParameter < CurveTolerance)//过短返回空一般是0
{
startCurve = null;
}
else
{
startCurve = curve.Clone();
startCurve.MakeBound(param1, startParameter);
}
}
else
{
startCurve = curve.Clone();
}
if (curve.IsInside(startParameter))
{
var acutallLength = param2 - startParameter;
double count = Math.Floor(acutallLength / length);
double remainder = acutallLength % length;
for (double i = 0; i < count; i++)
{
var startParam = i * length + startParameter;
var endParam = startParam + length;
var segment = curve.Clone();
segment.MakeBound(startParam, endParam);
segments.Add(segment);
}
if (remainder < 10e-6)//没有余数,完全等分
{
retainingCurve = null;
}
else
{
retainingCurve = curve.Clone();
retainingCurve.MakeBound(length * count + startParameter, param2);
}
}
else
{
retainingCurve = curve.Clone();
}
return segments;
}
/// <summary>
/// 获取几何实例的准确的定位
/// </summary>
/// <param name="dwgTransform">dwg的变换</param>
/// <param name="ins"></param>
/// <param name="rotation"></param>
public static XYZ GetLocationPoint(this GeometryInstance ins, Transform dwgTransform, out double rotation)
{
var blockLocation = ins.Transform.Origin;
if (ins.Transform.Origin.GetLength() is > 1000 or 0)
{
foreach (var geometryObject in ins.GetInstanceGeometry())
{
if (geometryObject is Arc arc)
{
blockLocation = arc.Center;
break;
}
if (geometryObject is PolyLine pl)
{
blockLocation = (pl.GetOutline().MaximumPoint + pl.GetOutline().MinimumPoint) / 2;
break;
}
}
}
rotation = Math.Abs(ins.Transform.BasisX.AngleOnPlaneTo(XYZ.BasisX, XYZ.BasisZ) - 2 * Math.PI);
return dwgTransform.OfPoint(blockLocation);
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="geoObject"></param>
/// <returns></returns>
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;
}
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;
}
/// <summary>创建一个新的实体,它是输入实体的副本</summary>
/// <param name="solid">要复制的输入实体</param>
/// <returns>新的实体</returns>
public static Solid Clone(this Solid solid)
{
return SolidUtils.Clone(solid);
}
/// <summary>创建一个新的实体,它是输入实体的转换</summary>
/// <param name="solid">要转换的输入实体</param>
/// <param name="transform">变换(必须是保角变换)</param>
/// <returns>新的实体</returns>
/// <exception cref="Command:Autodesk.Revit.Exceptions.ArgumentOutOfRangeException">
/// 变换不保形。
/// 或变换的比例为负或零
/// </exception>
public static Solid CreateTransformed(this Solid solid, Transform transform)
{
return SolidUtils.CreateTransformed(solid, transform);
}
/// <summary>
/// 直线间距离,无边界的情况
/// </summary>
/// <param name="line1"></param>
/// <param name="line2"></param>
/// <returns></returns>
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<XYZ> DrawMesh(this Sweep sweep)
{
Options option = new() { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine };
var geomElement = sweep.get_Geometry(option);
List<XYZ> 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;
}
/// <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 };
}
/// <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>
/// 延长或缩短直线
/// </summary>
/// <param name="line"></param>
/// <param name="endPoint"></param>
/// <param name="startSection">点在直线内时保留部分为null时保留长的部分</param>
/// <returns></returns>
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("不可延伸到该点");
}
/// <summary>
/// 重心,质心
/// </summary>
/// <param name="points"></param>
/// <returns></returns>
public static XYZ GetCentroid(this List<XYZ> 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);
}
/// <summary>
/// 将几何实例的实例集合的PolyLine的线条转换成曲线组
/// </summary>
/// <param name="geometryObjects"></param>
/// <returns></returns>
public static List<CurveArray> GetCurveArray(IEnumerable<GeometryObject> geometryObjects)
{
List<CurveArray> 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;
}
/// <summary>
/// 获取所有线的交点
/// </summary>
/// <param name="curves"></param>
/// <returns></returns>
public static List<XYZ> GetCurvesIntersect(List<Curve> curves)
{
List<XYZ> 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;
}
/// <summary>
/// 获取平面的边
/// </summary>
/// <param name="face"></param>
/// <returns></returns>
public static List<Edge> GetEdges(this PlanarFace face)
{
List<Edge> list = [];
var edgeLoops = face.EdgeLoops;
foreach (var obj in edgeLoops)
{
var edgeArray = (EdgeArray)obj;
list.AddRange(edgeArray.Cast<Edge>());
}
return list;
}
/// <summary>
///
/// </summary>
/// <param name="curve"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 端点
/// </summary>
/// <param name="curve"></param>
/// <returns></returns>
public static List<XYZ> GetEndPoints(this Curve curve)
{
return [curve.GetEndPoint(0), curve.GetEndPoint(1)];
}
/// <summary>
/// 根据等分量等分点曲线,得到所有分割点,含起终点
/// </summary>
/// <param name="curve"></param>
/// <param name="num">等分量</param>
/// <returns></returns>
public static List<XYZ> GetEquivalentPoints(this Curve curve, int num)
{
List<XYZ> 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;
}
///// <summary>
///// 步长划分线,含起终点
///// </summary>
///// <param name="curve"></param>
///// <param name="step"></param>
///// <returns></returns>
//public static IEnumerable<XYZ> 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);
// }
//}
/// <summary>
/// 根据等距离量分割曲线,得到所有分割点,含起点,不含终点
/// </summary>
/// <param name="curve"></param>
/// <param name="step">等距离量</param>
/// <returns></returns>
public static List<XYZ> GetEquivalentPoints(this Curve curve, double step)
{
List<XYZ> 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;
}
/// <summary>
/// 获取最远的两个点的扩展方法
/// </summary>
/// <param name="points"></param>
/// <returns></returns>
public static Tuple<XYZ, XYZ> GetFurthestPoints(this IList<XYZ> points)
{
Tuple<XYZ, XYZ> 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<XYZ, XYZ>(pt1, pt2);
dist = d;
}
}
}
return result;
}
/// <summary>
/// 获取几何对象,嵌套一次
/// </summary>
/// <typeparam name="T">对应类型的几何对象如Solid、Face、Edge</typeparam>
/// <param name="elem">获取的元素</param>
/// <returns></returns>
public static List<T> GetAllGeometryObjects<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>());
}
}
else
{
geometryObjects.AddRange(geomObj.GetGeoObjects<T>());
}
}
return geometryObjects;
}
/// <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>
/// <param name="face"></param>
/// <param name="curve"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 获取元素定位线
/// </summary>
/// <param name="instance">以点定位的实例</param>
/// <returns></returns>
public static Curve GetCurve(this Element instance)
{
return instance.Location is LocationCurve lc ? lc.Curve : null;
}
/// <summary>
/// 获取元素定位线
/// </summary>
/// <param name="instance">以点定位的实例</param>
/// <returns></returns>
public static LocationCurve GetLocationCurve(this Element instance)
{
return instance.Location is LocationCurve lc ? lc : null;
}
/// <summary>
/// 获取元素定位点
/// </summary>
/// <param name="instance">以点定位的实例</param>
/// <returns></returns>
public static LocationPoint GetLocationPoint(this Element instance)
{
return instance.Location is LocationPoint lp ? lp : null;
}
/// <summary>
/// 获取元素定位点
/// </summary>
/// <param name="instance">以点定位的实例</param>
/// <returns></returns>
public static XYZ GetLocXYZ(this Element instance)
{
return instance.Location is LocationPoint lp ? lp.Point : null;
}
/// <summary>
/// 底部中心点
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
public static XYZ GetBottomCenterPoint(this Element element)
{
BoundingBoxXYZ boundingBox = element.get_BoundingBox(null);
// 获取包围盒的最小点和最大点
XYZ minPoint = boundingBox.Min;
XYZ maxPoint = boundingBox.Max;
// 计算底部中心点的X坐标
double centerX = (minPoint.X + maxPoint.X) / 2.0;
// 计算底部中心点的Y坐标
double centerY = (minPoint.Y + maxPoint.Y) / 2.0;
// 底部中心点的Z坐标就是包围盒的最小Z坐标
double centerZ = minPoint.Z;
// 创建并返回底部中心点
XYZ bottomCenter = new XYZ(centerX, centerY, centerZ);
return bottomCenter;
}
/// <summary>
/// 过直线朝上的平面
/// </summary>
/// <param name="line"></param>
/// <returns></returns>
public static Plane GetUpwardPlaneThroughLine(this Line line)
{
XYZ lineOrigin = line.Origin;
XYZ lineDirection = line.Direction; // 确保方向向量是单位向量
// 1. 特殊情况如果直线是垂直的无法找到法线Z分量大于0的平面。
// 这意味着 lineDirection 接近 (0,0,1) 或 (0,0,-1)。
// 使用一个小的容差值进行比较。
if (Math.Abs(lineDirection.X) < 1e-6 && Math.Abs(lineDirection.Y) < 1e-6)
{
// 直线是垂直的。返回 null 或抛出异常因为无法满足“法线Z>0”的条件。
return null;
}
// 2. 第一次叉积:得到一个与 lineDirection 和 XYZ.BasisZ 都正交的向量。
// 这个向量 V1 是一个水平向量,它描述了一个包含 lineDirection 的垂直平面的法线。
XYZ v1 = lineDirection.CrossProduct(XYZ.BasisZ);
// 3. 第二次叉积:得到最终的法线候选向量。
// 这个向量 N_candidate 与 lineDirection 正交并倾向于有一个正的Z分量。
XYZ normalCandidate = v1.CrossProduct(lineDirection);
// 4. 归一化法线向量并确保其Z分量为正。
normalCandidate = normalCandidate.Normalize();
if (normalCandidate.Z < 0)
{
normalCandidate = -normalCandidate;
}
// 如果 normalCandidate.Z 此时仍然接近 0说明直线是水平的
// 并且两次叉积的结果依然是水平法线。这通常表示一个垂直平面。
// 在这种情况下,如果你的“面朝上”严格要求 Z > 0则需要进一步处理。
// 然而,对于大多数非垂直线,上述逻辑足以得到一个 Z > 0 的法线。
// 对于水平线,两次叉积会得到一个垂直方向的法线(如(0,0,1)或(0,0,-1)的倍数),
// 经过 Z<0 调整后,确保了 Z > 0。
// 例子:如果 lineDirection 是 (1,0,0) (X轴),
// v1 = (1,0,0) x (0,0,1) = (0, -1, 0)
// normalCandidate = (1,0,0) x (0,-1,0) = (0,0,-1)
// 经过 Z<0 调整后normalCandidate = (0,0,1)。这是一个面朝上的水平平面。
// 5. 使用直线的起点和计算出的法线创建平面。
Plane upwardFacingPlane = Plane.CreateByNormalAndOrigin(lineOrigin, normalCandidate);
return upwardFacingPlane;
}
public static double GetMaxX(this PlanarFace face)
{
List<XYZ> 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<XYZ> 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<XYZ> 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<XYZ> 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<XYZ> 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<XYZ> 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;
}
/// <summary>
/// 两条异面直线的公垂线(两直线距离长度最短)与异面直线的交点(示例:管线碰撞翻弯)
/// </summary>
/// <param name="line1"></param>
/// <param name="line2"></param>
/// <returns></returns>
public static Dictionary<Line, XYZ> GetNearestPoints(this Line line1, Line line2)
{
Dictionary<Line, XYZ> 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;
}
/// <summary>
/// 获取平行面分组
/// </summary>
/// <param name="faces"></param>
/// <param name="groups"></param>
public static void GetParallelFaceGroups(this List<Face> faces, List<List<Face>> groups)
{
var face = faces.FirstOrDefault();
faces = faces.FindAll(f => f is PlanarFace);
List<Face> 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);
}
}
/// <summary>
/// 获取平行面
/// </summary>
/// <param name="faces"></param>
/// <param name="groups"></param>
public static void GetParallelFaces(this List<Face> faces, List<List<Face>> groups)
{
while (true)
{
var firstFace = faces.FirstOrDefault();
faces = faces.FindAll(face => face is PlanarFace);
List<Face> 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;
}
}
/// <summary>
/// 递归找到所有长度相同的平行线集合
/// </summary>
public static void GetParallelLineGroups(List<Curve> lines, List<List<Curve>> groups)
{
while (true)
{
var firstLine = lines.FirstOrDefault();
lines = lines.FindAll(curve => curve is Line);
List<Curve> 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;
}
}
/// <summary>
/// 投影到一个平面
/// </summary>
/// <param name="plane"></param>
/// <param name="point"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 计算两条线的交点
/// </summary>
/// <param name="curve1"></param>
/// <param name="curve2"></param>
/// <returns></returns>
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;
}
/// <summary>
/// </summary>
/// <param name="upstreamBranch">上游分支</param>
/// <param name="downstreamBranch">下游分支</param>
/// <param name="downstreamMain">下游主线</param>
/// <param name="upstreamMain">上游主线</param>
/// <returns></returns>
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;
}
/// <summary>
/// 平面与直线的交点平行时为null
/// </summary>
/// <param name="plane"></param>
/// <param name="line"></param>
/// <returns>交点</returns>
public static XYZ IntersectPoint(this Plane plane, Line line)
{
var startPoint = line.Origin;
var intersectLine = line;
if (intersectLine.IsBound)
{
intersectLine = line.Clone() as Line;
intersectLine.MakeUnbound();
}
var projectPoint = ProjectOf(plane, startPoint);
if (Math.Abs(intersectLine.Direction.DotProduct(plane.Normal)) < 10e-6) //是否平行
{
return null;
}
var length = startPoint.DistanceTo(projectPoint);
if (length < 10e-6) //不平行但刚好是Origin,即为交点
{
return projectPoint;
}
var vector = projectPoint - startPoint;
//var per = Line.CreateBound(startPoint, projectPoint);//垂线
if (vector.IsParallelTo(intersectLine.Direction))
{
return projectPoint;
}
var cos = plane.Normal.DotProduct(intersectLine.Direction);
if (cos < 0)
{
cos = plane.Normal.Negate().DotProduct(intersectLine.Direction);
}
var distance = length / cos;
return vector.Normalize().DotProduct(intersectLine.Direction) < 0
? startPoint - (intersectLine.Direction * distance)
: startPoint + (intersectLine.Direction * distance);
}
/// <summary>
/// 是否水平
/// </summary>
/// <param name="face"></param>
/// <returns></returns>
public static bool IsHorizontal(this PlanarFace face)
{
return Math.Abs(face.GetMinY() - face.GetMaxY()) < VbTolerance;
}
/// <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)
{
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;
}
/// <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>
/// <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)) < VbTolerance;
return a || b || c;
}
/// <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 = VbTolerance)
{
return Math.Abs(line.Direction.DotProduct(checkedLine.Direction)) < tolerance; //点乘满足交换律不影响计算结果
}
/// <summary>测试输入的实体或壳体是否可用于细分曲面</summary>
/// <param name="solid">固体或外壳</param>
/// <returns>如果实体或外壳对细分有效,则为 true否则为 false</returns>
public static bool IsValidForTessellation(this Solid solid)
{
return SolidUtils.IsValidForTessellation(solid);
}
/// <summary>
/// 是否是垂直面
/// </summary>
/// <param name="face"></param>
/// <returns></returns>
public static bool IsVertical(this PlanarFace face)
{
return Math.Abs(face.GetMinX() - face.GetMaxX()) < VbTolerance;
}
/// <summary>
/// 得到垂足
/// </summary>
/// <param name="p0"></param>
/// <param name="p1"></param>
/// <param name="pX">经过该点作与p0,p1所在直线的垂线</param>
/// <returns></returns>
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));
}
/// <summary>
/// 点在平面的投影
/// </summary>
/// <param name="plane"></param>
/// <param name="p"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 点在平面的投影
/// </summary>
/// <param name="plane"></param>
/// <param name="p"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 直线在几何平面上的投影
/// </summary>
/// <param name="plane"></param>
/// <param name="line"></param>
/// <returns></returns>
public static Line ProjectOnto(this PlanarFace plane, Line line)
{
return Line.CreateBound(plane.ProjectOf(line.GetEndPoint(0)), plane.ProjectOf(line.GetEndPoint(1)));
}
/// <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.ProjectOf(line.GetEndPoint(0)), plane.ProjectOf(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 d1 = plane.ProjectOf(p, out _);
// var q = p - (d1 * plane.FaceNormal);
// return q;
//}
/// <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="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));
}
//var sourceCurve=instance.GetCurve();
if (instance.Location is LocationCurve lc)
{
try
{
lc.Curve = curve;
}
catch (Exception)
{
throw new InvalidOperationException("无法修改定位线");
}
}
}
/// <summary>
/// 获取元素定位线
/// </summary>
/// <param name="instance">以点定位的实例</param>
/// <param name="xyz"></param>
/// <returns></returns>
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("无法修改定位点");
}
}
}
/// <summary>将实体几何分割成多个独立实体</summary>
/// <remarks>
/// 如果不进行拆分,输出数组将返回输入实体的副本
/// </remarks>
/// <param name="solid">The solid</param>
/// <returns>分割实体几何图形</returns>
/// <exception cref="Command:Autodesk.Revit.Exceptions.InvalidOperationException">
/// 分割实体几何体失败
/// </exception>
public static IList<Solid> SplitVolumes(this Solid solid)
{
return SolidUtils.SplitVolumes(solid);
}
/// <summary>
/// 此函数对实体或开口壳体进行切面(即三角化)。实体或外壳的每个边界
/// 实体或外壳的每个边界部分都由一个三角形结构来表示
/// </summary>
/// <remarks>
/// 实体(或壳体)边界组件三角剖分上的每一点都应位于某个 "精度 "输入项指定的三维距离之内。
/// 壳体)上的每一点都应位于 "精确度 "输入所指定的三维距离内,反之亦然。
/// 三角剖面上的某一点的 "精度 "输入所指定的三维距离之内,反之亦然。在某些情况下,可以
/// 启发式地(而非严格地)执行
/// </remarks>
/// <param name="solid">要切面的固体或外壳</param>
/// <param name="tessellationControls">
/// 该输入可控制三角测量的各个方面
/// </param>
/// <returns>
/// 与输入实体的边界成分或输入壳体的成分相对应的三角形结构
/// 输入实体或输入壳体的分量
/// </returns>
/// <exception cref="Command:Autodesk.Revit.Exceptions.ArgumentException">
/// solidOrShell 对三角剖分无效(例如,它不包含任何面)
/// </exception>
/// <exception cref="Command:Autodesk.Revit.Exceptions.InvalidOperationException">
/// 无法对实体或外壳进行三角测量
/// </exception>
public static TriangulatedSolidOrShell TessellateSolidOrShell(this Solid solid, SolidOrShellTessellationControls tessellationControls)
{
return SolidUtils.TessellateSolidOrShell(solid, tessellationControls);
}
/// <summary>
/// 变换坐标点等同于transform.OfPoint()
/// </summary>
/// <param name="point">点坐标值为在transform相对坐标系坐标系上的值</param>
/// <param name="transform">用于变换的坐标系</param>
/// <returns>在transform坐标系上的点在绝对坐标系上的值</returns>
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
/// <summary>查找输入 EdgeEndPoint 所标识顶点上的所有 EdgeEndPoints</summary>
/// <param name="edgeEndPoint">标识顶点的输入 EdgeEndPoint</param>
/// <returns>顶点上的所有边缘端点。输入的边缘端点也包括在内</returns>
/// <exception cref="T:Autodesk.Revit.Exceptions.InvalidOperationException">
/// 查找输入 EdgeEndPoint 所标识顶点上的所有 EdgeEndPoints失败
/// </exception>
[Pure]
public static IList<EdgeEndPoint> FindAllEdgeEndPointsAtVertex(this EdgeEndPoint edgeEndPoint)
{
return SolidUtils.FindAllEdgeEndPointsAtVertex(edgeEndPoint);
}
#endif
}