/// <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);
        }
Exemple #2
0
        /// <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);
        }