using System; using System.Collections.Generic; using UnityEngine; namespace Framework.Geometry.Lines.Fitting { /// Fits 2D lines to different kinds of inputs public static class Line2Fitter { /// Fits a line to an arbitrary set of points /// Points to which a line will be fitted /// The line fitted to the points public static Line2 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 Line2(Vector2.zero, Vector2.right); ; } case 1: { return new Line2(points[0], Vector2.right); } } // Calculate sums and mean values required to figure out the slope of the line float meanX, meanY, sumX, sumY, sumXY, sumX2, sumY2; { sumX = sumY = sumXY = sumX2 = sumY2 = 0.0f; for (int index = 0; index < pointCount; ++index) { float x = points[index].x; float y = points[index].y; sumX += x; sumY += y; sumXY += (x * y); sumX2 += (x * x); sumY2 += (y * y); } meanX = sumX / pointCount; meanY = sumY / pointCount; } // Set up a line following the points as either an X-major or an Y-major slope Line2 result; { float meanSumX = sumX * meanX; float meanSumY = sumY * meanY; float denominatorMajorX = sumX2 - meanSumX; float denominatorMajorY = sumY2 - meanSumY; // Is this an X-major (horizontal) slope? if (Math.Abs(denominatorMajorX) >= Math.Abs(denominatorMajorY)) { // Is there no slope at all (all points clumped in a single spot)? if (Mathf.Approximately(meanSumX, sumX2)) { result.Offset = new Vector2(meanX, meanY); result.Direction = Vector2.right; } else { // Horizontal line float slope = (sumXY - (sumX * meanY)) / denominatorMajorX; result.Offset.x = 0.0f; result.Offset.y = (meanY) - (slope * meanX); result.Direction = buildNormalizedVector(1.0f, slope); } } else { // It's an Y-major (vertical) slope // Is there no slope at all (all points clumped in a single spot)? if(Mathf.Approximately(meanSumY, sumY2)) { result.Offset = new Vector2(meanX, meanY); result.Direction = Vector2.right; } else { float slope = (sumXY - (sumY * meanX)) / denominatorMajorY; result.Offset.x = (meanX) - (slope * meanY); result.Offset.y = 0.0f; result.Direction = buildNormalizedVector(slope, 1.0f); } } } return result; } /// Constructs a new vector and normalized it /// X length of the vector before normalization /// Y length of the vector before normalization /// The normalized vector private static Vector2 buildNormalizedVector(float x, float y) { var vector = new Vector2(x, y); vector.Normalize(); return vector; } } } // namespace Framework.Geometry.Lines.Fitting