Skip to content
New issue

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

Convert To C# #5

Open
Scarraluba opened this issue Apr 16, 2024 · 0 comments
Open

Convert To C# #5

Scarraluba opened this issue Apr 16, 2024 · 0 comments

Comments

@Scarraluba
Copy link

Scarraluba commented Apr 16, 2024

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);
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant