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-e03cc46cb475
master
fixeworks 2017-04-02 12:41:13 +00:00
parent f28e0ba53c
commit 56f0372862
9 changed files with 192 additions and 51 deletions

View File

@ -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)
{

View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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" />

View File

@ -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" />

View File

@ -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" />

View File

@ -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" />

View File

@ -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" />