Files
ShrlAlgoToolkit/ShrlAlgoToolkit.Revit/Assists/SpatialAssist.cs

453 lines
16 KiB
C#
Raw Normal View History

2025-04-24 20:56:44 +08:00
using Autodesk.Revit.DB;
namespace ShrlAlgoToolkit.Revit.Assists;
2025-12-28 11:47:54 +08:00
public static class SpatialAssist
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
//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)));
}
2025-04-24 20:56:44 +08:00
2025-07-11 09:20:23 +08:00
//已经满足分割长度,新建下一个列表
if (neededLength < 10e-6)
{
mergeList = new CurveList([]);
container.Add(mergeList);
}
if (remainingCurve == null)//完全均分时,重置起点
{
neededLength = 0.0;
}
else//剩余曲线添加到下一个列表中
{
mergeList.Curves.Add(remainingCurve);
2025-04-24 20:56:44 +08:00
2025-07-11 09:20:23 +08:00
neededLength = segmentLength - mergeList.Curves.Sum(c => c.Length);
}
}
//container.Add(mergeList); // 添加最后的合并列表
return container;
}
2025-04-24 20:56:44 +08:00
/// <summary>
2025-07-11 09:20:23 +08:00
/// 拆分成点和右侧朝向。
2025-04-24 20:56:44 +08:00
/// </summary>
2025-07-11 09:20:23 +08:00
/// <param name="curveChain"></param>
/// <param name="segmentLength"></param>
/// <returns></returns>
public static Dictionary<XYZ, XYZ> EvalutePointCurveChainByFixedLength(CurveLoop curveChain, double segmentLength)
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
Dictionary<XYZ, XYZ> container = new();
if (curveChain.GetExactLength() - segmentLength < 10e-6)//总长度小于分割长度时,取起点
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
var curve = curveChain.FirstOrDefault();
var startPoint = curve.GetEndPoint(0);
var orientation = curve.ComputeDerivatives(0, true).BasisX.Normalize();
return new Dictionary<XYZ, XYZ> { { startPoint, orientation } };
2025-04-24 20:56:44 +08:00
}
2025-07-11 09:20:23 +08:00
//CurveList mergeList = null;
double currentLength = 0.0;
//还需要的曲线长度才能满足分割长度
double neededLength = 0.0;
for (int i = 0; i < curveChain.Count(); i++)
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
Curve currentCurve = curveChain.ElementAt(i);
//如果需要的长度大于曲线长度,说明整条曲线都应该被包含在内
if (neededLength > currentCurve.Length)
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
//mergeList.Curves.Add(currentCurve);
currentLength += currentCurve.Length;
neededLength -= currentCurve.Length;
continue;
2025-04-24 20:56:44 +08:00
}
2025-07-11 09:20:23 +08:00
var splitList = currentCurve.EvalutePointByFixedLength(
neededLength,
segmentLength,
out var previous,
out var remaining);
//需要将前置分割曲线添加上一段的列表中
if (previous > 0.0)
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
currentLength += previous;
//扣除已经添加的曲线长度,得到剩余需要的长度
neededLength = segmentLength - currentLength;
2025-04-24 20:56:44 +08:00
}
2025-07-11 09:20:23 +08:00
//可以分割
if (splitList.Count > 0)
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
foreach (var item in splitList)
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
if (!container.Any(p => p.Key.IsAlmostEqualTo(item.Key)))//闭合曲线起终点有可能一样
{
container.Add(item.Key, item.Value);
}
2025-04-24 20:56:44 +08:00
}
}
2025-07-11 09:20:23 +08:00
//已经满足分割长度
if (neededLength < 10e-6)
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
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))
2025-04-24 20:56:44 +08:00
//{
//}
2025-07-11 09:20:23 +08:00
//container.Add(point,orientation);
}
2025-04-24 20:56:44 +08:00
2025-07-11 09:20:23 +08:00
if (remaining == 0.0)//完全均分时,重置起点
{
neededLength = 0.0;
currentLength = 0.0;
if (i < curveChain.Count() - 1)//如果还有下一个的时候,移除最后一个点
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
var last = container.Last();
container.Remove(last.Key);
2025-04-24 20:56:44 +08:00
}
2025-07-11 09:20:23 +08:00
}
else//剩余曲线添加到下一个列表中
{
currentLength += remaining;
neededLength = segmentLength - currentLength;
}
}
//container.Add(mergeList); // 添加最后的合并列表
return container;
}
public static Transform GetProfileTranform(Curve referCurve)
{
//非刚性变换,会改变原几何,此变换在曲线计算导数时为非刚性变换各个basis的模不等于1
var point = referCurve.GetEndPoint(0);
var transform = referCurve.ComputeDerivatives(0, true);
2025-04-24 20:56:44 +08:00
2025-07-11 09:20:23 +08:00
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--)
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
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;
}
2025-04-24 20:56:44 +08:00
}
2025-07-11 09:20:23 +08:00
// 如果本轮没有延长线串,说明这条线串已经构建完毕
if (!chainExtended)
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
break; // 跳出 while(true) 循环
2025-04-24 20:56:44 +08:00
}
}
2025-07-11 09:20:23 +08:00
// 将构建完成的线串添加到最终结果中
allChains.Add(currentChain);
2025-04-24 20:56:44 +08:00
}
2025-07-11 09:20:23 +08:00
return allChains;
2025-04-24 20:56:44 +08:00
}
2025-07-11 09:20:23 +08:00
///// <summary>
///// 连成线串
///// </summary>
///// <param name="initCurve"></param>
///// <param name="sourceCurves"></param>
//private static void SortOpenedContinuousCurves(Curve initCurve, List<Curve> 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<Curve>() { 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);
// }
// }
//}
2025-04-24 20:56:44 +08:00
/// <summary>
/// 转线串
/// </summary>
/// <param name="curves"></param>
/// <returns></returns>
2025-07-11 09:20:23 +08:00
//public static List<List<Curve>> ToCurveLoops(List<Curve> curves)
//{
// List<List<Curve>> 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;
//}
}
2025-12-28 11:47:54 +08:00
public interface ICurveContainer
2025-07-11 09:20:23 +08:00
{
}
public class SingleCurve : ICurveContainer
{
public SingleCurve(Curve curve)
2025-04-24 20:56:44 +08:00
{
2025-07-11 09:20:23 +08:00
Curve = curve;
2025-04-24 20:56:44 +08:00
}
2025-07-11 09:20:23 +08:00
public Curve Curve { get; }
}
public class CurveList : ICurveContainer
{
public List<Curve> Curves { get; }
public CurveList(List<Curve> curves) => Curves = curves;
2025-04-24 20:56:44 +08:00
}