/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
* Copyright 2008-2012 Pelican Mapping
* http://osgearth.org
*
* osgEarth is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*/
#ifndef OSGEARTH_MATH_H
#define OSGEARTH_MATH_H 1
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
namespace osgEarth
{
struct Line2d;
struct Segment2d;
struct Ray2d;
struct Segment3d;
/** Infinite 2D line. */
struct OSGEARTH_EXPORT Line2d
{
osg::Vec3d _a, _b; // two points on the line (Z ignored)
Line2d() { }
Line2d(const osg::Vec3d& a, const osg::Vec3d& b) : _a(a), _b(b) { }
Line2d(const osg::Vec2d& a, const osg::Vec2d& b) : _a(a.x(), a.y(), 0), _b(b.x(), b.y(), 0) { }
Line2d(const osg::Vec4d& a, const osg::Vec4d& b) : _a(a.x()/a.w(), a.y()/a.w(), a.z()/a.w()), _b(b.x()/b.w(), b.y()/b.w(), b.z()/b.w()) { }
Line2d(const Line2d& rhs) : _a(rhs._a), _b(rhs._b) { }
bool intersect( const Line2d& rhs, osg::Vec2d& out ) const;
bool intersect( const Ray2d& rhs, osg::Vec2d& out ) const;
bool intersect( const Segment2d& rhs, osg::Vec2d& out ) const;
bool intersect( const Line2d& rhs, osg::Vec3d& out ) const;
bool intersect( const Ray2d& rhs, osg::Vec3d& out ) const;
bool intersect( const Segment2d& rhs, osg::Vec3d& out ) const;
bool intersect( const Line2d& rhs, osg::Vec4d& out ) const;
bool isPointOnLeft( const osg::Vec2d& p ) const;
bool isPointOnLeft( const osg::Vec3d& p ) const;
};
//// rotate a Line
//inline Line2d operator* (const osg::Quat& q, const Line2d& rhs) {
// return Line( q * rhs._a, q * rhs._b );
//}
/** Endpoint and a direction vector */
struct OSGEARTH_EXPORT Ray2d
{
osg::Vec3d _a; // endpoint
osg::Vec3d _dv; // directional vector
Ray2d() { }
Ray2d(const osg::Vec3d& a, const osg::Vec3d& dv) : _a(a), _dv(dv) { }
Ray2d(const osg::Vec2d& a, const osg::Vec2d& dv) : _a(a.x(), a.y(), 0), _dv(dv.x(), dv.y(), 0) { }
Ray2d(const Ray2d& rhs) : _a(rhs._a), _dv(rhs._dv) { }
bool intersect( const Line2d& rhs, osg::Vec2d& out ) const;
bool intersect( const Ray2d& rhs, osg::Vec2d& out ) const;
bool intersect( const Segment2d& rhs, osg::Vec2d& out ) const;
bool intersect( const Line2d& rhs, osg::Vec3d& out ) const;
bool intersect( const Ray2d& rhs, osg::Vec3d& out ) const;
bool intersect( const Segment2d& rhs, osg::Vec3d& out ) const;
bool isPointOnLeft( const osg::Vec2d& p ) const;
bool isPointOnLeft( const osg::Vec3d& p ) const;
double angle(const Segment2d& rhs) const;
};
//// rotate a Ray
//inline Ray operator* (const osg::Quat& q, const Ray& rhs) {
// return Ray( q * rhs._a, q * rhs._dv );
//}
/** Finite line between two endpoints */
struct OSGEARTH_EXPORT Segment2d
{
osg::Vec3d _a, _b; // endpoints
Segment2d() { }
Segment2d(const osg::Vec3d& a, const osg::Vec3d& b) : _a(a), _b(b) { }
Segment2d(const Segment2d& rhs) : _a(rhs._a), _b(rhs._b) { }
bool intersect( const Line2d& rhs, osg::Vec2d& out ) const;
bool intersect( const Ray2d& rhs, osg::Vec2d& out ) const;
bool intersect( const Segment2d& rhs, osg::Vec2d& out ) const;
bool intersect( const Line2d& rhs, osg::Vec3d& out ) const;
bool intersect( const Ray2d& rhs, osg::Vec3d& out ) const;
bool intersect( const Segment2d& rhs, osg::Vec3d& out ) const;
bool isPointOnLeft( const osg::Vec2d& p ) const;
bool isPointOnLeft( const osg::Vec3d& p ) const;
Segment3d unrotateTo3D(const osg::Quat& q) const;
double angle(const Segment2d& rhs) const;
// Distance of point to segment; left side is positive
double leftDistanceXY( const osg::Vec3d&p ) const
{
osg::Vec2d base(_b.x() - _a.x(), _b.y() - _a.y());
// 2D cross product, 2 * area of triangle
double cross = base.x() * (p.y() - _a.y()) - base.y() * (p.x() - _a.x());
return cross / base.length();
}
double squaredDistanceTo(const osg::Vec3d& point) const;
osg::Vec3d closestPointTo(const osg::Vec3d& point) const;
};
struct OSGEARTH_EXPORT Segment3d
{
osg::Vec3d _a, _b; // endpoints
Segment3d() { }
Segment3d(const osg::Vec3d& a, const osg::Vec3d& b) : _a(a), _b(b) { }
Segment3d(const Segment3d& rhs) : _a(rhs._a), _b(rhs._b) { }
Segment3d(const Segment2d& seg2d, const osg::Vec3& planeNormal);
Segment2d rotateTo2D(const osg::Quat& q) { return Segment2d(q*_a, q*_b); }
};
//// rotate a Segment
//inline Segment operator* (const osg::Quat& q, const Segment& rhs) {
// return Segment( q * rhs._a, q * rhs._b );
//}
/** 2D traingle with CCW winding. */
struct OSGEARTH_EXPORT Triangle2d
{
osg::Vec3d _a, _b, _c;
Triangle2d(const osg::Vec3d& a, const osg::Vec3d& b, const osg::Vec3d& c) : _a(a), _b(b), _c(c) { }
bool contains(const osg::Vec3d& p) const;
};
// Work in progress. We want to use Ray without doing the work to
// fully support the other types.
struct Line;
struct Ray;
#if 0
/** Infinite 3D line. */
struct Line
{
osg::Vec3d _a, _b; // two points on the line
Line(const osg::Vec3d& a, const osg::Vec3d& b) : _a(a), _b(b) { }
Line(const Line& rhs) : _a(rhs._a), _b(rhs._b) { }
bool intersectXY( const Line& rhs, osg::Vec3d& out ) const;
bool intersectXY( const Ray& rhs, osg::Vec3d& out ) const;
bool intersectXY( const Segment& rhs, osg::Vec3d& out ) const;
};
// rotate a Line
inline Line operator* (const osg::Quat& q, const Line& rhs) {
return Line( q * rhs._a, q * rhs._b );
}
#endif
/** Endpoint and a direction vector */
struct Ray
{
osg::Vec3d _a; // endpoint
osg::Vec3d _dv; // directional vector
Ray(const osg::Vec3d& a, const osg::Vec3d& dv) : _a(a), _dv(dv) { }
Ray(const Ray& rhs) : _a(rhs._a), _dv(rhs._dv) { }
#if 0
bool intersectXY( const Line& rhs, osg::Vec3d& out ) const;
bool intersectXY( const Ray& rhs, osg::Vec3d& out ) const;
bool intersectXY( const Segment& rhs, osg::Vec3d& out ) const;
bool isPointOnLeftXY( const osg::Vec3d& p ) const;
#endif
};
// rotate a Ray
inline Ray operator* (const osg::Quat& q, const Ray& rhs) {
return Ray( q * rhs._a, q * rhs._dv );
}
#if 0
/** Finite line between two endpoints */
// This will need a new name before it's enabled; conflicts with a
// type in osgEarth/Geometry.
struct Segment
{
osg::Vec3d _a, _b; // endpoints
Segment(const osg::Vec3d& a, const osg::Vec3d& b) : _a(a), _b(b) { }
Segment(const Segment& rhs) : _a(rhs._a), _b(rhs._b) { }
bool intersectXY( const Line& rhs, osg::Vec3d& out ) const;
bool intersectXY( const Ray& rhs, osg::Vec3d& out ) const;
bool intersectXY( const Segment& rhs, osg::Vec3d& out ) const;
bool isPointOnLeftXY( const osg::Vec3d& p ) const;
};
// rotate a Segment
inline Segment operator* (const osg::Quat& q, const Segment& rhs) {
return Segment( q * rhs._a, q * rhs._b );
}
#endif
// Utility functions for arrays of points treated as a 2d polygon
OSGEARTH_EXPORT osg::BoundingBoxd polygonBBox2d(const osg::Vec3dArray& points);
template
osg::BoundingBoxd polygonBBox2d(Iterator begin, Iterator end)
{
osg::BoundingBoxd result(DBL_MAX, DBL_MAX, DBL_MAX, -DBL_MAX, -DBL_MAX, -DBL_MAX);
for (Iterator itr = begin; itr != end; ++itr)
{
result.xMin() = std::min(result.xMin(), (*itr).x());
result.xMax() = std::max(result.xMax(), (*itr).x());
result.yMin() = std::min(result.yMin(), (*itr).y());
result.yMax() = std::max(result.yMax(), (*itr).y());
}
return result;
}
OSGEARTH_EXPORT bool pointInPoly2d(const osg::Vec3d& pt, const Polygon& polyPoints,
double tolerance = 0.0);
OSGEARTH_EXPORT bool pointInPoly2d(const osg::Vec3d& pt, const osg::Geometry* polyPoints,
float tolerance = 0.0f);
// Winding number test; see http://geomalgorithms.com/a03-_inclusion.html
template
bool
pointInPoly2d(const Pt& pt, const PtItr& begin, const PtItr& end, double tolerance = 0.0)
{
int windingNum = 0;
for (PtItr itr = begin; itr != end; ++itr)
{
Segment2d seg = (std::next(itr) == end
? Segment2d(*itr, *begin)
: Segment2d(*itr, *std::next(itr)));
// if the segment is horizontal, then the "is left" test
// isn't meaningful. We count the point as in if it's on or
// to the left of the segment.
if (seg._a.y() == seg._b.y() && fabs(pt.y() - seg._a.y()) <= tolerance)
{
if (pt.x() < seg._a.x() || pt.x() < seg._b.x())
{
windingNum++;
}
}
else if (seg._a.y() <= pt.y())
{
if (seg._b.y() > pt.y())
{
double dist = seg.leftDistanceXY(pt);
if (dist > -tolerance)
{
windingNum++;
}
}
}
else if (seg._b.y() <= pt.y())
{
double dist = seg.leftDistanceXY(pt);
if (dist < tolerance)
{
windingNum--;
}
}
}
return windingNum != 0;
}
template
inline T deg2rad(T v) {
return v * static_cast(M_PI)/static_cast(180.0);
}
template
inline T rad2deg(T v) {
return v * static_cast(180.0) / static_cast(M_PI);
}
template
inline T step(const T& edge, const T& x)
{
return x < edge ? static_cast(0.0) : static_cast(1.0);
}
template
inline T clamp(const T& x, const T& lo, const T& hi)
{
return xhi ? hi : x;
}
template
inline T lerpstep(T lo, T hi, T x)
{
if (x <= lo) return static_cast(0.0);
else if (x >= hi) return static_cast(1.0);
else return (x - lo) / (hi - lo);
}
template
inline T smoothstep(T lo, T hi, T x)
{
T t = clamp((x - lo) / (hi - lo), static_cast(0.0), static_cast(1.0));
return t * t*(static_cast(3.0) - static_cast(2.0)*t);
}
// move closer to one
template
inline T harden(T x)
{
return static_cast(1.0) - (static_cast(1.0) - x)*(static_cast(1.0) - x);
}
template
inline T decel(T x) { return harden(x); }
// move closer to zero
template
inline T soften(T x)
{
return x * x;
}
template
inline T accel(T x) { return soften(x); }
template
inline T threshold(T x, T thresh, T buf)
{
if (x < thresh - buf) return static_cast(0.0);
else if (x > thresh + buf) return static_cast(1.0);
else return clamp((x - (thresh - buf)) / (buf*static_cast(2.0)), static_cast(0.0), static_cast(1.0));
}
template
inline T fract(T x)
{
return x - floor(x);
}
template
inline double unitremap(T a, T lo, T hi)
{
return clamp((a - lo) / (hi - lo), static_cast(0.0), static_cast(1.0));
}
template
inline T mix(const T& a, const T& b, float c)
{
return a * (1.0 - c) + b * c;
}
template
inline double dot2D(const T& a, const T& b)
{
return a.x()*b.x() + a.y()*b.y();
}
template
inline double dot3D(const T& a, const T& b)
{
return a.x()*b.x() + a.y()*b.y() + a.z()*b.z();
}
template
inline double distanceSquared2D(const T& a, const T& b)
{
return
(b.x() - a.x())*(b.x() - a.x()) +
(b.y() - a.y())*(b.y() - a.y());
}
template
inline double distanceSquared3D(const T& a, const T& b)
{
return
(b.x() - a.x())*(b.x() - a.x()) +
(b.y() - a.y())*(b.y() - a.y()) +
(b.z() - a.z())*(b.z() - a.z());
}
template
inline double distance2D(const T& a, const T& b)
{
return sqrt(distanceSquared2D(a, b));
}
template
inline double distance3D(const T& a, const T& b)
{
return sqrt(distanceSquared3D(a, b));
}
template
inline T normalize(const T& a)
{
T temp = a;
temp.normalize();
return temp;
}
// Newton-Raphson solver
template
double solve(Func func, FuncDeriv deriv, double guess, double tolerance, bool& valid, int maxIterations = 16)
{
double xn = guess;
for (int i = 0; i <= maxIterations; ++i)
{
double f = func(xn);
if (fabs(f) <= tolerance)
{
valid = true;
return xn;
}
xn = xn - f / deriv(xn);
}
valid = false;
return xn;
}
// Courtesy of stackoverflow, return -1, 0, +1 based on the sign
// of a number
template
int sgn(T val)
{
return (T(0) < val) - (val < T(0));
}
// Bisection solver, useful when the derivative of a function is
// unknown or expensive to calculate.
// x0 and x1 are initial guesses surrounding the root. The signs
// of func(x0) and func(x1) should be different; otherwise we bail
// immediately.
template
double solveBisect(const Func& func, double x0, double x1, double tolerance, int maxIterations = 8)
{
double f0 = func(x0);
double f1 =func(x1);
if (sgn(f0) == sgn(f1))
{
return x0;
}
double midPoint = 0.0;
for (int i = 0; i < maxIterations; ++i)
{
midPoint = (x0 + x1) / 2.0;
double fMidpoint = func(midPoint);
if (fabs(fMidpoint) <= tolerance)
{
return midPoint;
}
else if (sgn(f0) == sgn(fMidpoint))
{
x0 = midPoint;
f0 = fMidpoint;
}
else
{
x1 = midPoint;
f1 = fMidpoint;
}
}
return midPoint;
}
// Project osg::Vec3 a onto b.
template
VecType vecProjection(const VecType& a, const VecType& b)
{
return b * ((a * b) / (b * b));
}
// Project osg::Vec3 a onto the plane perpendicular to b.
template
VecType vecRejection(const VecType& a, const VecType& b)
{
return a - vecProjection(a, b);
}
// Round integral x to the nearest multiple of "multiple" greater than or equal to x
template
T align(T x, T multiple) {
T isPositive = (T)(x >= 0);
return ((x + isPositive * (multiple - 1)) / multiple) * multiple;
}
// equal within a default threshold
template
bool equivalent(T x, T y) {
return osg::equivalent(x, y);
}
// equal within a threshold
template
bool equivalent(T x, T y, T epsilon) {
return osg::equivalent(x, y, epsilon);
}
inline int nextPowerOf2(int x)
{
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x + 1;
}
// Adapted from Boost - see boost license
// https://www.boost.org/users/license.html
template inline std::size_t hash_value_unsigned(T val) {
const int size_t_bits = std::numeric_limits::digits;
const int length = (std::numeric_limits::digits - 1) / size_t_bits;
std::size_t seed = 0;
for(unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits)
seed ^= (std::size_t) (val >> i) + (seed<<6) + (seed>>2);
seed ^= (std::size_t) val + (seed<<6) + (seed>>2);
return seed;
}
inline std::size_t hash_value_unsigned(bool val) {
return hash_value_unsigned((unsigned)val ? 0x1111111 : 0x2222222);
}
template
inline std::size_t hash_value_unsigned(const optional& val) {
if (val.isSet())
return hash_value_unsigned(0x3333333u, val.get());
else
return (std::size_t)0;
}
template inline std::size_t hash_value_unsigned(A a, B b) {
std::size_t seed = hash_value_unsigned(a);
seed ^= hash_value_unsigned(b) + 0x9e3779b9 + (seed<<6) + (seed>>2);
return seed;
}
template inline std::size_t hash_value_unsigned(A a, B b, C c) {
std::size_t seed = hash_value_unsigned(a);
seed ^= hash_value_unsigned(b) + 0x9e3779b9 + (seed<<6) + (seed>>2);
seed ^= hash_value_unsigned(c) + 0x9e3779b9 + (seed<<6) + (seed>>2);
return seed;
}
template inline std::size_t hash_value_unsigned(A a, B b, C c, D d) {
std::size_t seed = hash_value_unsigned(a);
seed ^= hash_value_unsigned(b) + 0x9e3779b9 + (seed<<6) + (seed>>2);
seed ^= hash_value_unsigned(c) + 0x9e3779b9 + (seed<<6) + (seed>>2);
seed ^= hash_value_unsigned(d) + 0x9e3779b9 + (seed<<6) + (seed>>2);
return seed;
}
template inline std::size_t hash_value_unsigned(A a, B b, C c, D d, E e) {
std::size_t seed = hash_value_unsigned(a);
seed ^= hash_value_unsigned(b) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= hash_value_unsigned(c) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= hash_value_unsigned(d) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
seed ^= hash_value_unsigned(e) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
// https://github.com/matteo65/mzHash64 (Public Domain)
inline std::size_t hash_string(const std::string& input, std::size_t seed = 0) {
auto hash = seed ^ 0xD45E69F901E72147L;
auto len = input.length();
for(std::size_t i=0; i> 2);
return hash;
};
/**
* Utilities to manipulate a projection matrix.
* These functions will automatically handle standard OGL versus Reverse-Z
* projection matrices.
*/
struct OSGEARTH_EXPORT ProjectionMatrix
{
//! Projection matrix type
using Type = enum {
STANDARD,
REVERSE_Z,
UNKNOWN
};
//! True for an orthographic projection matrix
static inline bool isOrtho(const osg::Matrix& m) {
return !m.isIdentity() && m(3, 3) > 0.0;
}
//! True for a perspective projection matrix
static inline bool isPerspective(const osg::Matrix& m) {
return m(3, 3) == 0.0;
}
//! Detected type of the projection matrix
static Type getType(const osg::Matrix& m);
//! Construct a perspective matrix, deriving the type from the
//! existing values in the matrix if possible
static void setPerspective(
osg::Matrix& m,
double vfov, double ar, double N, double F,
Type = UNKNOWN);
//! Extract values from a perspective matrix
static bool getPerspective(
const osg::Matrix& m,
double& vfov, double& ar, double& N, double& F);
//! Extract raw frustum values from a perspective matrix
static bool getPerspective(
const osg::Matrix& m,
double& L, double& R, double& B, double& T, double& N, double& F);
//! Construct an orthographc matrix, deriving the type from the
//! existing values in the matrix if possible
static void setOrtho(
osg::Matrix& m,
double L, double R, double B, double T, double N, double F,
Type = UNKNOWN);
//! Extract frustum values from an orthographic matrix
static bool getOrtho(
const osg::Matrix& m,
double& L, double& R, double& B, double& T, double& N, double& F);
};
}
#endif