Added new intersection methods, improved comparison to be reusable (without hardcoding rounding constants), added unit tests for some functionality
git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@5390 dfc29bdd-3216-0410-991c-e03cc46cb475master
parent
f28e0ba53c
commit
56f0372862
|
@ -43,6 +43,7 @@ namespace core
|
|||
//! Rounding error constant often used when comparing f32 values.
|
||||
|
||||
const s32 ROUNDING_ERROR_S32 = 0;
|
||||
|
||||
#ifdef __IRR_HAS_S64
|
||||
const s64 ROUNDING_ERROR_S64 = 0;
|
||||
#endif
|
||||
|
@ -181,16 +182,83 @@ namespace core
|
|||
b = c;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T roundingError();
|
||||
|
||||
template <>
|
||||
inline f32 roundingError()
|
||||
{
|
||||
return ROUNDING_ERROR_f32;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline f64 roundingError()
|
||||
{
|
||||
return ROUNDING_ERROR_f64;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline s32 roundingError()
|
||||
{
|
||||
return ROUNDING_ERROR_S32;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline u32 roundingError()
|
||||
{
|
||||
return ROUNDING_ERROR_S32;
|
||||
}
|
||||
|
||||
#ifdef __IRR_HAS_S64
|
||||
template <>
|
||||
inline s64 roundingError()
|
||||
{
|
||||
return ROUNDING_ERROR_S64;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline u64 roundingError()
|
||||
{
|
||||
return ROUNDING_ERROR_S64;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
inline T relativeErrorFactor()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline f32 relativeErrorFactor()
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline f64 relativeErrorFactor()
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
//! returns if a equals b, taking possible rounding errors into account
|
||||
inline bool equals(const f64 a, const f64 b, const f64 tolerance = ROUNDING_ERROR_f64)
|
||||
template <class T>
|
||||
inline T equals(const T a, const T b, const T tolerance = roundingError<T>())
|
||||
{
|
||||
return (a + tolerance >= b) && (a - tolerance <= b);
|
||||
}
|
||||
|
||||
//! returns if a equals b, taking possible rounding errors into account
|
||||
inline bool equals(const f32 a, const f32 b, const f32 tolerance = ROUNDING_ERROR_f32)
|
||||
|
||||
//! returns if a equals b, taking relative error in form of factor
|
||||
//! this particular function does not involve any division.
|
||||
template <class T>
|
||||
inline bool equalsRelative( const T a, const T b, const T factor = relativeErrorFactor<T>())
|
||||
{
|
||||
return (a + tolerance >= b) && (a - tolerance <= b);
|
||||
const T maxi = max_( a, b);
|
||||
const T mini = min_( a, b);
|
||||
const T maxMagnitude = max_( maxi, -mini);
|
||||
|
||||
return (maxMagnitude*factor + maxi) == (maxMagnitude*factor + mini); // MAD Wise
|
||||
}
|
||||
|
||||
union FloatIntUnion32
|
||||
|
@ -233,39 +301,6 @@ namespace core
|
|||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//! returns if a equals b, not using any rounding tolerance
|
||||
inline bool equals(const s32 a, const s32 b)
|
||||
{
|
||||
return (a == b);
|
||||
}
|
||||
|
||||
//! returns if a equals b, not using any rounding tolerance
|
||||
inline bool equals(const u32 a, const u32 b)
|
||||
{
|
||||
return (a == b);
|
||||
}
|
||||
#endif
|
||||
//! returns if a equals b, taking an explicit rounding tolerance into account
|
||||
inline bool equals(const s32 a, const s32 b, const s32 tolerance = ROUNDING_ERROR_S32)
|
||||
{
|
||||
return (a + tolerance >= b) && (a - tolerance <= b);
|
||||
}
|
||||
|
||||
//! returns if a equals b, taking an explicit rounding tolerance into account
|
||||
inline bool equals(const u32 a, const u32 b, const s32 tolerance = ROUNDING_ERROR_S32)
|
||||
{
|
||||
return (a + tolerance >= b) && (a - tolerance <= b);
|
||||
}
|
||||
|
||||
#ifdef __IRR_HAS_S64
|
||||
//! returns if a equals b, taking an explicit rounding tolerance into account
|
||||
inline bool equals(const s64 a, const s64 b, const s64 tolerance = ROUNDING_ERROR_S64)
|
||||
{
|
||||
return (a + tolerance >= b) && (a - tolerance <= b);
|
||||
}
|
||||
#endif
|
||||
|
||||
//! returns if a equals zero, taking rounding errors into account
|
||||
inline bool iszero(const f64 a, const f64 tolerance = ROUNDING_ERROR_f64)
|
||||
{
|
||||
|
|
|
@ -103,12 +103,46 @@ class line2d
|
|||
}
|
||||
|
||||
/*! Check if 2 lines/segments are parallel or nearly parallel.*/
|
||||
bool nearlyParallel( const line2d<T>& line) const
|
||||
bool nearlyParallel( const line2d<T>& line, const T factor = relativeErrorFactor<T>()) const
|
||||
{
|
||||
vector2d<T> a = this->getVector();
|
||||
vector2d<T> b = line.getVector();
|
||||
const vector2d<T> a = getVector();
|
||||
const vector2d<T> b = line.getVector();
|
||||
|
||||
return (f32)a.dotProduct(b) < irr::core::ROUNDING_ERROR_f32;
|
||||
return a.nearlyParallel( b, factor);
|
||||
}
|
||||
|
||||
/*! returns a intersection point of 2 lines (if lines are not parallel). Behaviour
|
||||
undefined if lines are parallel or coincident.*/
|
||||
vector2d<T> fastLinesIntersection( const line2d<T>& l)
|
||||
{
|
||||
const f32 commonDenominator = (f32)((l.end.Y - l.start.Y)*(end.X - start.X) -
|
||||
(l.end.X - l.start.X)*(end.Y - start.Y));
|
||||
|
||||
const f32 numeratorA = (f32)((l.end.X - l.start.X)*(start.Y - l.start.Y) -
|
||||
(l.end.Y - l.start.Y)*(start.X - l.start.X));
|
||||
|
||||
const f32 numeratorB = (f32)((end.X - start.X)*(start.Y - l.start.Y) -
|
||||
(end.Y - start.Y)*(start.X - l.start.X));
|
||||
|
||||
const f32 uA = numeratorA / commonDenominator;
|
||||
const f32 uB = numeratorB / commonDenominator;
|
||||
|
||||
// Calculate the intersection point.
|
||||
return vector2d<T> (
|
||||
(T)(start.X + uA * (end.X - start.X)),
|
||||
(T)(start.Y + uA * (end.Y - start.Y))
|
||||
);
|
||||
}
|
||||
|
||||
/*! Check if this line intersect a segment. The eventual intersection point is returned in "out".*/
|
||||
bool lineIntersectSegment( const line2d<T>& segment, vector2d<T> & out) const
|
||||
{
|
||||
if (nearlyParallel( segment))
|
||||
return false;
|
||||
|
||||
out = fastLinesIntersection( segment);
|
||||
|
||||
return segment.start.onSegment( intersection, segment.end);
|
||||
}
|
||||
|
||||
//! Tests if this line intersects with another line.
|
||||
|
|
|
@ -127,6 +127,19 @@ public:
|
|||
return X*other.X + Y*other.Y;
|
||||
}
|
||||
|
||||
//! check if this vector is parallel to another vector
|
||||
bool nearlyParallel( const vector2d<T> & other, const T factor = relativeErrorFactor<T>()) const
|
||||
{
|
||||
// if a || b then a.x/a.y = b.x/b.y (similiar triangles)
|
||||
// if a || b then either both x are 0 or both y are 0.
|
||||
|
||||
return equalsRelative( X*other.Y, other.X* Y, factor)
|
||||
&& // a bit counterintuitive, but makes sure that
|
||||
// only y or only x are 0, and at same time deals
|
||||
// with the case where one vector is zero vector.
|
||||
(X*other.X + Y*other.Y) != 0;
|
||||
}
|
||||
|
||||
//! Gets distance from another point.
|
||||
/** Here, the vector is interpreted as a point in 2-dimensional space.
|
||||
\param other Other vector to measure from.
|
||||
|
@ -259,16 +272,7 @@ public:
|
|||
\return True if this vector is between begin and end, false if not. */
|
||||
bool isBetweenPoints(const vector2d<T>& begin, const vector2d<T>& end) const
|
||||
{
|
||||
if (begin.X != end.X)
|
||||
{
|
||||
return ((begin.X <= X && X <= end.X) ||
|
||||
(begin.X >= X && X >= end.X));
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((begin.Y <= Y && Y <= end.Y) ||
|
||||
(begin.Y >= Y && Y >= end.Y));
|
||||
}
|
||||
return begin.onSegment( *this, end);
|
||||
}
|
||||
|
||||
//! Creates an interpolated vector between this vector and another vector.
|
||||
|
@ -300,6 +304,68 @@ public:
|
|||
(T)(Y * mul0 + v2.Y * mul1 + v3.Y * mul2));
|
||||
}
|
||||
|
||||
/*! Test if this point and another 2 poitns taken as triplet
|
||||
are colinear, clockwise, anticlockwise. This can be used also
|
||||
to check winding order in triangles for 2D meshes.
|
||||
\return 0 if points are colinear, 1 if clockwise, 2 if anticlockwise
|
||||
*/
|
||||
s32 checkOrientation( const vector2d<T> & b, const vector2d<T> & c) const
|
||||
{
|
||||
// Example of clockwise points
|
||||
//
|
||||
// ^ Y
|
||||
// | A
|
||||
// | / \
|
||||
// | / \
|
||||
// | C-----B
|
||||
// +---------------> X
|
||||
|
||||
T val = (b.Y - Y) * (c.X - b.X) -
|
||||
(b.X - X) * (c.Y - b.Y);
|
||||
|
||||
if (val == 0) return 0; // colinear
|
||||
|
||||
return (val > 0) ? 1 : 2; // clock or counterclock wise
|
||||
}
|
||||
|
||||
/*! Returns true if points (a,b,c) are clockwise on the X,Y plane*/
|
||||
inline bool areClockwise( const vector2d<T> & b, const vector2d<T> & c) const
|
||||
{
|
||||
T val = (b.Y - Y) * (c.X - b.X) -
|
||||
(b.X - X) * (c.Y - b.Y);
|
||||
|
||||
return val > 0;
|
||||
}
|
||||
|
||||
/*! Returns true if points (a,b,c) are counterclockwise on the X,Y plane*/
|
||||
inline bool areCounterClockwise( const vector2d<T> & b, const vector2d<T> & c) const
|
||||
{
|
||||
T val = (b.Y - Y) * (c.X - b.X) -
|
||||
(b.X - X) * (c.Y - b.Y);
|
||||
|
||||
return val < 0;
|
||||
}
|
||||
|
||||
// Given three COLINEAR POINTS p, q, r, the function checks if
|
||||
// point q lies on segment 'pr'. The point "p" is this one.
|
||||
bool onSegment(const vector2d<T> & q, const vector2d<T> & r) const
|
||||
{
|
||||
// (this)p .
|
||||
// \
|
||||
// \
|
||||
// \
|
||||
// . r
|
||||
// -
|
||||
// -
|
||||
// . q (hei there! Am I on the segment or outside?)
|
||||
//
|
||||
if (q.X <= max_( X, r.X) && q.X >= min_( X, r.X) &&
|
||||
q.Y <= max_( Y, r.Y) && q.X >= min_( Y, r.Y))
|
||||
return true; // inside
|
||||
|
||||
return false; // outside
|
||||
}
|
||||
|
||||
//! Sets this vector to the linearly interpolated vector between a and b.
|
||||
/** \param a first vector to interpolate with, maximum at 1.0f
|
||||
\param b second vector to interpolate with, maximum at 0.0f
|
||||
|
|
|
@ -129,6 +129,7 @@ int main(int argumentCount, char * arguments[])
|
|||
TEST(terrainSceneNode);
|
||||
TEST(lightMaps);
|
||||
TEST(triangleSelector);
|
||||
TEST(line2DTest);
|
||||
|
||||
unsigned int numberOfTests = tests.size();
|
||||
unsigned int testToRun = 0;
|
||||
|
|
|
@ -107,6 +107,7 @@
|
|||
<Unit filename="irrString.cpp" />
|
||||
<Unit filename="lightMaps.cpp" />
|
||||
<Unit filename="lights.cpp" />
|
||||
<Unit filename="line2d.cpp" />
|
||||
<Unit filename="loadTextures.cpp" />
|
||||
<Unit filename="main.cpp" />
|
||||
<Unit filename="makeColorKeyTexture.cpp" />
|
||||
|
|
|
@ -179,6 +179,7 @@
|
|||
<ClCompile Include="irrString.cpp" />
|
||||
<ClCompile Include="lightMaps.cpp" />
|
||||
<ClCompile Include="lights.cpp" />
|
||||
<ClCompile Include="line2d.cpp" />
|
||||
<ClCompile Include="loadTextures.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="makeColorKeyTexture.cpp" />
|
||||
|
|
|
@ -179,6 +179,7 @@
|
|||
<ClCompile Include="irrString.cpp" />
|
||||
<ClCompile Include="lightMaps.cpp" />
|
||||
<ClCompile Include="lights.cpp" />
|
||||
<ClCompile Include="line2d.cpp" />
|
||||
<ClCompile Include="loadTextures.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="makeColorKeyTexture.cpp" />
|
||||
|
|
|
@ -179,6 +179,7 @@
|
|||
<ClCompile Include="irrString.cpp" />
|
||||
<ClCompile Include="lightMaps.cpp" />
|
||||
<ClCompile Include="lights.cpp" />
|
||||
<ClCompile Include="line2d.cpp" />
|
||||
<ClCompile Include="loadTextures.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="makeColorKeyTexture.cpp" />
|
||||
|
|
|
@ -179,6 +179,7 @@
|
|||
<ClCompile Include="irrString.cpp" />
|
||||
<ClCompile Include="lightMaps.cpp" />
|
||||
<ClCompile Include="lights.cpp" />
|
||||
<ClCompile Include="line2d.cpp" />
|
||||
<ClCompile Include="loadTextures.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="makeColorKeyTexture.cpp" />
|
||||
|
|
Loading…
Reference in New Issue