Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/core/StarLine.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/core/StarLine.hpp')
-rw-r--r--source/core/StarLine.hpp289
1 files changed, 289 insertions, 0 deletions
diff --git a/source/core/StarLine.hpp b/source/core/StarLine.hpp
new file mode 100644
index 0000000..ccb3e26
--- /dev/null
+++ b/source/core/StarLine.hpp
@@ -0,0 +1,289 @@
+#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