Files
Shrlalgo.RvKits/ShrlAlgoToolkit.Revit/Assists/SpatialAssist.cs
2026-01-01 10:02:59 +08:00

314 lines
12 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;
namespace ShrlAlgoToolkit.Revit.Assists;
public static class SpatialAssist
{
//private static List<Curve> _curvesSorted;
/// <summary>
/// 拆分线串变成固定长度的曲线段。
/// </summary>
/// <param name="curveChain"></param>
/// <param name="segmentLength"></param>
/// <returns></returns>
public static List<ICurveContainer> SplitCurveChainByFixedLength(CurveLoop curveChain, double segmentLength)
{
List<ICurveContainer> container = new List<ICurveContainer>();
if (curveChain.GetExactLength() - segmentLength < 10e-6)//总长度小于分割长度
{
return [.. curveChain.Select(c => new SingleCurve(c))];
}
//double currentParam = 0.0;
//Curve currentCurve;
//Curve remainingCurve;
//int currentCurveIndex = 0;
CurveList mergeList = null;
//double tempLength = 0.0;
//double currentStartParam = 0.0;
//还需要的曲线长度才能满足分割长度
double neededLength = 0.0;
//List<CurveLoop> loops = new List<CurveLoop>();
for (int i = 0; i < curveChain.Count(); i++)
{
Curve currentCurve = curveChain.ElementAt(i);
//如果需要的长度大于曲线长度,说明整条曲线都应该被包含在内
if (neededLength > currentCurve.Length)
{
mergeList.Curves.Add(currentCurve);
neededLength -= currentCurve.Length;
continue;
}
var splitList = currentCurve.SplitByFixedLength(
neededLength,
segmentLength,
out var previousNeedCurve,
out var remainingCurve);
//需要将前置分割曲线添加上一段的列表中
if (previousNeedCurve != null)
{
mergeList.Curves.Add(previousNeedCurve);
//扣除已经添加的曲线长度,得到剩余需要的长度
neededLength = segmentLength - mergeList.Curves.Sum(c => c.Length);
}
//可以分割
if (splitList.Count > 0)
{
container.AddRange(splitList.Select(c => new SingleCurve(c)));
}
//已经满足分割长度,新建下一个列表
if (neededLength < 10e-6)
{
mergeList = new CurveList([]);
container.Add(mergeList);
}
if (remainingCurve == null)//完全均分时,重置起点
{
neededLength = 0.0;
}
else//剩余曲线添加到下一个列表中
{
mergeList.Curves.Add(remainingCurve);
neededLength = segmentLength - mergeList.Curves.Sum(c => c.Length);
}
}
//container.Add(mergeList); // 添加最后的合并列表
return container;
}
/// <summary>
/// 拆分成点和右侧朝向。
/// </summary>
/// <param name="curveChain"></param>
/// <param name="segmentLength"></param>
/// <returns></returns>
public static Dictionary<XYZ, XYZ> EvalutePointCurveChainByFixedLength(CurveLoop curveChain, double segmentLength)
{
Dictionary<XYZ, XYZ> container = new();
if (curveChain.GetExactLength() - segmentLength < 10e-6)//总长度小于分割长度时,取起点
{
var curve = curveChain.FirstOrDefault();
var startPoint = curve.GetEndPoint(0);
var orientation = curve.ComputeDerivatives(0, true).BasisX.Normalize();
return new Dictionary<XYZ, XYZ> { { startPoint, orientation } };
}
//CurveList mergeList = null;
double currentLength = 0.0;
//还需要的曲线长度才能满足分割长度
double neededLength = 0.0;
for (int i = 0; i < curveChain.Count(); i++)
{
Curve currentCurve = curveChain.ElementAt(i);
//如果需要的长度大于曲线长度,说明整条曲线都应该被包含在内
if (neededLength > currentCurve.Length)
{
//mergeList.Curves.Add(currentCurve);
currentLength += currentCurve.Length;
neededLength -= currentCurve.Length;
continue;
}
var splitList = currentCurve.EvalutePointByFixedLength(
neededLength,
segmentLength,
out var previous,
out var remaining);
//需要将前置分割曲线添加上一段的列表中
if (previous > 0.0)
{
currentLength += previous;
//扣除已经添加的曲线长度,得到剩余需要的长度
neededLength = segmentLength - currentLength;
}
//可以分割
if (splitList.Count > 0)
{
foreach (var item in splitList)
{
if (!container.Any(p => p.Key.IsAlmostEqualTo(item.Key)))//闭合曲线起终点有可能一样
{
container.Add(item.Key, item.Value);
}
}
}
//已经满足分割长度
if (neededLength < 10e-6)
{
currentLength = 0;
//var point = currentCurve.Evaluate(1, false);
//var orientation = currentCurve.Evaluate(1, false);
//var p = new XYZ(-411.791042284402, 1330.9543320873, 0);
//if (p.IsAlmostEqualTo(point))
//{
//}
//container.Add(point,orientation);
}
if (remaining == 0.0)//完全均分时,重置起点
{
neededLength = 0.0;
currentLength = 0.0;
if (i < curveChain.Count() - 1)//如果还有下一个的时候,移除最后一个点
{
var last = container.Last();
container.Remove(last.Key);
}
}
else//剩余曲线添加到下一个列表中
{
currentLength += remaining;
neededLength = segmentLength - currentLength;
}
}
//container.Add(mergeList); // 添加最后的合并列表
return container;
}
public static Transform GetProfileTransform(Curve referCurve)
{
//非刚性变换,会改变原几何,此变换在曲线计算导数时为非刚性变换各个basis的模不等于1
var point = referCurve.GetEndPoint(0);
var transform = referCurve.ComputeDerivatives(0, true);
var tangent = transform.BasisX.Normalize();
var rightBasis = tangent.CrossProduct(XYZ.BasisZ).Normalize();
var topBasis = rightBasis.CrossProduct(tangent).Normalize();
//构造新的变换
var profileTransform = Transform.Identity;
profileTransform.Origin = point;
profileTransform.BasisX = rightBasis;
profileTransform.BasisY = topBasis;
profileTransform.BasisZ = tangent;
return profileTransform;
}
/// <summary>
/// 多段线
/// </summary>
/// <param name="curves"></param>
/// <returns></returns>
public static PolyLine ByJoinedCurves(List<Curve> curves)
{
List<XYZ> points = [];
foreach (var c in curves)
{
points.Add(c.GetEndPoint(0));
}
points.Add(curves.Last().GetEndPoint(1));
if (points.Count == 0)
{
return null;
}
return PolyLine.Create(points);
}
/// <summary>
/// 将一组离散的曲线整理成多条连续的线串。
/// </summary>
/// <param name="curves">输入的曲线集合,可能包含直线、圆弧、样条曲线等。</param>
/// <returns>一个包含多条线串的列表,每条线串本身也是一个曲线列表,且内部曲线方向连续。</returns>
public static List<List<Curve>> GroupContinuousCurves(IEnumerable<Curve> curves)
{
// 最终返回的所有线串集合
var allChains = new List<List<Curve>>();
// 可被消耗的曲线列表
var remainingCurves = new List<Curve>(curves);
// Revit API 中用于几何比较的默认精度
double tolerance = 1e-9;
// 当还有未处理的曲线时,持续循环
while (remainingCurves.Any())
{
// 开始一条新的线串
var currentChain = new List<Curve>();
// 从剩余曲线中取第一条作为新线串的起点
var firstCurve = remainingCurves[0];
currentChain.Add(firstCurve);
remainingCurves.RemoveAt(0);
// 持续延长当前线串,直到无法再延长
while (true)
{
bool chainExtended = false;
// 获取当前线串的头尾端点
XYZ chainStartPoint = currentChain.First().GetEndPoint(0);
XYZ chainEndPoint = currentChain.Last().GetEndPoint(1);
// 从后向前遍历以安全地在循环中移除元素
for (int i = remainingCurves.Count - 1; i >= 0; i--)
{
var candidate = remainingCurves[i];
XYZ candidateStartPoint = candidate.GetEndPoint(0);
XYZ candidateEndPoint = candidate.GetEndPoint(1);
// 尝试连接到线串的尾部
if (chainEndPoint.IsAlmostEqualTo(candidateStartPoint, tolerance))
{
currentChain.Add(candidate);
remainingCurves.RemoveAt(i);
chainExtended = true;
break; // 找到一个后就跳出内层for循环重新获取线串端点
}
else if (chainEndPoint.IsAlmostEqualTo(candidateEndPoint, tolerance))
{
currentChain.Add(candidate.CreateReversed()); // 反向后添加
remainingCurves.RemoveAt(i);
chainExtended = true;
break;
}
// 尝试连接到线串的头部
else if (chainStartPoint.IsAlmostEqualTo(candidateEndPoint, tolerance))
{
currentChain.Insert(0, candidate); // 插入到头部
remainingCurves.RemoveAt(i);
chainExtended = true;
break;
}
else if (chainStartPoint.IsAlmostEqualTo(candidateStartPoint, tolerance))
{
currentChain.Insert(0, candidate.CreateReversed()); // 反向后插入到头部
remainingCurves.RemoveAt(i);
chainExtended = true;
break;
}
}
// 如果本轮没有延长线串,说明这条线串已经构建完毕
if (!chainExtended)
{
break; // 跳出 while(true) 循环
}
}
// 将构建完成的线串添加到最终结果中
allChains.Add(currentChain);
}
return allChains;
}
}
public interface ICurveContainer
{
}
public class SingleCurve : ICurveContainer
{
public SingleCurve(Curve curve)
{
Curve = curve;
}
public Curve Curve { get; }
}
public class CurveList : ICurveContainer
{
public List<Curve> Curves { get; }
public CurveList(List<Curve> curves) => Curves = curves;
}