/// <summary> /// Creates an open or closed CurveLoop from a list of vertices. /// </summary> /// <param name="pointXYZs">The list of vertices.</param> /// <param name="points">The optional list of IFCAnyHandles that generated the vertices, used solely for error reporting.</param> /// <param name="id">The id of the IFCAnyHandle associated with the CurveLoop.</param> /// <param name="closeCurve">True if the loop needs a segment between the last point and the first point.</param> /// <returns>The new curve loop.</returns> /// <remarks>If closeCurve is true, there will be pointsXyz.Count line segments. Otherwise, there will be pointsXyz.Count-1.</remarks> public static CurveLoop CreatePolyCurveLoop(IList <XYZ> pointXYZs, IList <IFCAnyHandle> points, int id, bool closeCurve) { int numPoints = pointXYZs.Count; if (numPoints < 2) { // TODO: log warning return(null); } IList <int> badIds = new List <int>(); // The input polycurve loop may or may not repeat the start/end point. // wasAlreadyClosed checks if the point was repeated. bool wasAlreadyClosed = pointXYZs[0].IsAlmostEqualTo(pointXYZs[numPoints - 1]); bool wasClosed = closeCurve ? true : wasAlreadyClosed; // We expect at least 3 points if the curve is closed, 2 otherwise. int numMinPoints = wasAlreadyClosed ? 4 : (closeCurve ? 3 : 2); if (numPoints < numMinPoints) { // TODO: log warning return(null); } // Check distance between points; remove too-close points, and warn if result is non-collinear. // Always include first point. IList <XYZ> finalPoints = new List <XYZ>(); finalPoints.Add(pointXYZs[0]); int numNewPoints = 1; int numPointsToCheck = closeCurve ? numPoints + 1 : numPoints; for (int ii = 1; ii < numPointsToCheck; ii++) { int nextIndex = (ii % numPoints); int nextNextIndex = (nextIndex == numPoints - 1 && wasAlreadyClosed) ? 1 : ((ii + 1) % numPoints); // Only check if the last segment overlaps the first segment if we have a closed curve. bool doSegmentOverlapCheck = (ii < numPointsToCheck - 1) || wasClosed; if (LineSegmentIsTooShort(finalPoints[numNewPoints - 1], pointXYZs[nextIndex]) || (doSegmentOverlapCheck && LineSegmentsOverlap(finalPoints[numNewPoints - 1], pointXYZs[nextIndex], pointXYZs[nextNextIndex]))) { if (points != null) { badIds.Add(points[nextIndex].StepId); } else { badIds.Add(nextIndex + 1); } } else { finalPoints.Add(pointXYZs[nextIndex]); numNewPoints++; } } // Check final segment; if too short, delete 2nd to last point instead of the last. if (wasClosed) { if (numNewPoints < 4) { return(null); } bool isClosed = finalPoints[numNewPoints - 1].IsAlmostEqualTo(finalPoints[0]); // Do we have a closed loop now? if (wasClosed && !isClosed) // If we had a closed loop, and now we don't, fix it up. { // Presumably, the second-to-last point had to be very close to the last point, or we wouldn't have removed the last point. // So instead of creating a too-short segment, we replace the last point of the new point list with the last point of the original point list. finalPoints[numNewPoints - 1] = pointXYZs[numPoints - 1]; // Now we have to check that we didn't inadvertently make a "too-short" segment. for (int ii = numNewPoints - 1; ii > 0; ii--) { if (IFCGeometryUtil.LineSegmentIsTooShort(finalPoints[ii], finalPoints[ii - 1])) { // TODO: log this removal. finalPoints.RemoveAt(ii - 1); // Remove the intermediate point, not the last point. numNewPoints--; } else { break; // We are in the clear, unless we removed too many points - we've already checked the rest of the loop. } } } if (numNewPoints < 4) { return(null); } } // This can be a very common warning, so we will restrict to verbose logging. if (Importer.TheOptions.VerboseLogging) { if (badIds.Count > 0) { int count = badIds.Count; string msg = null; if (count == 1) { msg = "Polyline had 1 point that was too close to one of its neighbors, removing point: #" + badIds[0] + "."; } else { msg = "Polyline had " + count + " points that were too close to one of their neighbors, removing points:"; foreach (int badId in badIds) { msg += " #" + badId; } msg += "."; } Importer.TheLog.LogWarning(id, msg, false); } } if (numNewPoints < numMinPoints) { if (Importer.TheOptions.VerboseLogging) { string msg = "PolyCurve had " + numNewPoints + " point(s) after removing points that were too close, expected at least " + numMinPoints + ", ignoring."; Importer.TheLog.LogWarning(id, msg, false); } return(null); } CurveLoop curveLoop = new CurveLoop(); for (int ii = 0; ii < numNewPoints - 1; ii++) { curveLoop.Append(Line.CreateBound(finalPoints[ii], finalPoints[ii + 1])); } return(curveLoop); }
/// <summary> /// Creates an open or closed CurveLoop from a list of vertices. /// </summary> /// <param name="pointXYZs">The list of vertices.</param> /// <param name="points">The optional list of IFCAnyHandles that generated the vertices, used solely for error reporting.</param> /// <param name="id">The id of the IFCAnyHandle associated with the CurveLoop.</param> /// <param name="isClosedLoop">True if the vertices represent a closed loop, false if not.</param> /// <returns>The new curve loop.</returns> /// <remarks>If isClosedLoop is true, there will be pointsXyz.Count line segments. Otherwise, there will be pointsXyz.Count-1.</remarks> public static CurveLoop CreatePolyCurveLoop(IList <XYZ> pointXYZs, IList <IFCAnyHandle> points, int id, bool isClosedLoop) { int numPoints = pointXYZs.Count; if (numPoints < 2) { return(null); } IList <int> badIds = new List <int>(); int numMinPoints = isClosedLoop ? 3 : 2; // Check distance between points; remove too-close points, and warn if result is non-collinear. // Always include first point. IList <XYZ> finalPoints = new List <XYZ>(); finalPoints.Add(pointXYZs[0]); int numNewPoints = 1; for (int ii = 1; ii < numPoints; ii++) { if (IFCGeometryUtil.LineSegmentIsTooShort(finalPoints[numNewPoints - 1], pointXYZs[ii])) { if (points != null) { badIds.Add(points[ii].StepId); } else { badIds.Add(ii + 1); } } else { finalPoints.Add(pointXYZs[ii]); numNewPoints++; } } // Check final segment; if too short, delete 2nd to last point. if (isClosedLoop) { if (IFCGeometryUtil.LineSegmentIsTooShort(finalPoints[numNewPoints - 1], pointXYZs[0])) { finalPoints.RemoveAt(numNewPoints - 1); numNewPoints--; } } // This can be a very common warning, so we will restrict to verbose logging. if (Importer.TheOptions.VerboseLogging) { if (badIds.Count > 0) { int count = badIds.Count; string msg = null; if (count == 1) { msg = "Polyline had 1 point that was too close to one of its neighbors, removing point: #" + badIds[0] + "."; } else { msg = "Polyline had " + count + " points that were too close to one of their neighbors, removing points:"; foreach (int badId in badIds) { msg += " #" + badId; } msg += "."; } IFCImportFile.TheLog.LogWarning(id, msg, false); } } if (numNewPoints < numMinPoints) { if (Importer.TheOptions.VerboseLogging) { string msg = "PolyCurve had " + numNewPoints + " point(s) after removing points that were too close, expected at least " + numMinPoints + ", ignoring."; IFCImportFile.TheLog.LogWarning(id, msg, false); } return(null); } CurveLoop curveLoop = new CurveLoop(); for (int ii = 0; ii < numNewPoints - 1; ii++) { curveLoop.Append(Line.CreateBound(finalPoints[ii], finalPoints[ii + 1])); } if (isClosedLoop) { curveLoop.Append(Line.CreateBound(finalPoints[numNewPoints - 1], finalPoints[0])); } return(curveLoop); }