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. https://forums.autodesk.com/t5/revit-api-forum/is-the-first-edgeloop-still-the-outer-loop/m-p/13011048/highlight/true#M81376 What is happening? The API ought to implement a new functionality that would be very handy for API users. Right now, API users have to do a lot of work that is uncessessary, and that should be (in my opinion) a core part of the library. 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. https://forums.autodesk.com/t5/revit-api-forum/is-the-first-edgeloop-still-the-outer-loop/m-p/10242847/highlight/true#M55285 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(); }
Show More