We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; [ExecuteAlways] public class Squarer : MonoBehaviour { public List<Vector2> originalPolygonVertices; public float maxAngleChange = 10f; public float skewTolerance = 10f; void OnDrawGizmos() { Bounds boundingBox = CalculateBoundingBox(originalPolygonVertices); DrawBoundingBox(boundingBox); DrawPolygon(originalPolygonVertices, Color.green); DrawPolygon(OrthPolygon(originalPolygonVertices, maxAngleChange, skewTolerance), Color.magenta); // Debug.LogError(.Count); // Handle the returned values as needed /*Debug.Log("Original Angles: " + string.Join(", ", orgAngles)); Debug.Log("Correction Angles: " + string.Join(", ", corAngles)); Debug.Log("Directional Angles: " + string.Join(", ", dirAngles));*/ } public List<Vector2> OrthPolygon(List<Vector2> polygon, float maxAngleChange = 15f, float skewTolerance = 15f) { List<Vector2> poly = new List<Vector2>(); List<float> orgAngles; List<float> corAngles; List<int> dirAngles; (orgAngles, corAngles, dirAngles) = CalculateSegmentAngles(originalPolygonVertices); float medAngle = 0; if (StandardDeviation(corAngles) < 30) { medAngle = Median(corAngles); } else { medAngle = 45; } List<Vector2> polySimpleR = RotatePolygon(polygon, medAngle); (orgAngles, corAngles, dirAngles) = CalculateSegmentAngles(polySimpleR, maxAngleChange); List<float> rotatedX = new List<float>(); List<float> rotatedY = new List<float>(); foreach (Vector2 vertex in polySimpleR) { rotatedX.Add(vertex.x); rotatedY.Add(vertex.y); } // Scan backwards to check if starting segment is a continuation of straight region in the same direction int shift = 0; for (int i = 1; i < dirAngles.Count; i++) { if (dirAngles[0] == dirAngles[^i]) { shift = i; } else { break; } } // If the first segment is part of continuing straight region then reset the index to its beginning if (shift != 0) { dirAngles = dirAngles.GetRange(dirAngles.Count - shift, shift) .Concat(dirAngles.GetRange(0, dirAngles.Count - shift)).ToList(); orgAngles = orgAngles.GetRange(orgAngles.Count - shift, shift) .Concat(orgAngles.GetRange(0, orgAngles.Count - shift)).ToList(); rotatedX = rotatedX.GetRange(rotatedX.Count - shift - 1, shift) .Concat(rotatedX.GetRange(0, rotatedX.Count - shift)).ToList(); rotatedY = rotatedY.GetRange(rotatedY.Count - shift - 1, shift) .Concat(rotatedY.GetRange(0, rotatedY.Count - shift)).ToList(); } // Fix 180 degree turns (N->S, S->N, E->W, W->E) // Subtract two adjacent directions and if the difference is 2, which means we have 180˚ turn (0,1,3 are OK) then use the direction of the previous segment List<int> dirAngleRoll = dirAngles.GetRange(1, dirAngles.Count - 1) .Concat(dirAngles.GetRange(0, 1)).ToList(); dirAngles = dirAngles.Select((value, index) => MathF.Abs(value - dirAngleRoll[index]) == 2 ? dirAngles[index - 1] : value).ToList(); // Assuming dirAngle, orgAngle, rotatedX, rotatedY, skewTolerance, and shift are defined elsewhere in the code dirAngles.Add(dirAngles[0]); // Append dummy value orgAngles.Add(orgAngles[0]); // Append dummy value List<int> segmentBuffer = new List<int>(); // Buffer for determining which segments are part of one large straight line for (int i = 0; i < dirAngles.Count - 1; i++) { // Preserving skewed walls: Leave walls that are obviously meant to be skewed 45˚+/- tolerance˚ (e.g.angle 30-60 degrees) off main walls as untouched if (orgAngles[i] % 90 > (45 - skewTolerance) && orgAngles[i] % 90 < (45 + skewTolerance)) { continue; } // Dealing with adjacent segments following the same direction segmentBuffer.Add(i); if (dirAngles[i] == dirAngles [i + 1]) // If next segment is of same orientation, we need 180 deg angle for straight line. Keep checking. { if (orgAngles[i + 1] % 90 > (45 - skewTolerance) && orgAngles[i + 1] % 90 < (45 + skewTolerance)) { continue; } } if (dirAngles[i] == 0 || dirAngles[i] == 2) // for N,S segments average x coordinate { float tempX = rotatedX.GetRange(segmentBuffer[0], segmentBuffer.Count).Average(); // Update with new coordinates for (int j = segmentBuffer[0]; j <= segmentBuffer[segmentBuffer.Count - 1]; j++) { rotatedX[j] = tempX; } } else if (dirAngles[i] == 1 || dirAngles[i] == 3) // for E,W segments average y coordinate { float tempY = rotatedY.GetRange(segmentBuffer[0], segmentBuffer.Count).Average(); // Update with new coordinates for (int j = segmentBuffer[0]; j <= segmentBuffer[segmentBuffer.Count - 1]; j++) { rotatedY[j] = tempY; } } if (segmentBuffer.Contains(0)) // Copy change in first point to its last point so we don't lose it during Reverse shift { rotatedX[rotatedX.Count - 1] = rotatedX[0]; rotatedY[rotatedY.Count - 1] = rotatedY[0]; } segmentBuffer.Clear(); } // Reverse shift so we get polygon with the same start/end point as before if (shift != 0) { rotatedX = rotatedX.Skip(shift).Concat(rotatedX.Take(shift + 1)).ToList(); rotatedY = rotatedY.Skip(shift).Concat(rotatedY.Take(shift + 1)).ToList(); } else { rotatedX[0] = rotatedX[rotatedX.Count - 1]; // Copy updated coordinates to first node rotatedY[0] = rotatedY[rotatedY.Count - 1]; } for (int i = 0; i < rotatedX.Count; i++) { poly.Add(new Vector2(rotatedX[i], rotatedY[i])); } return RotatePolygon(poly, -medAngle); } /** :Returns: - orgAngle: Segments bearing - corAngle: Segments angles to closest cardinal direction - dirAngle: Segments direction [N, E, S, W] as [0, 1, 2, 3]*/ public ( List<float>, List<float>, List<int>) CalculateSegmentAngles(List<Vector2> polySimple, float maxAngleChange = 45f) { if (polySimple == null || polySimple.Count < 2) { Debug.LogError("Polygon should contain at least two points."); return (null, null, null); } List<float> orgAngle = new List<float>(); List<float> corAngle = new List<float>(); List<int> dirAngle = new List<int>(); float[] limit = new float[4]; maxAngleChange = 45 - maxAngleChange; for (int i = 0; i < 4; i++) { limit[i] = 0; } // Calculate angles between consecutive segments for (int i = 0; i < polySimple.Count; i++) { Vector2 point1 = polySimple[i]; Vector2 point2 = polySimple[(i + 1) % polySimple.Count]; float angle = CalculateInitialCompassBearing(point1, point2); if (angle > 45 + limit[1] && angle <= 135 - limit[1]) { orgAngle.Add(angle); corAngle.Add(angle - 90); dirAngle.Add(1); } else if (angle > 135 + limit[2] && angle <= 225 - limit[2]) { orgAngle.Add(angle); corAngle.Add(angle - 180); dirAngle.Add(2); } else if (angle > (225 + limit[3]) && angle <= (315 - limit[3])) { orgAngle.Add(angle); corAngle.Add(angle - 270); dirAngle.Add(3); } else if (angle > (315 + limit[0]) && angle <= 360) { orgAngle.Add(angle); corAngle.Add(angle - 360); dirAngle.Add(0); } else if (angle >= 0 && angle <= (45 - limit[0])) { orgAngle.Add(angle); corAngle.Add(angle); dirAngle.Add(0); } for (int b = 0; b < 4; b++) { limit[b] = 0; } limit[dirAngle[i]] = maxAngleChange; limit[(dirAngle[i] + 1) % 4] = -maxAngleChange; limit[(dirAngle[i] - 1 + 4) % 4] = -maxAngleChange; } return (orgAngle, corAngle, dirAngle); } public List<Vector2> RotatePolygon(List<Vector2> polygon, float angle) { Vector2 centroid = CalculateCentroid(polygon); List<Vector2> rotatedPolygon = new List<Vector2>(); foreach (Vector2 vertex in polygon) { Vector2 rotatedVertex = RotatePoint(vertex, centroid, angle); rotatedPolygon.Add(rotatedVertex); } return rotatedPolygon; } private Vector2 RotatePoint(Vector2 point, Vector2 pivot, float angle) { float angleInRadians = angle * Mathf.Deg2Rad; float cosTheta = Mathf.Cos(angleInRadians); float sinTheta = Mathf.Sin(angleInRadians); float newX = cosTheta * (point.x - pivot.x) - sinTheta * (point.y - pivot.y) + pivot.x; float newY = sinTheta * (point.x - pivot.x) + cosTheta * (point.y - pivot.y) + pivot.y; return new Vector2(newX, newY); } private Vector2 CalculateCentroid(List<Vector2> polygon) { Vector2 centroid = Vector2.zero; foreach (Vector2 vertex in polygon) { centroid += vertex; } centroid /= polygon.Count; return centroid; } public float CalculateInitialCompassBearing(Vector2 pointA, Vector2 pointB) { // Check if the inputs are vectors if (!(pointA is Vector2) || !(pointB is Vector2)) { throw new ArgumentException("Only vectors are supported as arguments"); } // Convert latitude and longitude from degrees to radians float lat1 = Mathf.Deg2Rad * pointA.x; float lat2 = Mathf.Deg2Rad * pointB.x; float diffLong = Mathf.Deg2Rad * (pointB.y - pointA.y); float x = Mathf.Sin(diffLong) * Mathf.Cos(lat2); float y = Mathf.Cos(lat1) * Mathf.Sin(lat2) - (Mathf.Sin(lat1) * Mathf.Cos(lat2) * Mathf.Cos(diffLong)); // Calculate the initial bearing float initialBearing = Mathf.Atan2(x, y); // Normalize the initial bearing to be between 0 and 360 degrees float compassBearing = (Mathf.Rad2Deg * initialBearing + 360) % 360; return compassBearing; } void DrawBoundingBox(Bounds boundingBox) { Vector3 min = boundingBox.min; Vector3 max = boundingBox.max; Vector3[] corners = new Vector3[] { new Vector3(min.x, 0, min.y), new Vector3(min.x, 0, max.y), new Vector3(max.x, 0, max.y), new Vector3(max.x, 0, min.y), new Vector3(min.x, 0, min.y) }; for (int i = 0; i < corners.Length - 1; i++) { Gizmos.color = new Color(1f, 0.69f, 0.16f); Gizmos.DrawLine(corners[i], corners[i + 1]); } } void DrawPolygon(List<Vector2> vertices, Color color) { for (int i = 0; i < vertices.Count; i++) { Vector2 start = vertices[i]; Vector2 end = vertices[(i + 1) % vertices.Count]; Gizmos.color = color; Gizmos.DrawLine(new Vector3(start.x, 0, start.y), new Vector3(end.x, 0, end.y)); Debug.LogError(start); } } Bounds CalculateBoundingBox(List<Vector2> vertices) { Vector2 min = vertices[0]; Vector2 max = vertices[0]; foreach (Vector2 vertex in vertices) { min = Vector2.Min(min, vertex); max = Vector2.Max(max, vertex); } return new Bounds((min + max) / 2, max - min); } public static float Median(IEnumerable<float> source) { var sorted = source.OrderBy(x => x).ToList(); var count = sorted.Count; if (count % 2 == 0) { return (sorted[count / 2 - 1] + sorted[count / 2]) / 2; } else { return sorted[count / 2]; } } public static float StandardDeviation(IEnumerable<float> source) { var values = source.ToList(); var count = values.Count; var mean = values.Average(); var sumOfSquares = values.Sum(x => Mathf.Pow(x - mean, 2)); return Mathf.Sqrt(sumOfSquares / count); } }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
The text was updated successfully, but these errors were encountered: