//----------------------------------------------------------------------------- // Data structures used frequently in the program, various kinds of vectors // (of real numbers, not symbolic algebra stuff) and our templated lists. // // Copyright 2008-2013 Jonathan Westhues. //----------------------------------------------------------------------------- #ifndef SOLVESPACE_DSC_H #define SOLVESPACE_DSC_H #include "solvespace.h" #include #include /// Trait indicating which types are handle types and should get the associated operators. /// Specialize for each handle type and inherit from std::true_type. template struct IsHandleOracle : std::false_type {}; // Equality-compare any two instances of a handle type. template static inline typename std::enable_if::value, bool>::type operator==(T const &lhs, T const &rhs) { return lhs.v == rhs.v; } // Inequality-compare any two instances of a handle type. template static inline typename std::enable_if::value, bool>::type operator!=(T const &lhs, T const &rhs) { return !(lhs == rhs); } // Less-than-compare any two instances of a handle type. template static inline typename std::enable_if::value, bool>::type operator<(T const &lhs, T const &rhs) { return lhs.v < rhs.v; } class Vector; class Vector4; class Point2d; class hEntity; class hParam; class Quaternion { public: // a + (vx)*i + (vy)*j + (vz)*k double w, vx, vy, vz; static const Quaternion IDENTITY; static Quaternion From(double w, double vx, double vy, double vz); static Quaternion From(hParam w, hParam vx, hParam vy, hParam vz); static Quaternion From(Vector u, Vector v); static Quaternion From(Vector axis, double dtheta); Quaternion Plus(Quaternion b) const; Quaternion Minus(Quaternion b) const; Quaternion ScaledBy(double s) const; double Magnitude() const; Quaternion WithMagnitude(double s) const; // Call a rotation matrix [ u' v' n' ]'; this returns the first and // second rows, where that matrix is generated by this quaternion Vector RotationU() const; Vector RotationV() const; Vector RotationN() const; Vector Rotate(Vector p) const; Quaternion ToThe(double p) const; Quaternion Inverse() const; Quaternion Times(Quaternion b) const; Quaternion Mirror() const; }; class Vector { public: double x, y, z; static Vector From(double x, double y, double z); static Vector From(hParam x, hParam y, hParam z); static Vector AtIntersectionOfPlanes(Vector n1, double d1, Vector n2, double d2); static Vector AtIntersectionOfLines(Vector a0, Vector a1, Vector b0, Vector b1, bool *skew, double *pa=NULL, double *pb=NULL); static Vector AtIntersectionOfPlaneAndLine(Vector n, double d, Vector p0, Vector p1, bool *parallel); static Vector AtIntersectionOfPlanes(Vector na, double da, Vector nb, double db, Vector nc, double dc, bool *parallel); static void ClosestPointBetweenLines(Vector pa, Vector da, Vector pb, Vector db, double *ta, double *tb); double Element(int i) const; bool Equals(Vector v, double tol=LENGTH_EPS) const; bool EqualsExactly(Vector v) const; Vector Plus(Vector b) const; Vector Minus(Vector b) const; Vector Negated() const; Vector Cross(Vector b) const; double DirectionCosineWith(Vector b) const; double Dot(Vector b) const; Vector Normal(int which) const; Vector RotatedAbout(Vector orig, Vector axis, double theta) const; Vector RotatedAbout(Vector axis, double theta) const; Vector DotInToCsys(Vector u, Vector v, Vector n) const; Vector ScaleOutOfCsys(Vector u, Vector v, Vector n) const; double DistanceToLine(Vector p0, Vector dp) const; double DistanceToPlane(Vector normal, Vector origin) const; bool OnLineSegment(Vector a, Vector b, double tol=LENGTH_EPS) const; Vector ClosestPointOnLine(Vector p0, Vector deltal) const; double Magnitude() const; double MagSquared() const; Vector WithMagnitude(double s) const; Vector ScaledBy(double s) const; Vector ProjectInto(hEntity wrkpl) const; Vector ProjectVectorInto(hEntity wrkpl) const; double DivProjected(Vector delta) const; Vector ClosestOrtho() const; void MakeMaxMin(Vector *maxv, Vector *minv) const; Vector ClampWithin(double minv, double maxv) const; static bool BoundingBoxesDisjoint(Vector amax, Vector amin, Vector bmax, Vector bmin); static bool BoundingBoxIntersectsLine(Vector amax, Vector amin, Vector p0, Vector p1, bool asSegment); bool OutsideAndNotOn(Vector maxv, Vector minv) const; Vector InPerspective(Vector u, Vector v, Vector n, Vector origin, double cameraTan) const; Point2d Project2d(Vector u, Vector v) const; Point2d ProjectXy() const; Vector4 Project4d() const; }; inline double Vector::Element(int i) const { switch (i) { case 0: return x; case 1: return y; case 2: return z; default: ssassert(false, "Unexpected vector element index"); } } inline bool Vector::Equals(Vector v, double tol) const { // Quick axis-aligned tests before going further const Vector dv = this->Minus(v); if (fabs(dv.x) > tol) return false; if (fabs(dv.y) > tol) return false; if (fabs(dv.z) > tol) return false; return dv.MagSquared() < tol*tol; } inline Vector Vector::From(double x, double y, double z) { return {x, y, z}; } inline Vector Vector::Plus(Vector b) const { return {x + b.x, y + b.y, z + b.z}; } inline Vector Vector::Minus(Vector b) const { return {x - b.x, y - b.y, z - b.z}; } inline Vector Vector::Negated() const { return {-x, -y, -z}; } inline Vector Vector::Cross(Vector b) const { return {-(z * b.y) + (y * b.z), (z * b.x) - (x * b.z), -(y * b.x) + (x * b.y)}; } inline double Vector::Dot(Vector b) const { return (x * b.x + y * b.y + z * b.z); } inline double Vector::MagSquared() const { return x * x + y * y + z * z; } inline double Vector::Magnitude() const { return sqrt(x * x + y * y + z * z); } inline Vector Vector::ScaledBy(const double v) const { return {x * v, y * v, z * v}; } inline void Vector::MakeMaxMin(Vector *maxv, Vector *minv) const { maxv->x = max(maxv->x, x); maxv->y = max(maxv->y, y); maxv->z = max(maxv->z, z); minv->x = min(minv->x, x); minv->y = min(minv->y, y); minv->z = min(minv->z, z); } struct VectorHash { size_t operator()(const Vector &v) const; }; struct VectorPred { bool operator()(Vector a, Vector b) const; }; class Vector4 { public: double w, x, y, z; static Vector4 From(double w, double x, double y, double z); static Vector4 From(double w, Vector v3); static Vector4 Blend(Vector4 a, Vector4 b, double t); Vector4 Plus(Vector4 b) const; Vector4 Minus(Vector4 b) const; Vector4 ScaledBy(double s) const; Vector PerspectiveProject() const; }; class Point2d { public: double x, y; static Point2d From(double x, double y); static Point2d FromPolar(double r, double a); Point2d Plus(const Point2d &b) const; Point2d Minus(const Point2d &b) const; Point2d ScaledBy(double s) const; double DivProjected(Point2d delta) const; double Dot(Point2d p) const; double DistanceTo(const Point2d &p) const; double DistanceToLine(const Point2d &p0, const Point2d &dp, bool asSegment) const; double DistanceToLineSigned(const Point2d &p0, const Point2d &dp, bool asSegment) const; double Angle() const; double AngleTo(const Point2d &p) const; double Magnitude() const; double MagSquared() const; Point2d WithMagnitude(double v) const; Point2d Normal() const; bool Equals(Point2d v, double tol=LENGTH_EPS) const; }; // A simple list template class List { T *elem = nullptr; int elemsAllocated = 0; public: int n = 0; bool IsEmpty() const { return n == 0; } void ReserveMore(int howMuch) { if(n + howMuch > elemsAllocated) { elemsAllocated = n + howMuch; T *newElem = (T *)::operator new[]((size_t)elemsAllocated*sizeof(T)); for(int i = 0; i < n; i++) { new(&newElem[i]) T(std::move(elem[i])); elem[i].~T(); } ::operator delete[](elem); elem = newElem; } } void AllocForOneMore() { if(n >= elemsAllocated) { ReserveMore((elemsAllocated + 32)*2 - n); } } void Add(const T *t) { AllocForOneMore(); new(&elem[n++]) T(*t); } void AddToBeginning(const T *t) { AllocForOneMore(); new(&elem[n]) T(); std::move_backward(elem, elem + 1, elem + n + 1); elem[0] = *t; n++; } T *First() { return IsEmpty() ? nullptr : &(elem[0]); } const T *First() const { return IsEmpty() ? nullptr : &(elem[0]); } T *Last() { return IsEmpty() ? nullptr : &(elem[n - 1]); } const T *Last() const { return IsEmpty() ? nullptr : &(elem[n - 1]); } T *NextAfter(T *prev) { if(IsEmpty() || !prev) return NULL; if(prev - First() == (n - 1)) return NULL; return prev + 1; } const T *NextAfter(const T *prev) const { if(IsEmpty() || !prev) return NULL; if(prev - First() == (n - 1)) return NULL; return prev + 1; } T &Get(size_t i) { return elem[i]; } T const &Get(size_t i) const { return elem[i]; } T &operator[](size_t i) { return Get(i); } T const &operator[](size_t i) const { return Get(i); } T *begin() { return IsEmpty() ? nullptr : &elem[0]; } T *end() { return IsEmpty() ? nullptr : &elem[n]; } const T *begin() const { return IsEmpty() ? nullptr : &elem[0]; } const T *end() const { return IsEmpty() ? nullptr : &elem[n]; } const T *cbegin() const { return begin(); } const T *cend() const { return end(); } void ClearTags() { for(auto & elt : *this) { elt.tag = 0; } } void Clear() { for(int i = 0; i < n; i++) elem[i].~T(); if(elem) ::operator delete[](elem); elem = NULL; n = elemsAllocated = 0; } void RemoveTagged() { auto newEnd = std::remove_if(this->begin(), this->end(), [](T &t) { if(t.tag) { return true; } return false; }); auto oldEnd = this->end(); n = newEnd - begin(); if (newEnd != nullptr && oldEnd != nullptr) { while(newEnd != oldEnd) { newEnd->~T(); ++newEnd; } } // and elemsAllocated is untouched, because we didn't resize } void RemoveLast(int cnt) { ssassert(n >= cnt, "Removing more elements than the list contains"); for(int i = n - cnt; i < n; i++) elem[i].~T(); n -= cnt; // and elemsAllocated is untouched, same as in RemoveTagged } void Reverse() { int i; for(i = 0; i < (n/2); i++) { swap(elem[i], elem[(n-1)-i]); } } }; template class IdList; // Comparison functor used by IdList and related classes template struct CompareId { CompareId(const IdList *list) { idlist = list; } bool operator()(int lhs, T const& rhs) const { return idlist->elemstore[lhs].h.v < rhs.h.v; } bool operator()(int lhs, H rhs) const { return idlist->elemstore[lhs].h.v < rhs.v; } bool operator()(T *lhs, int rhs) const { return lhs->h.v < idlist->elemstore[rhs].h.v; } private: const IdList *idlist; }; // A list, where each element has an integer identifier. The list is kept // sorted by that identifier, and items can be looked up in log n time by // id. template class IdList { std::vector elemstore; std::vector elemidx; std::vector freelist; public: int n = 0; // PAR@@@@@ make this private to see all interesting and suspicious places in SoveSpace ;-) friend struct CompareId; using Compare = CompareId; struct iterator { typedef std::random_access_iterator_tag iterator_category; typedef T value_type; typedef int difference_type; typedef T *pointer; typedef T &reference; public: T &operator*() const noexcept { return *elem; } const T *operator->() const noexcept { return elem; } bool operator==(const iterator &p) const { return p.position == position; } bool operator!=(const iterator &p) const { return !operator==(p); } iterator &operator++() { ++position; if(position >= (int)list->elemidx.size()) { elem = nullptr; // PAR@@@@ Remove just debugging } else if(0 <= position) { elem = &(list->elemstore[list->elemidx[position]]); } return *this; } // Needed for std:find_if of gcc used in entity.cpp GenerateEquations difference_type operator-(const iterator &rhs) const noexcept { return position - rhs.position; } iterator(IdList *l) : position(0), list(l) { if(list) { if(list->elemstore.size() && list->elemidx.size()) { elem = &(list->elemstore[list->elemidx[position]]); } } }; iterator(IdList *l, int pos) : position(pos), list(l) { if(position >= (int)list->elemidx.size()) { elem = nullptr; } else if(0 <= position) { elem = &((list->elemstore)[list->elemidx[position]]); } }; private: int position; T *elem; IdList *list; }; bool IsEmpty() const { return n == 0; } uint32_t MaximumId() { if(IsEmpty()) { return 0; } else { return elemstore[elemidx.back()].h.v; } } H AddAndAssignId(T *t) { t->h.v = (MaximumId() + 1); // Add at the end of the list. elemstore.push_back(*t); elemidx.push_back(elemstore.size()-1); ++n; return t->h; } void ReserveMore(int howMuch) { elemstore.reserve(elemstore.size() + howMuch); elemidx.reserve(elemidx.size() + howMuch); // freelist.reserve(freelist.size() + howMuch); // PAR@@@@ maybe we should - not much more RAM } void Add(T *t) { // Look to see if we already have something with the same handle value. ssassert(FindByIdNoOops(t->h) == nullptr, "Handle isn't unique"); // Find out where the added element should be. auto pos = std::lower_bound(elemidx.begin(), elemidx.end(), *t, Compare(this)); if(freelist.empty()) { // Add a new element to the store elemstore.push_back(*t); // Insert a pointer to the element at the correct position if(elemidx.empty()) { // The list is empty so pos, begin and end are all null. // insert does not work in this case. elemidx.push_back(elemstore.size()-1); } else { elemidx.insert(pos, elemstore.size() - 1); } } else { // Use the last element from the freelist // Insert an index to the element at the correct position elemidx.insert(pos, freelist.back()); // Remove the element from the freelist freelist.pop_back(); // Copy-construct to the element storage. elemstore[*pos] = T(*t); // *elemptr[pos] = *t; // PAR@@@@@@ maybe this? } ++n; } T *FindById(H h) { T *t = FindByIdNoOops(h); ssassert(t != nullptr, "Cannot find handle"); return t; } T *FindByIdNoOops(H h) { if(IsEmpty()) { return nullptr; } auto it = std::lower_bound(elemidx.begin(), elemidx.end(), h, Compare(this)); if(it == elemidx.end()) { return nullptr; } else { if(elemstore[*it].h.v != h.v) { return nullptr; } return &elemstore[*it]; } } T &Get(size_t i) { return elemstore[elemidx[i]]; } T &operator[](size_t i) { return Get(i); } iterator begin() { return IsEmpty() ? nullptr : iterator(this); } iterator end() { return IsEmpty() ? nullptr : iterator(this, elemidx.size()); } void ClearTags() { for(auto &elt : *this) { elt.tag = 0; } } void Tag(H h, int tag) { auto it = FindByIdNoOops(h); if (it != nullptr) { it->tag = tag; } } void RemoveTagged() { int src, dest; dest = 0; for(src = 0; src < n; src++) { if(elemstore[elemidx[src]].tag) { // this item should be deleted elemstore[elemidx[src]].Clear(); // elemstore[elemidx[src]].~T(); // Clear below calls the destructors freelist.push_back(elemidx[src]); elemidx[src] = 0xDEADBEEF; // PAR@@@@@ just for debugging, not needed, remove later } else { if(src != dest) { elemidx[dest] = elemidx[src]; } dest++; } } n = dest; elemidx.resize(n); // Clear left over elements at the end. } void RemoveById(H h) { // PAR@@@@@ this can be optimized ClearTags(); FindById(h)->tag = 1; RemoveTagged(); } void MoveSelfInto(IdList *l) { l->Clear(); std::swap(l->elemstore, elemstore); std::swap(l->elemidx, elemidx); std::swap(l->freelist, freelist); std::swap(l->n, n); } void DeepCopyInto(IdList *l) { l->Clear(); for(auto const &it : elemstore) { l->elemstore.push_back(it); } for(auto const &it : elemidx) { l->elemidx.push_back(it); } l->n = n; } void Clear() { for(auto &it : elemidx) { elemstore[it].Clear(); // elemstore[it].~T(); // clear below calls the destructors } freelist.clear(); elemidx.clear(); elemstore.clear(); n = 0; } }; class BandedMatrix { public: enum { MAX_UNKNOWNS = 16, RIGHT_OF_DIAG = 1, LEFT_OF_DIAG = 2 }; double A[MAX_UNKNOWNS][MAX_UNKNOWNS]; double B[MAX_UNKNOWNS]; double X[MAX_UNKNOWNS]; int n; void Solve(); }; #define RGBi(r, g, b) RgbaColor::From((r), (g), (b)) #define RGBf(r, g, b) RgbaColor::FromFloat((float)(r), (float)(g), (float)(b)) // Note: sizeof(class RgbaColor) should be exactly 4 // class RgbaColor { public: uint8_t red, green, blue, alpha; float redF() const { return (float)red / 255.0f; } float greenF() const { return (float)green / 255.0f; } float blueF() const { return (float)blue / 255.0f; } float alphaF() const { return (float)alpha / 255.0f; } bool IsEmpty() const { return alpha == 0; } bool Equals(RgbaColor c) const { return c.red == red && c.green == green && c.blue == blue && c.alpha == alpha; } RgbaColor WithAlpha(uint8_t newAlpha) const { RgbaColor color = *this; color.alpha = newAlpha; return color; } uint32_t ToPackedIntBGRA() const { return blue | (uint32_t)(green << 8) | (uint32_t)(red << 16) | (uint32_t)((255 - alpha) << 24); } uint32_t ToPackedInt() const { return red | (uint32_t)(green << 8) | (uint32_t)(blue << 16) | (uint32_t)((255 - alpha) << 24); } uint32_t ToARGB32() const { return blue | (uint32_t)(green << 8) | (uint32_t)(red << 16) | (uint32_t)(alpha << 24); } static RgbaColor From(int r, int g, int b, int a = 255) { RgbaColor c; c.red = (uint8_t)r; c.green = (uint8_t)g; c.blue = (uint8_t)b; c.alpha = (uint8_t)a; return c; } static RgbaColor FromFloat(float r, float g, float b, float a = 1.0) { return From( (int)(255.1f * r), (int)(255.1f * g), (int)(255.1f * b), (int)(255.1f * a)); } static RgbaColor FromPackedInt(uint32_t rgba) { return From( (int)((rgba) & 0xff), (int)((rgba >> 8) & 0xff), (int)((rgba >> 16) & 0xff), (int)(255 - ((rgba >> 24) & 0xff))); } static RgbaColor FromPackedIntBGRA(uint32_t bgra) { return From( (int)((bgra >> 16) & 0xff), (int)((bgra >> 8) & 0xff), (int)((bgra) & 0xff), (int)(255 - ((bgra >> 24) & 0xff))); } }; struct RgbaColorCompare { bool operator()(RgbaColor a, RgbaColor b) const { return a.ToARGB32() < b.ToARGB32(); } }; class BBox { public: Vector minp; Vector maxp; static BBox From(const Vector &p0, const Vector &p1); Vector GetOrigin() const; Vector GetExtents() const; void Include(const Vector &v, double r = 0.0); bool Overlaps(const BBox &b1) const; bool Contains(const Point2d &p, double r = 0.0) const; }; #endif