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