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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
|
#pragma once
#include "StarMatrix3.hpp"
namespace Star {
template <typename T, size_t N>
class Line {
public:
typedef Vector<T, N> VectorType;
struct IntersectResult {
// Whether or not the two objects intersect
bool intersects;
// Where the intersection is (minimum value if intersection occurs in more
// than one point.)
VectorType point;
// T value where intersection occurs, 0 is min, 1 is max
T t;
// Whether or not the two lines, if they were infinite lines, are the exact
// same line
bool coincides;
// Whether or not the intersection is a glancing one, meaning the other
// line isn't actually skewered, it's just barely touching Coincidental
// lines are always glancing intersections.
bool glances;
};
Line() {}
template <typename T2>
explicit Line(Line<T2, N> const& line)
: m_min(line.min()), m_max(line.max()) {}
Line(VectorType const& a, VectorType const& b)
: m_min(a), m_max(b) {}
VectorType direction() const {
return diff().normalized();
}
T length() const {
return diff().magnitude();
}
T angle() const {
return diff().angle();
}
VectorType eval(T t) const {
return m_min + diff() * t;
}
VectorType diff() const {
return (m_max - m_min);
}
VectorType center() const {
return (m_min + m_max) / 2;
}
void setCenter(VectorType c) {
return translate(c - center());
}
VectorType& min() {
return m_min;
}
VectorType& max() {
return m_max;
}
VectorType const& min() const {
return m_min;
}
VectorType const& max() const {
return m_max;
}
VectorType midpoint() const {
return (m_max + m_min) / 2;
}
bool makePositive() {
bool changed = false;
for (unsigned i = 0; i < N; i++) {
if (m_min[i] < m_max[i]) {
break;
} else if (m_min[i] > m_max[i]) {
std::swap(m_min, m_max);
changed = true;
break;
}
}
return changed;
}
void reverse() {
std::swap(m_min, m_max);
}
Line reversed() {
return Line(m_max, m_min);
}
void translate(VectorType const& trans) {
m_min += trans;
m_max += trans;
}
Line translated(VectorType const& trans) {
return Line(m_min + trans, m_max + trans);
}
void scale(VectorType const& s, VectorType const& c = VectorType()) {
m_min = vmult(m_min - c, s) + c;
m_max = vmult(m_max - c, s) + c;
}
void scale(T s, VectorType const& c = VectorType()) {
scale(VectorType::filled(s), c);
}
bool operator==(Line const& rhs) const {
return tie(m_min, m_max) == tie(rhs.m_min, rhs.m_max);
}
bool operator<(Line const& rhs) const {
return tie(m_min, m_max) < tie(rhs.m_min, rhs.m_max);
}
// Line2
template <size_t P = N>
typename std::enable_if<P == 2 && N == P, IntersectResult>::type intersection(
Line const& line2, bool infinite = false) const {
Line l1 = *this;
Line l2 = line2;
// Warning to others, do not make the lines positive, because points of
// intersection for coincidental lines are determined by the first point
// And makePositive() changes the order of points. This causes headaches
// later on
// l1.makePositive();
// l2.makePositive();
VectorType a = l1.min();
VectorType b = l1.max();
VectorType c = l2.min();
VectorType d = l2.max();
VectorType ab = diff();
VectorType cd = l2.diff();
T denom = ab ^ cd;
T xNumer = (a ^ b) * cd[0] - (c ^ d) * ab[0];
T yNumer = (a ^ b) * cd[1] - (c ^ d) * ab[1];
IntersectResult isect;
if (nearZero(denom)) { // the lines are parallel unless
if (nearZero(xNumer) && nearZero(yNumer)) { // the lines are coincidental
isect.intersects = infinite || (a >= c && a <= d) || (c >= a && c <= b);
if (isect.intersects) {
// returns the minimum intersection point
if (infinite) {
isect.point = VectorType::filled(-std::numeric_limits<T>::max());
} else {
isect.point = a < c ? c : a;
}
}
if (a < c) {
if (c[0] != a[0]) {
isect.t = (c[0] - a[0]) / ab[0];
} else {
isect.t = (c[1] - a[1]) / ab[1];
}
} else if (a > d) {
if (d[0] != a[0]) {
isect.t = (d[0] - a[0]) / ab[0];
} else {
isect.t = (d[1] - a[1]) / ab[1];
}
} else {
isect.t = 0;
}
isect.coincides = true;
isect.glances = isect.intersects;
} else {
isect.intersects = false;
isect.t = std::numeric_limits<T>::max();
isect.point = VectorType();
isect.coincides = false;
isect.glances = false;
}
} else {
T ta = ((c - a) ^ cd) / denom;
T tb = ((c - a) ^ ab) / denom;
isect.intersects = infinite || (ta >= 0 && ta <= 1.0 && tb >= 0 && tb <= 1.0);
isect.t = ta;
isect.point = VectorType(ta * (b[0] - a[0]) + a[0], ta * (b[1] - a[1]) + a[1]);
isect.coincides = false;
isect.glances = !infinite && isect.intersects && (nearZero(ta) || nearEqual(ta, 1.0f) || nearZero(tb) || nearEqual(tb, 1.0f));
}
return isect;
}
template <size_t P = N>
typename std::enable_if<P == 2 && N == P, bool>::type intersects(Line const& l2, bool infinite = false) const {
return intersection(l2, infinite).intersects;
}
// Returns t value for closest point on the line. t value is *not* clamped
// from 0.0 to 1.0
template <size_t P = N>
typename std::enable_if<P == 2 && N == P, T>::type lineProjection(VectorType const& l2) const {
VectorType d = diff();
return ((l2[0] - min()[0]) * d[0] + (l2[1] - min()[1]) * d[1]) / d.magnitudeSquared();
}
template <size_t P = N>
typename std::enable_if<P == 2 && N == P, T>::type distanceTo(VectorType const& l, bool infinite = false) const {
auto t = lineProjection(l);
if (!infinite)
t = clamp<T>(t, 0, 1);
return vmag(l - eval(t));
}
template <size_t P = N>
typename std::enable_if<P == 2 && N == P, void>::type rotate(
T angle, VectorType const& rotationCenter = VectorType()) {
auto rotMatrix = Mat3F::rotation(angle, rotationCenter);
min() = rotMatrix.transformVec2(min());
max() = rotMatrix.transformVec2(max());
}
template <typename T2, size_t P = N>
typename std::enable_if<P == 2 && N == P, void>::type transform(Matrix3<T2> const& transform) {
min() = transform.transformVec2(min());
max() = transform.transformVec2(max());
}
template <typename T2, size_t P = N>
typename std::enable_if<P == 2 && N == P, Line>::type transformed(Matrix3<T2> const& transform) const {
return Line(transform.transformVec2(min()), transform.transformVec2(max()));
}
template <size_t P = N>
typename std::enable_if<P == 2 && N == P, void>::type flipHorizontal(T horizontalPos) {
m_min[0] = horizontalPos + (horizontalPos - m_min[0]);
m_max[0] = horizontalPos + (horizontalPos - m_max[0]);
}
template <size_t P = N>
typename std::enable_if<P == 2 && N == P, void>::type flipVertical(T verticalPos) {
m_min[1] = verticalPos + (verticalPos - m_min[1]);
m_max[1] = verticalPos + (verticalPos - m_max[1]);
}
private:
VectorType m_min;
VectorType m_max;
};
typedef Line<float, 2> Line2F;
typedef Line<double, 2> Line2D;
typedef Line<int, 2> Line2I;
template <typename T, size_t N>
std::ostream& operator<<(std::ostream& os, Line<T, N> const& l) {
os << '[' << l.min() << ", " << l.max() << ']';
return os;
}
template <typename T, size_t N>
struct hash<Line<T, N>> {
size_t operator()(Line<T, N> const& line) const {
size_t hashval = 0;
hashCombine(hashval, vectorHasher(line.min()));
hashCombine(hashval, vectorHasher(line.max()));
return hashval;
}
Star::hash<typename Line<T, N>::VectorType> vectorHasher;
};
}
template <typename T, size_t N>
struct fmt::formatter<Star::Line<T, N>> : ostream_formatter {};
|