290 lines
7.8 KiB
C++
290 lines
7.8 KiB
C++
|
#ifndef STAR_LINE_HPP
|
||
|
#define STAR_LINE_HPP
|
||
|
|
||
|
#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;
|
||
|
};
|
||
|
|
||
|
}
|
||
|
|
||
|
#endif
|