core/indigo-core/common/math/algebra.h (648 lines of code) (raw):

/**************************************************************************** * Copyright (C) from 2009 to Present EPAM Systems. * * This file is part of Indigo toolkit. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ #ifndef _ALGEBRA_H_ #define _ALGEBRA_H_ #include <algorithm> #include <cmath> #include <iostream> #include <limits> #include "base_c/defs.h" #include "base_cpp/exception.h" #define SQR(x) ((x) * (x)) #define DEG2RAD(x) ((x)*M_PI / 180) #define RAD2DEG(x) ((x)*180 / M_PI) #define HYPOT(a, b) (sqrt((a) * (a) + (b) * (b))) namespace indigo { const float EPSILON = 0.000001f; // frac of type 1/n for acos_stable const double frac1[25] = {0., 1., 1. / 2, 1. / 3, 1. / 4, 1. / 5, 1. / 6, 1. / 7, 1. / 8, 1. / 9, 1. / 10, 1. / 11, 1. / 12, 1. / 13, 1. / 14, 1. / 15, 1. / 16, 1. / 17, 1. / 18, 1. / 19, 1. / 20, 1. / 21, 1. / 22, 1. / 23, 1. / 24}; struct Transform3f; struct Vec3f; struct Vec2f { DECL_ERROR; Vec2f() : x(0), y(0) { } Vec2f(const Vec2f& a) : x(a.x), y(a.y) { } Vec2f(float xx, float yy) : x(xx), y(yy) { } float x, y; static constexpr auto min_coord() { return std::numeric_limits<decltype(x)>::lowest(); } static constexpr auto max_coord() { return std::numeric_limits<decltype(x)>::max(); } inline void set(float xx, float yy) { x = xx; y = yy; } inline void copy(const Vec2f& a) { x = a.x; y = a.y; } inline void zero() { x = 0; y = 0; } inline void clear() { zero(); } inline void negate() { x = -x; y = -y; } inline void negation(const Vec2f& v) { x = -v.x; y = -v.y; } inline void add(const Vec2f& v) { x += v.x; y += v.y; } inline void sum(const Vec2f& a, const Vec2f& b) { x = a.x + b.x; y = a.y + b.y; } inline void sub(const Vec2f& v) { x -= v.x; y -= v.y; } inline void diff(const Vec2f& a, const Vec2f& b) { x = a.x - b.x; y = a.y - b.y; } inline void min(const Vec2f& a) { x = std::min(x, a.x); y = std::min(y, a.y); } inline void max(const Vec2f& a) { x = std::max(x, a.x); y = std::max(y, a.y); } inline float lengthSqr() const { return x * x + y * y; } inline float length() const { return _2FLOAT(sqrt(lengthSqr())); } // OPERATORS: inline bool operator<(const Vec2f& a) const { return std::make_pair(x, y) < std::make_pair(a.x, a.y); } inline float operator*(const Vec2f& a) const { return x * a.x + y * a.y; } inline Vec2f operator+(const Vec2f& a) const { return Vec2f(x + a.x, y + a.y); } inline Vec2f operator-(const Vec2f& a) const { return Vec2f(x - a.x, y - a.y); } inline Vec2f operator*(float t) const { return Vec2f(x * t, y * t); } inline Vec2f operator/(float t) const { return Vec2f(x / t, y / t); } inline Vec2f operator+=(const Vec2f& a) { x += a.x; y += a.y; return *this; } inline Vec2f operator-=(const Vec2f& a) { x -= a.x; y -= a.y; return *this; } inline Vec2f operator*=(float t) { x *= t; y *= t; return *this; } inline Vec2f operator/=(float t) { x /= t; y /= t; return *this; } inline float vcos(const Vec2f& a) const { float scalar = *this * a; float ta = length() * a.length(); if (ta < EPSILON) ta = EPSILON; return scalar / ta; } inline float vsin(const Vec2f& a) const { float scalar = *this * a; float ta = lengthSqr() * a.lengthSqr(); if (ta < EPSILON) ta = EPSILON; return sqrt(1 - scalar * scalar / ta); } DLLEXPORT bool normalize(); DLLEXPORT bool normalization(const Vec2f& v); DLLEXPORT float tiltAngle(); DLLEXPORT float tiltAngle2(); DLLEXPORT float calc_angle(Vec2f a, Vec2f b); DLLEXPORT float calc_angle_pos(Vec2f a, Vec2f b); inline void scale(const float s) { x *= s; y *= s; } inline void scaled(const Vec2f& v, float s) { x = v.x * s; y = v.y * s; } inline void addScaled(const Vec2f& v, float s) { x += v.x * s; y += v.y * s; } inline void lineCombin(const Vec2f& a, const Vec2f& b, float t) { x = a.x + b.x * t; y = a.y + b.y * t; } inline void lineCombin2(const Vec2f& a, float ta, const Vec2f& b, float tb) { x = a.x * ta + b.x * tb; y = a.y * ta + b.y * tb; } DLLEXPORT void rotate(float angle); DLLEXPORT void rotate(float si, float co); DLLEXPORT void rotate(Vec2f vec); DLLEXPORT void rotateL(float angle); DLLEXPORT void rotateL(float si, float co); DLLEXPORT void rotateL(Vec2f vec); DLLEXPORT void rotateAroundSegmentEnd(const Vec2f& a, const Vec2f& b, float angle); DLLEXPORT static float distSqr(const Vec2f& a, const Vec2f& b); DLLEXPORT static float dist(const Vec2f& a, const Vec2f& b); DLLEXPORT static float dot(const Vec2f& a, const Vec2f& b); DLLEXPORT static float cross(const Vec2f& a, const Vec2f& b); DLLEXPORT static void projectZ(Vec2f& v2, const Vec3f& v3); DLLEXPORT static bool intersection(const Vec2f& v1_1, const Vec2f& v1_2, const Vec2f& v2_1, const Vec2f& v2_2, Vec2f& p); DLLEXPORT static float triangleArea(const Vec2f& a, const Vec2f& b, const Vec2f& c); DLLEXPORT static bool segmentsIntersect(const Vec2f& a0, const Vec2f& a1, const Vec2f& b0, const Vec2f& b1); DLLEXPORT static bool segmentsIntersectInternal(const Vec2f& a0, const Vec2f& a1, const Vec2f& b0, const Vec2f& b1); DLLEXPORT static float distPointSegment(Vec2f p, Vec2f q, Vec2f r); DLLEXPORT static float distSegmentSegment(Vec2f p, Vec2f q, Vec2f r, Vec2f s); DLLEXPORT static Vec2f get_circle_center(Vec2f p, Vec2f q, float angle); DLLEXPORT static Vec2f get_circle_center(Vec2f a, Vec2f b, Vec2f c); DLLEXPORT static float asin_stable(float x) { double x2 = _2DOUBLE(x) * _2DOUBLE(x); double res = 0.; double y = _2DOUBLE(x); for (int i = 0; i < 12; i++) { res += y * frac1[2 * i + 1]; y *= (1. - frac1[2 * i + 2]) * x2; } return _2FLOAT(res); } DLLEXPORT static float acos_stable(float x) { return _2FLOAT((M_PI / 2.) - asin_stable(x)); } }; inline bool rayIntersectsSegment(const Vec2f& beg, const Vec2f& end, const Vec2f& a, const Vec2f& b) { Vec2f ray = end - beg; // Vector of the segment Vec2f segment = a - b; // Calculate the cross products float cross1 = Vec2f::cross(ray, segment); if (fabs(cross1) < 1e-6) { // Parallel segments return false; } // Compute the cross product of vectors (beg - a) and (beg - b) Vec2f vecBA = beg - a; Vec2f vecBB = beg - b; float t = Vec2f::cross(vecBA, segment) / cross1; if (t < 0) { // Intersection is behind the starting point return false; } float u = Vec2f::cross(vecBA, ray) / cross1; return (u >= 0 && u <= 1); } struct Rect2f { explicit Rect2f() { } Rect2f(Vec2f a, Vec2f b) { _leftBottom = a; _leftBottom.min(b); _rightTop = a; _rightTop.max(b); } Rect2f(Rect2f a, Rect2f b) { _leftBottom = a._leftBottom; _leftBottom.min(b._leftBottom); _rightTop = a._rightTop; _rightTop.max(b._rightTop); } inline void copy(Rect2f& other) { _leftBottom = other._leftBottom; _rightTop = other._rightTop; } inline bool pointInRect(const Vec2f& pt) const { return _leftBottom.x < pt.x && _leftBottom.y < pt.y && _rightTop.x > pt.x && _rightTop.y > pt.y; } inline bool intersects(const Rect2f& other) const { return !(right() < other.left() || left() > other.right() || top() < other.bottom() || bottom() > other.top()); } inline bool rayIntersectsRect(const Vec2f& begin, const Vec2f& end) { auto lb = _leftBottom; auto lt = leftTop(); auto rt = _rightTop; auto rb = rightBottom(); return rayIntersectsSegment(begin, end, lb, lt) || rayIntersectsSegment(begin, end, lt, rt) || rayIntersectsSegment(begin, end, rt, rb) || rayIntersectsSegment(begin, end, rb, lb); } inline float pointDistance(const Vec2f& pt) { if (pt.x <= left()) { if (pt.y <= bottom()) return HYPOT(left() - pt.x, bottom() - pt.y); if (top() <= pt.y) return HYPOT(left() - pt.x, pt.y - top()); return left() - pt.x; } if (pt.x >= right()) { if (pt.y <= bottom()) return HYPOT(pt.x - right(), bottom() - pt.y); if (top() <= pt.y) return HYPOT(pt.x - right(), pt.y - top()); return pt.x - right(); } if (pt.y <= bottom()) return bottom() - pt.y; if (top() <= pt.y) return pt.y - top(); return 0.0; // if pt is inside the box } inline void extend(const Rect2f& second) { _leftBottom.min(second._leftBottom); _rightTop.max(second._rightTop); } inline void offset(const Vec2f& offset) { _leftBottom += offset; _rightTop += offset; } inline float left() const { return _leftBottom.x; } inline float right() const { return _rightTop.x; } inline float bottom() const { return _leftBottom.y; } inline float top() const { return _rightTop.y; } inline static float middle(const float& one, const float& second) { return (one + second) / 2; } inline float middleX() const { return middle(_leftBottom.x, _rightTop.x); } inline float middleY() const { return middle(_leftBottom.y, _rightTop.y); } inline float between_left_box(const Rect2f& second) const { return middle(second.right(), left()); } inline Vec2f leftBottom() const { return _leftBottom; } inline Vec2f rightTop() const { return _rightTop; } inline Vec2f leftTop() const { return Vec2f(left(), top()); } inline Vec2f rightBottom() const { return Vec2f(right(), bottom()); } inline Vec2f leftMiddle() const { return Vec2f(left(), middleY()); } inline Vec2f rightMiddle() const { return Vec2f(right(), middleY()); } inline Vec2f bottomMiddle() const { return Vec2f(middleX(), bottom()); } inline Vec2f topMiddle() const { return Vec2f(middleX(), top()); } inline Vec2f center() const { return Vec2f(middleX(), middleY()); } inline float width() const { return _rightTop.x - _leftBottom.x; } inline float height() const { return _rightTop.y - _leftBottom.y; } protected: Vec2f _leftBottom; Vec2f _rightTop; }; struct Vec3f { Vec3f() : x(0), y(0), z(0) { } Vec3f(float xx, float yy, float zz) : x(xx), y(yy), z(zz) { } Vec3f(Vec2f& v) : x(v.x), y(v.y), z(0) { } float x, y, z; inline void set(float xx, float yy, float zz) { x = xx; y = yy; z = zz; } inline void copy(const Vec3f& a) { x = a.x; y = a.y; z = a.z; } inline void zero() { x = 0; y = 0; z = 0; } inline void clear() { zero(); } inline void negate() { x = -x; y = -y; z = -z; } inline void negation(const Vec3f& v) { x = -v.x; y = -v.y; z = -v.z; } inline void add(const Vec3f& v) { x += v.x; y += v.y; z += v.z; } inline void sum(const Vec3f& a, const Vec3f& b) { x = a.x + b.x; y = a.y + b.y; z = a.z + b.z; } inline void sub(const Vec3f& v) { x -= v.x; y -= v.y; z -= v.z; } inline void diff(const Vec3f& a, const Vec3f& b) { x = a.x - b.x; y = a.y - b.y; z = a.z - b.z; } inline void min(const Vec3f& a) { x = std::min(x, a.x); y = std::min(y, a.y); z = std::min(z, a.z); } inline void max(const Vec3f& a) { x = std::max(x, a.x); y = std::max(y, a.y); z = std::max(z, a.z); } inline void cross(const Vec3f& a, const Vec3f& b) { x = a.y * b.z - a.z * b.y; y = a.z * b.x - a.x * b.z; z = a.x * b.y - a.y * b.x; } inline float lengthSqr() const { return x * x + y * y + z * z; } DLLEXPORT float length() const; DLLEXPORT bool normalize(); DLLEXPORT bool normalization(const Vec3f& v); inline void scale(float s) { x *= s; y *= s; z *= s; } inline void scaled(const Vec3f& v, float s) { x = v.x * s; y = v.y * s; z = v.z * s; } inline void addScaled(const Vec3f& v, float s) { x += v.x * s; y += v.y * s; z += v.z * s; } inline void lineCombin(const Vec3f& a, const Vec3f& b, float t) { x = a.x + b.x * t; y = a.y + b.y * t; z = a.z + b.z * t; } inline void lineCombin2(const Vec3f& a, float ta, const Vec3f& b, float tb) { x = a.x * ta + b.x * tb; y = a.y * ta + b.y * tb; z = a.z * ta + b.z * tb; } inline Vec2f projectZ() const { return Vec2f(x, y); } DLLEXPORT void rotateX(float angle); DLLEXPORT void rotateY(float angle); DLLEXPORT void rotateZ(float angle); DLLEXPORT void rotate(const Vec3f& around, float angle); DLLEXPORT void transformPoint(const Transform3f& matr); DLLEXPORT void transformVector(const Transform3f& matr); DLLEXPORT void invTransformVector(const Transform3f& matr); DLLEXPORT void pointTransformation(const Vec3f& v, const Transform3f& matr); DLLEXPORT void vectorTransformation(const Vec3f& v, const Transform3f& matr); DLLEXPORT void invVectorTransformation(const Vec3f& v, const Transform3f& matr); // returns value in range 0..pi DLLEXPORT static bool angle(const Vec3f& a, const Vec3f& b, float& res); DLLEXPORT static float dot(const Vec3f& a, const Vec3f& b); DLLEXPORT static float dist(const Vec3f& a, const Vec3f& b); DLLEXPORT static float distSqr(const Vec3f& a, const Vec3f& b); }; const Vec3f VZero3f(0.f, 0.f, 0.f); struct Transform3f { DECL_ERROR; float elements[16]; void rotation(float x, float y, float z, float angle); void rotationX(float angle); void rotationY(float angle); void rotationZ(float angle); bool rotationVecVec(const Vec3f& v1, const Vec3f& v2); bool rotationQuat(float quat[4]); bool inversion(const Transform3f& matr); void copy(const Transform3f& matr); void identity(void); void getOrigin(Vec3f& origin); void composition(const Transform3f& matr, const Transform3f& transform); void transform(const Transform3f& transform); void transformLocal(const Transform3f& transform); void setOrigin(float x, float y, float z); void setOrigin(const Vec3f& origin); void translate(const Vec3f& translation); void translateLocal(float x, float y, float z); void translateLocal(const Vec3f& translation); void translateLocalInv(const Vec3f& translation); void translateInv(const Vec3f& translation); void rotateX(float angle); void rotateY(float angle); void rotateZ(float angle); void rotateXLocal(float angle); void rotateYLocal(float angle); void rotateZLocal(float angle); bool bestFit(int npoints, const Vec3f points[], const Vec3f goals[], float* sqsum_out); }; struct Matr3x3d { double elements[9]; DECL_ERROR; Matr3x3d(); void copy(const Matr3x3d& matr); void transpose(); void getTransposed(Matr3x3d& matr_out) const; void identity(); void matrixMatrixMultiply(const Matr3x3d& m, Matr3x3d& matrix_out) const; void matrixVectorMultiply(const Vec3f& a, Vec3f& b) const; void eigenSystem(Matr3x3d& evec_out); protected: void _qrStep(int n, double gc[], double gs[]); void _givensRotation(double x0, double x1, double& c, double& s); }; struct LSeg3f { LSeg3f(const Vec3f& beg, const Vec3f& end); float distToPoint(const Vec3f& point, Vec3f* closest) const; protected: Vec3f _beg; Vec3f _end; Vec3f _diff; float _length_sqr; bool _is_degenerate; }; struct Line3f { Vec3f org; Vec3f dir; explicit Line3f(); void copy(Line3f& other); float distFromPoint(const Vec3f& point) const; bool bestFit(int npoints, const Vec3f points[], float* sqsum_out); }; struct Plane3f { explicit Plane3f(); void copy(const Plane3f& other); inline const Vec3f& getNorm() const { return _norm; } inline const float& getD() const { return _d; } void projection(const Vec3f& point, Vec3f& proj_out) const; bool byPointAndLine(const Vec3f& point, const Line3f& line); float distFromPoint(const Vec3f& point) const; bool bestFit(int npoints, const Vec3f points[], float* sqsum_out); protected: Vec3f _norm; float _d; }; } // namespace indigo #endif