-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathPolygonInfo.cs
117 lines (103 loc) · 4.02 KB
/
PolygonInfo.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Mathematics;
namespace Evryway
{
public class PolygonInfo
{
public bool valid;
public bool convex;
public bool clockwise;
public double area;
public string info { get => $"valid:{valid} convex:{convex} clockwise:{clockwise} area:{area}"; }
const float twopi = Mathf.PI * 2.0f;
const float ang_delta_epsilon = 0.001f;
static Vector2[] edges = new Vector2[65536];
public static PolygonInfo invalid = new PolygonInfo { valid = false, convex = false, clockwise = false, area = 0 };
// points are all on the XY plane.
public static PolygonInfo Create(Vector2[] points)
{
if (points.Length < 3) return invalid;
var area = PolygonArea(points);
if (area == 0) return invalid;
int pc = points.Length;
if (pc > edges.Length) edges = new Vector2[pc * 2];
for (int i = 0; i < pc; i++)
{
var ip1 = (i + 1) % pc;
var pi = points[i];
var pip1 = points[ip1];
var edge = new Vector2(pip1.x - pi.x, pip1.y - pi.y);
if (math.length(edge) <= 0.0f)
{
return invalid;
}
edges[i] = edge;
}
return CreateFromEdges(edges, pc, area);
}
public static PolygonInfo CreateFromEdges(Vector2[] edges, int count, double area)
{
bool convex = true;
float ang_dir_expected = 0.0f;
double ang_delta_total = 0.0;
for (int i = 0; i < count; i++)
{
var ip1 = (i + 1) % count;
var e1 = edges[i];
var e2 = edges[ip1];
var ang1 = math.atan2(e1.y, e1.x);
var ang2 = math.atan2(e2.y, e2.x);
var ang_delta = ang2 - ang1;
if (ang_delta < -Mathf.PI) ang_delta += (twopi);
else if (ang_delta > Mathf.PI) ang_delta -= (twopi);
// clockwise goes negative (see Atan2 deets above). anticlockwise goes positive.
var ang_dir = ang_delta >= 0.0f ? 1.0f : -1.0f;
if (i == 0)
{
// first edge, set expected direction. -1 ? clockwise. 1 ? anticlockwise.
ang_dir_expected = ang_dir;
}
else
{
// if our expected direction varies, we're not convex.
if (ang_dir_expected != ang_dir)
{
convex = false;
}
}
ang_delta_total += ang_delta;
}
// ang_delta_total should be either -2*PI or 2*PI (within error bounds)
// if it's not, we've got a poly that's self-winding more than once (e.g. star, bowtie, that sort of thing)
// it's probably self-intersecting in that case ...
if (convex)
{
var c = (ang_delta_total * ang_dir_expected);
if (math.abs(c - twopi) > ang_delta_epsilon)
{
convex = false;
}
}
// if ang_delta_total is negative, we've gone clockwise around.
bool clockwise = ang_delta_total < 0.0f ? true : false;
bool valid = count > 4 || ang_delta_total != 0;
return new PolygonInfo { valid = valid, convex = convex, clockwise = clockwise, area = area };
}
public static double PolygonArea(Vector2[] points)
{
var pc = points.Length;
double sum = 0;
for (int i = 0 ; i < pc ; i++)
{
var p = points[i];
var j = (i+1) % pc;
var q = points[j];
sum += (q.x-p.x) * (q.y+p.y);
}
sum /= 2;
return sum;
}
}
}