forked from mateusvrs/competitive-programming
-
Notifications
You must be signed in to change notification settings - Fork 0
/
polygon.cpp
146 lines (112 loc) · 3.39 KB
/
polygon.cpp
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
template <typename T>
class Polygon {
private:
vector<Point<T>> vs;
int n;
public:
// O parâmetro deve conter os n vértices do polígono
Polygon(const vector<Point<T>>& ps) : vs(ps), n(vs.size()) {
vs.push_back(vs.front());
}
private:
T D(const Point<T>& P, const Point<T>& Q, const Point<T>& R) const {
return (P.x * Q.y + P.y * R.x + Q.x * R.y) -
(R.x * Q.y + R.y * P.x + Q.x * P.y);
}
public:
bool convex() const {
// Um polígono deve ter, no minimo, 3 vértices
if (n < 3) return false;
int P = 0, N = 0, Z = 0;
for (int i = 0; i < n; ++i) {
auto d = D(vs[i], vs[(i + 1) % n], vs[(i + 2) % n]);
d ? (d > 0 ? ++P : ++N) : ++Z;
}
return P == n or N == n;
}
private:
double distance(const Point<T>& P, const Point<T>& Q) {
return hypot(P.x - Q.x, P.y - Q.y);
}
public:
double perimeter() const {
auto p = 0.0;
for (int i = 0; i < n; ++i) p += distance(vs[i], vs[i + 1]);
return p;
}
double area() const {
auto a = 0.0;
for (int i = 0; i < n; ++i) {
a += vs[i].x * vs[i + 1].y;
a -= vs[i + 1].x * vs[i].y;
}
return 0.5 * fabs(a);
}
private:
// Ângulo APB, em radianos
double angle(const Point<T>& P, const Point<T>& A, const Point<T>& B) {
auto ux = P.x - A.x;
auto uy = P.y - A.y;
auto vx = P.x - B.x;
auto vy = P.y - B.y;
auto num = ux * vx + uy * vy;
auto den = hypot(ux, uy) * hypot(vx, vy);
// Caso especial: se den == 0, algum dos vetores é degenerado: os
// dois pontos são iguais. Neste caso, o ângulo não está definido
return acos(num / den);
}
bool equals(double x, double y) {
static const double EPS{1e-6};
return fabs(x - y) < EPS;
}
public:
bool contains(const Point<T>& P) const {
if (n < 3) return false;
auto sum = 0.0;
for (int i = 0; i < n - 1; ++i) {
auto d = D(P, vs[i], vs[i + 1]);
auto a = angle(P, vs[i], vs[i + 1]);
sum += d > 0 ? a : (d < 0 ? -a : 0);
}
static const double PI = acos(-1.0);
return equals(fabs(sum), 2 * PI);
}
private:
// Interseção entre a reta AB e o segmento de reta PQ
Point<T> intersection(const Point<T>& P, const Point<T>& Q, const Point<T>& A,
const Point<T>& B) {
auto a = B.y - A.y;
auto b = A.x - B.x;
auto c = B.x * A.y - A.x * B.y;
auto u = fabs(a * P.x + b * P.y + c);
auto v = fabs(a * Q.x + b * Q.y + c);
// Média ponderada pelas distâncias de P e Q até a reta AB
return {(P.x * v + Q.x * u) / (u + v), (P.y * v + Q.y * u) / (u + v)};
}
public:
// Corta o polígono com a reta r que passa por A e B
Polygon cut_polygon(const Point<T>& A, const Point<T>& B) const {
vector<Point<T>> points;
const double EPS{1e-6};
for (int i = 0; i < n; ++i) {
auto d1 = D(A, B, vs[i]);
auto d2 = D(A, B, vs[i + 1]);
// Vértice à esquerda da reta
if (d1 > -EPS) points.push_back(vs[i]);
// A aresta cruza a reta
if (d1 * d2 < -EPS)
points.push_back(intersection(vs[i], vs[i + 1], A, B));
}
return Polygon(points);
}
double circumradius() const {
auto s = distance(vs[0], vs[1]);
const double PI{acos(-1.0)};
return (s / 2.0) * (1.0 / sin(PI / n));
}
double apothem() const {
auto s = distance(vs[0], vs[1]);
const double PI{acos(-1.0)};
return (s / 2.0) * (1.0 / tan(PI / n));
}
};