This is an issue creating a headache and unexpected results for Revit API users:
Please refer to the following for some background. Mr Jeremy Tammik jeremy_tammik recommended that an idea be created for this.
What is happening?
I want to be able to do this, for example:
EdgeArray edgeArray = edgeArrayArrays.OuterLoop;
One single line of code. Instead, I have to implement all of this - and the only reason we have this code is because @stefanome very generously provided it to the community.
public static List<List<CurveLoop>> SortCurveLoops(Face face)
{
var allLoops = face.GetEdgesAsCurveLoops().Select(loop => new CurveLoopUV(loop, face)).ToList();
var outerLoops = allLoops.Where(loop => loop.IsCounterclockwise).ToList();
var innerLoops = allLoops.Where(loop => !outerLoops.Contains(loop)).ToList();
// sort outerLoops putting last the ones that are outside all the preceding loops
bool somethingHasChanged;
do
{
somethingHasChanged = false;
for (var i = 1; i < outerLoops.Count(); i++)
{
var point = outerLoops[i].StartPointUV;
var loop = outerLoops[i - 1];
if (loop.IsPointInside(point) is CurveLoopUV.PointLocation.Inside)
{
var tmp = outerLoops[i];
outerLoops[i] = outerLoops[i - 1];
outerLoops[i - 1] = tmp;
somethingHasChanged = true;
}
}
} while (somethingHasChanged);
var result = new List<List<CurveLoop>>();
foreach (var outerLoop in outerLoops)
{
var list = new List<CurveLoop> {outerLoop.Loop3d};
for (var i = innerLoops.Count - 1; i >= 0; i--)
{
var innerLoop = innerLoops[i];
if (outerLoops.Count() == 1 // skip testing whether the inner loop is inside the outer loop
|| outerLoop.IsPointInside(innerLoop.StartPointUV) == CurveLoopUV.PointLocation.Inside)
{
list.Add(innerLoop.Loop3d);
innerLoops.RemoveAt(i);
}
}
result.Add(list);
}
return result;
}
class CurveLoopUV : IEnumerable<Curve>
{
public enum PointLocation
{
Outside,
OnTheEdge,
Inside,
}
public CurveLoop Loop3d { get; }
private readonly CurveLoop _loop2d;
public readonly double MinX, MaxX, MinY, MaxY;
public CurveLoopUV(CurveLoop curveLoop, Face face)
{
Loop3d = curveLoop;
_loop2d = new CurveLoop();
var points3d = Loop3d.SelectMany(curve => curve.Tessellate().Skip(1));
var pointsUv = points3d.Select(point3d => face.Project(point3d).UVPoint);
var points2d = pointsUv.Select(pointUv => new XYZ(pointUv.U, pointUv.V, 0)).ToList();
MinX = MinY = 1.0e100;
MaxX = MaxY = -1.0e100;
var nPoints = points2d.Count();
for (var i = 0; i < nPoints; i++)
{
var p1 = points2d[i];
var p2 = points2d[(i + 1) % nPoints];
_loop2d.Append(Line.CreateBound(p1, p2));
if (p1.X < MinX) MinX = p1.X;
if (p1.Y < MinY) MinY = p1.Y;
if (p1.X > MaxX) MaxX = p1.X;
if (p1.Y > MaxY) MaxY = p1.Y;
}
}
public PointLocation IsPointInside(XYZ point)
{
if (point.Y + Eps < MinY || point.Y + Eps > MaxY)
return PointLocation.Outside;
if (_loop2d.Any(curve => curve.Distance(point) < Eps))
return PointLocation.OnTheEdge;
var line = Line.CreateBound(point, new XYZ(1.0e100, point.Y, 0));
var nIntersections = _loop2d.Count(edge => edge.Intersect(line) == SetComparisonResult.Overlap);
return nIntersections % 2 == 1 ? PointLocation.Inside : PointLocation.Outside;
}
public bool IsCounterclockwise => _loop2d.IsCounterclockwise(XYZ.BasisZ);
public XYZ StartPointUV => _loop2d.First().GetEndPoint(0);
public IEnumerator<Curve> GetEnumerator() => _loop2d.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Can't find what you're looking for? Ask the community or share your knowledge.