using System; using System.Collections.Generic; using UnityEngine; namespace Framework.Geometry.Lines.Fitting { /// Fits 2D line segments to geometric data public static class Segment2Fitter { /// Fits a line segment to an arbitrary set of points /// Points to which a line segment will be fitted /// The line segment fitted to the points public static Segment2 Fit(IList points) { // Special logic for point sets with no or just a single point int pointCount = points.Count; switch (pointCount) { case 0: { return new Segment2(); } case 1: { return new Segment2(points[0], points[0]); } } // Fit an infinite line to the point set Line2 fittedLine = Line2Fitter.Fit(points); // Now look for the points furthest to the negative direction of the line and // to the positive direction of the line, also sum the intervals so the direction // of the line segment can be matched to the direction of the point set. float minInterval, maxInterval, intervalSum; { float lastInterval = Vector2.Dot(points[0] - fittedLine.Offset, fittedLine.Direction); intervalSum = 0.0f; minInterval = lastInterval; maxInterval = lastInterval; for (int index = 1; index < pointCount; ++index) { float interval = Vector2.Dot(points[index] - fittedLine.Offset, fittedLine.Direction); if (interval < minInterval) { minInterval = interval; } if (interval > maxInterval) { maxInterval = interval; } intervalSum += (interval - lastInterval); lastInterval = interval; } } // If the interval sum was negative, the points are sorted in reverse to // the positive direction of the line, so flip it around if(intervalSum >= 0) { return new Segment2( fittedLine.Offset + (minInterval * fittedLine.Direction), fittedLine.Offset + (maxInterval * fittedLine.Direction) ); } else { return new Segment2( fittedLine.Offset + (maxInterval * fittedLine.Direction), fittedLine.Offset + (minInterval * fittedLine.Direction) ); } } } } // namespace Framework.Geometry.Lines.Fitting