Structs continued

Basic functions finished and tested for matrix and vector.
dev-ui
Piotr Dziwinski 2012-05-01 20:05:48 +02:00
parent 7369b10a87
commit 2513f6556e
9 changed files with 629 additions and 301 deletions

View File

@ -697,7 +697,7 @@ FILE_PATTERNS = *.h \
# should be searched for input files as well. Possible values are YES and NO. # should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used. # If left blank NO is used.
RECURSIVE = NO RECURSIVE = YES
# The EXCLUDE tag can be used to specify files and/or directories that should be # The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a # excluded from the INPUT source files. This way you can easily exclude a
@ -705,7 +705,7 @@ RECURSIVE = NO
# Note that relative paths are relative to the directory from which doxygen is # Note that relative paths are relative to the directory from which doxygen is
# run. # run.
EXCLUDE = EXCLUDE = "src/old" "src/metafile"
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded # directories that are symbolic links (a Unix file system feature) are excluded

View File

@ -14,15 +14,18 @@
// * You should have received a copy of the GNU General Public License // * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/. // * along with this program. If not, see http://www.gnu.org/licenses/.
// math/const.h /** @defgroup MathConstModule math/const.h
Contains the math constants used in math functions.
/* Math constants */ */
#pragma once #pragma once
// Math module namespace // Math module namespace
namespace Math namespace Math
{ {
/* @{ */ // start of group
//! Tolerance level -- minimum accepted float value //! Tolerance level -- minimum accepted float value
const float TOLERANCE = 1e-6f; const float TOLERANCE = 1e-6f;
@ -41,4 +44,6 @@ namespace Math
const float DEG_TO_RAD = 0.01745329251994329547f; const float DEG_TO_RAD = 0.01745329251994329547f;
//! Radians to degrees multiplier //! Radians to degrees multiplier
const float RAD_TO_DEG = 57.29577951308232286465f; const float RAD_TO_DEG = 57.29577951308232286465f;
};
/* @} */ // end of group
}; // namespace Math

View File

@ -14,9 +14,9 @@
// * You should have received a copy of the GNU General Public License // * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/. // * along with this program. If not, see http://www.gnu.org/licenses/.
// math/func.h /** @defgroup MathFuncModule math/func.h
Contains common math functions.
/* Common math functions */ */
#pragma once #pragma once
@ -26,9 +26,13 @@
#include <cmath> #include <cmath>
#include <cstdlib> #include <cstdlib>
// Math module namespace
namespace Math namespace Math
{ {
/* @{ */ // start of group
//! Compares \a a and \a b within \a tolerance //! Compares \a a and \a b within \a tolerance
inline bool IsEqual(float a, float b, float tolerance = Math::TOLERANCE) inline bool IsEqual(float a, float b, float tolerance = Math::TOLERANCE)
{ {
@ -254,5 +258,6 @@ inline float Bounce(float progress, float middle, float bounce)
} }
} }
}; // namespace Math /* @} */ // end of group
}; // namespace Math

View File

@ -14,9 +14,9 @@
// * You should have received a copy of the GNU General Public License // * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/. // * along with this program. If not, see http://www.gnu.org/licenses/.
// math/matrix.h /** @defgroup MathMatrixModule math/matrix.h
Contains the Matrix struct and related functions.
/* Matrix struct and functions */ */
#pragma once #pragma once
@ -27,35 +27,39 @@
#include <cmath> #include <cmath>
#include <cassert> #include <cassert>
// Math module namespace // Math module namespace
namespace Math namespace Math
{ {
/** 4x4 matrix /* @{ */ // start of group
/** \struct Matrix math/matrix.h
\brief 4x4 matrix
Represents an universal 4x4 matrix that can be used in OpenGL and DirectX engines. Represents an universal 4x4 matrix that can be used in OpenGL and DirectX engines.
Contains the required methods for operating on matrices (inverting, multiplying, etc.). Contains the required methods for operating on matrices (inverting, multiplying, etc.).
The internal representation is a 16-value table in column-major order, thus: The internal representation is a 16-value table in column-major order, thus:
m[0 ] m[4 ] m[8 ] m[12] \verbatim m[0 ] m[4 ] m[8 ] m[12]
m[1 ] m[5 ] m[9 ] m[13] m[1 ] m[5 ] m[9 ] m[13]
m[2 ] m[6 ] m[10] m[14] m[2 ] m[6 ] m[10] m[14]
m[3 ] m[7 ] m[11] m[15] m[3 ] m[7 ] m[11] m[15] \endverbatim
This representation is native to OpenGL; DirectX requires transposing the matrix. This representation is native to OpenGL; DirectX requires transposing the matrix.
The order of multiplication of matrix and vector is also OpenGL-native The order of multiplication of matrix and vector is also OpenGL-native
(see the method Vector::MultiplyMatrix). (see the function MatrixVectorMultiply).
All methods are made inline to maximize optimization. All methods are made inline to maximize optimization.
TODO test Unit tests for the structure and related functions are in module: math/test/matrix_test.cpp.
**/ **/
struct Matrix struct Matrix
{ {
//! Matrix values in column-major format //! Matrix values in column-major order
float m[16]; float m[16];
//! Creates the indentity matrix //! Creates the indentity matrix
@ -64,14 +68,28 @@ struct Matrix
LoadIdentity(); LoadIdentity();
} }
//! Creates the matrix from given values //! Creates the matrix from 1D array
/** \a m values in column-major format */ /** \a m matrix values in column-major order */
inline Matrix(const float (&m)[16]) inline Matrix(const float (&m)[16])
{ {
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
this->m[i] = m[i]; this->m[i] = m[i];
} }
//! Creates the matrix from 2D array
/** The array's first index is row, second is column.
\a m array with values */
inline Matrix(const float (&m)[4][4])
{
for (int c = 0; c < 4; ++c)
{
for (int r = 0; r < 4; ++r)
{
this->m[4*c+r] = m[r][c];
}
}
}
//! Loads the zero matrix //! Loads the zero matrix
inline void LoadZero() inline void LoadZero()
{ {
@ -83,7 +101,10 @@ struct Matrix
inline void LoadIdentity() inline void LoadIdentity()
{ {
LoadZero(); LoadZero();
m[0] = m[5] = m[10] = m[15] = 1.0f; /* (1,1) */ m[0 ] = 1.0f;
/* (2,2) */ m[5 ] = 1.0f;
/* (3,3) */ m[10] = 1.0f;
/* (4,4) */ m[15] = 1.0f;
} }
//! Transposes the matrix //! Transposes the matrix
@ -114,7 +135,7 @@ struct Matrix
//! Calculates the cofactor of the matrix //! Calculates the cofactor of the matrix
/** \a r row (0 to 3) /** \a r row (0 to 3)
\a c column (0 to 3) \a c column (0 to 3)
\returns the cofactor or 0.0f if invalid r, c given*/ \returns the cofactor */
inline float Cofactor(int r, int c) const inline float Cofactor(int r, int c) const
{ {
assert(r >= 0 && r <= 3); assert(r >= 0 && r <= 3);
@ -293,38 +314,48 @@ struct Matrix
return result; return result;
} }
//! Inverts the matrix //! Calculates the inverse matrix
inline void Invert() /** The determinant of the matrix must not be zero.
\returns the inverted matrix */
inline Matrix Inverse() const
{ {
float d = Det(); float d = Det();
assert(! IsZero(d)); assert(! IsZero(d));
Matrix temp = *this; float result[16] = { 0.0f };
for (int r = 0; r < 4; ++r) for (int r = 0; r < 4; ++r)
{ {
for (int c = 0; c < 4; ++c) for (int c = 0; c < 4; ++c)
{ {
m[4*r+c] = (1.0f / d) * temp.Cofactor(r, c); // Already transposed!
result[4*r+c] = (1.0f / d) * Cofactor(r, c);
} }
} }
return Matrix(result);
} }
//! Multiplies the matrix with the given matrix //! Calculates the multiplication of this matrix * given matrix
/** \a right right-hand matrix */ /** \a right right-hand matrix
inline void Multiply(const Matrix &right) \returns multiplication result */
inline Matrix Multiply(const Matrix &right) const
{ {
Matrix left = *this; float result[16] = { 0.0f };
for (int c = 0; c < 4; ++c) for (int c = 0; c < 4; ++c)
{ {
for (int r = 0; r < 4; ++r) for (int r = 0; r < 4; ++r)
{ {
m[4*c+r] = 0.0f; result[4*c+r] = 0.0f;
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
m[4*c+r] += left.m[4*i+r] * right.m[4*c+i]; result[4*c+r] += m[4*i+r] * right.m[4*c+i];
} }
} }
} }
return Matrix(result);
} }
//! Loads view matrix from the given vectors //! Loads view matrix from the given vectors
@ -374,56 +405,122 @@ struct Matrix
// Start building the matrix. The first three rows contains the basis // Start building the matrix. The first three rows contains the basis
// vectors used to rotate the view to point at the lookat point // vectors used to rotate the view to point at the lookat point
LoadIdentity(); LoadIdentity();
m[0 ] = right.x; m[1 ] = up.x; m[2 ] = view.x;
m[4 ] = right.y; m[5 ] = up.y; m[6 ] = view.y; /* (1,1) */ m[0 ] = right.x;
m[8 ] = right.z; m[9 ] = up.z; m[10] = view.z; /* (2,1) */ m[1 ] = up.x;
/* (3,1) */ m[2 ] = view.x;
/* (1,2) */ m[4 ] = right.y;
/* (2,2) */ m[5 ] = up.y;
/* (3,2) */ m[6 ] = view.y;
/* (1,3) */ m[8 ] = right.z;
/* (2,3) */ m[9 ] = up.z;
/* (3,3) */ m[10] = view.z;
// Do the translation values (rotations are still about the eyepoint) // Do the translation values (rotations are still about the eyepoint)
m[12] = - DotProduct(from, right); /* (1,4) */ m[12] = -DotProduct(from, right);
m[13] = - DotProduct(from, up); /* (2,4) */ m[13] = -DotProduct(from, up);
m[14] = - DotProduct(from, view); /* (3,4) */ m[14] = -DotProduct(from, view);
} }
//! Loads a perspective projection matrix
/** \a fov field of view in radians
\a aspect aspect ratio (width / height)
\a nearPlane distance to near cut plane
\a farPlane distance to far cut plane */
inline void LoadProjection(float fov = 1.570795f, float aspect = 1.0f, inline void LoadProjection(float fov = 1.570795f, float aspect = 1.0f,
float nearPlane = 1.0f, float farPlane = 1000.0f) float nearPlane = 1.0f, float farPlane = 1000.0f)
{ {
// TODO assert(fabs(farPlane - nearPlane) >= 0.01f);
assert(fabs(sin(fov / 2)) >= 0.01f);
float w = aspect * (cosf(fov / 2) / sinf(fov / 2));
float h = 1.0f * (cosf(fov / 2) / sinf(fov / 2));
float q = farPlane / (farPlane - nearPlane);
LoadZero();
/* (1,1) */ m[0 ] = w;
/* (2,2) */ m[5 ] = h;
/* (3,3) */ m[10] = q;
/* (3,4) */ m[14] = 1.0f;
/* (4,3) */ m[11] = -q * nearPlane;
} }
//! Loads a translation matrix from given vector
/** \a trans vector of translation*/
inline void LoadTranslation(const Vector &trans) inline void LoadTranslation(const Vector &trans)
{ {
LoadIdentity(); LoadIdentity();
m[12] = trans.x; /* (1,4) */ m[12] = trans.x;
m[13] = trans.y; /* (2,4) */ m[13] = trans.y;
m[14] = trans.z; /* (3,4) */ m[14] = trans.z;
} }
//! Loads a scaling matrix fom given vector
/** \a scale vector with scaling factors for X, Y, Z */
inline void LoadScale(const Vector &scale) inline void LoadScale(const Vector &scale)
{ {
LoadIdentity(); LoadIdentity();
m[0] = scale.x; /* (1,1) */ m[0 ] = scale.x;
m[5] = scale.y; /* (2,2) */ m[5 ] = scale.y;
m[10] = scale.z; /* (3,3) */ m[10] = scale.z;
} }
//! Loads a rotation matrix along the X axis
/** \a angle angle in radians */
inline void LoadRotationX(float angle) inline void LoadRotationX(float angle)
{ {
// TODO LoadIdentity();
/* (2,2) */ m[5 ] = cosf(angle);
/* (3,2) */ m[6 ] = sinf(angle);
/* (2,3) */ m[9 ] = -sinf(angle);
/* (3,3) */ m[10] = cosf(angle);
} }
//! Loads a rotation matrix along the Y axis
/** \a angle angle in radians */
inline void LoadRotationY(float angle) inline void LoadRotationY(float angle)
{ {
// TODO LoadIdentity();
/* (1,1) */ m[0 ] = cosf(angle);
/* (3,1) */ m[2 ] = -sinf(angle);
/* (1,3) */ m[8 ] = sinf(angle);
/* (3,3) */ m[10] = cosf(angle);
} }
//! Loads a rotation matrix along the Z axis
/** \a angle angle in radians */
inline void LoadRotationZ(float angle) inline void LoadRotationZ(float angle)
{ {
// TODO LoadIdentity();
/* (1,1) */ m[0 ] = cosf(angle);
/* (2,1) */ m[1 ] = sinf(angle);
/* (1,2) */ m[4 ] = -sinf(angle);
/* (2,2) */ m[5 ] = cosf(angle);
} }
//! Loads a rotation matrix along the given axis
/** \a dir axis of rotation
\a angle angle in radians */
inline void LoadRotation(const Vector &dir, float angle) inline void LoadRotation(const Vector &dir, float angle)
{ {
// TODO float cos = cosf(angle);
float sin = sinf(angle);
Vector v = Math::Normalize(dir);
LoadIdentity();
/* (1,1) */ m[0 ] = (v.x * v.x) * (1.0f - cos) + cos;
/* (2,1) */ m[1 ] = (v.x * v.y) * (1.0f - cos) - (v.z * sin);
/* (3,1) */ m[2 ] = (v.x * v.z) * (1.0f - cos) + (v.y * sin);
/* (1,2) */ m[4 ] = (v.y * v.x) * (1.0f - cos) + (v.z * sin);
/* (2,2) */ m[5 ] = (v.y * v.y) * (1.0f - cos) + cos ;
/* (3,2) */ m[6 ] = (v.y * v.z) * (1.0f - cos) - (v.x * sin);
/* (1,3) */ m[8 ] = (v.z * v.x) * (1.0f - cos) - (v.y * sin);
/* (2,3) */ m[9 ] = (v.z * v.y) * (1.0f - cos) + (v.x * sin);
/* (3,3) */ m[10] = (v.z * v.z) * (1.0f - cos) + cos;
} }
//! Calculates the matrix to make three rotations in the order X, Z and Y //! Calculates the matrix to make three rotations in the order X, Z and Y
@ -453,32 +550,38 @@ struct Matrix
} }
}; };
//! Convenience function for inverting a matrix //! Convenience function for getting transposed matrix
/** \a m input matrix inline Matrix Transpose(const Matrix &m)
\a result result -- inverted matrix */
inline void InvertMatrix(const Matrix &m, Matrix &result)
{ {
result = m; Matrix result = m;
result.Invert(); result.Transpose();
return result;
} }
//! Convenience function for multiplying a matrix //! Convenience function for multiplying a matrix
/** \a left left-hand matrix /** \a left left-hand matrix
\a right right-hand matrix \a right right-hand matrix
\a result result -- multiplied matrices */ \returns multiplied matrices */
inline void MultiplyMatrices(const Matrix &left, const Matrix &right, Matrix &result) inline Matrix MultiplyMatrices(const Matrix &left, const Matrix &right)
{ {
result = left; return left.Multiply(right);
result.Multiply(right);
} }
//! Calculates the result of multiplying m * v //! Calculates the result of multiplying m * v
/** The multiplication is performed thus:
\verbatim [ m.m[0 ] m.m[4 ] m.m[8 ] m.m[12] ] [ v.x ]
[ m.m[1 ] m.m[5 ] m.m[9 ] m.m[13] ] [ v.y ]
[ m.m[2 ] m.m[6 ] m.m[10] m.m[14] ] * [ v.z ]
[ m.m[3 ] m.m[7 ] m.m[11] m.m[15] ] [ 1 ] \endverbatim
The result, a 4x1 vector is then converted to 3x1 by dividing
x,y,z coords by the fourth coord (w). */
inline Vector MatrixVectorMultiply(const Matrix &m, const Vector &v) inline Vector MatrixVectorMultiply(const Matrix &m, const Vector &v)
{ {
float x = v.x * m.m[0 ] + v.y * m.m[4 ] + v.z * m.m[8 ] + m.m[12]; float x = v.x * m.m[0 ] + v.y * m.m[4 ] + v.z * m.m[8 ] + m.m[12];
float y = v.x * m.m[1 ] + v.y * m.m[5 ] + v.z * m.m[9 ] + m.m[13]; float y = v.x * m.m[1 ] + v.y * m.m[5 ] + v.z * m.m[9 ] + m.m[13];
float z = v.x * m.m[2 ] + v.y * m.m[6 ] + v.z * m.m[10] + m.m[14]; float z = v.x * m.m[2 ] + v.y * m.m[6 ] + v.z * m.m[10] + m.m[14];
float w = v.x * m.m[4 ] + v.y * m.m[7 ] + v.z * m.m[11] + m.m[15]; float w = v.x * m.m[4 ] + v.y * m.m[7 ] + v.z * m.m[11] + m.m[15];
if (IsZero(w)) if (IsZero(w))
return Vector(x, y, z); return Vector(x, y, z);
@ -503,4 +606,6 @@ inline bool MatricesEqual(const Math::Matrix &m1, const Math::Matrix &m2,
return true; return true;
} }
/* @} */ // end of group
}; // namespace Math }; // namespace Math

View File

@ -14,26 +14,49 @@
// * You should have received a copy of the GNU General Public License // * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/. // * along with this program. If not, see http://www.gnu.org/licenses/.
// math/point.h /** @defgroup MathPointModule math/point.h
Contains the Point struct and related functions.
/* Point struct and functions */ */
#pragma once #pragma once
#include <cmath> #include <cmath>
/* TODO
FPOINT RotatePoint(FPOINT center, float angle, FPOINT p);
FPOINT RotatePoint(float angle, FPOINT p);
FPOINT RotatePoint(float angle, float dist);
void RotatePoint(float cx, float cy, float angle, float &px, float &py);
void RotatePoint(D3DVECTOR center, float angleH, float angleV, D3DVECTOR &p);
void RotatePoint2(D3DVECTOR center, float angleH, float angleV, D3DVECTOR &p);
float RotateAngle(float x, float y);
float RotateAngle(FPOINT center, FPOINT p1, FPOINT p2);
float MidPoint(FPOINT a, FPOINT b, float px);
BOOL IsInsideTriangle(FPOINT a, FPOINT b, FPOINT c, FPOINT p);
BOOL LineFunction(FPOINT p1, FPOINT p2, float &a, float &b);
float IsInsideTriangle(FPOINT a, FPOINT b, FPOINT c);
*/
// Math module namespace
namespace Math namespace Math
{ {
/** 2D Point /* @{ */ // start of group
/** \struct Point math/point.h
\brief 2D point
Represents a 2D point (x, y). Represents a 2D point (x, y).
Contains the required methods for operating on points. Contains the required methods for operating on points.
All methods are made inline to maximize optimization. All methods are made inline to maximize optimization.
TODO test
*/ */
struct Point struct Point
{ {
@ -42,43 +65,38 @@ struct Point
//! Y coord //! Y coord
float y; float y;
//! Constructs a zero point: (0,0)
inline Point() inline Point()
{ {
LoadZero(); LoadZero();
} }
//! Constructs a point from given coords: (x,y)
inline Point(float x, float y) inline Point(float x, float y)
{ {
this->x = x; this->x = x;
this->y = y; this->y = y;
} }
//! Sets the zero point: (0,0)
inline void LoadZero() inline void LoadZero()
{ {
x = y = 0.0f; x = y = 0.0f;
} }
//! Returns the distance from (0,0) to the point (x,y)
inline float Length() inline float Length()
{ {
return std::sqrt(x*x + y*y); return std::sqrt(x*x + y*y);
} }
}; };
/* TODO
FPOINT RotatePoint(FPOINT center, float angle, FPOINT p);
FPOINT RotatePoint(float angle, FPOINT p);
FPOINT RotatePoint(float angle, float dist);
void RotatePoint(float cx, float cy, float angle, float &px, float &py);
void RotatePoint(D3DVECTOR center, float angleH, float angleV, D3DVECTOR &p);
void RotatePoint2(D3DVECTOR center, float angleH, float angleV, D3DVECTOR &p);
float Length(FPOINT a, FPOINT b);
float RotateAngle(float x, float y); //! Returns the distance between two points
float RotateAngle(FPOINT center, FPOINT p1, FPOINT p2); inline float Distance(const Point &a, const Point &b)
float MidPoint(FPOINT a, FPOINT b, float px); {
BOOL IsInsideTriangle(FPOINT a, FPOINT b, FPOINT c, FPOINT p); return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
BOOL LineFunction(FPOINT p1, FPOINT p2, float &a, float &b); /* @} */ // end of group
*/ }; // namespace Math
};

View File

@ -4,11 +4,22 @@ set(CMAKE_BUILD_TYPE debug)
set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g -O0") set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g -O0")
add_executable(matrix_test matrix_test.cpp) add_executable(matrix_test matrix_test.cpp)
add_executable(vector_test vector_test.cpp)
enable_testing() enable_testing()
add_test(matrix_test ./matrix_test) add_test(matrix_test ./matrix_test)
add_test(vector_test ./vector_test)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS matrix_test) # 'make check' will compile the required test programs
# Note that 'make test' will still fail without compiled programs
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS matrix_test vector_test)
add_custom_target(distclean COMMAND rm -rf ./matrix_test CMakeFiles Testing cmake_install.cmake CMakeCache.txt CTestTestfile.cmake Makefile) # Files to be removed in distclean
set(REMOVE_FILES
CMakeFiles Testing cmake_install.cmake CMakeCache.txt CTestTestfile.cmake Makefile
./matrix_test
./vector_test
)
add_custom_target(distclean COMMAND rm -rf ${REMOVE_FILES})

View File

@ -18,8 +18,8 @@
/* Unit tests for Matrix struct /* Unit tests for Matrix struct
Test data was randomly generated and the expected Test data was randomly generated and the expected results
results calculated using GNU Octave. calculated using GNU Octave.
*/ */
@ -30,53 +30,77 @@
using namespace std; using namespace std;
const float TEST_TOLERANCE = 1e-5; const float TEST_TOLERANCE = 1e-6;
int TestCofactor() int TestCofactor()
{ {
const float TEST_MATRIX[16] = const Math::Matrix mat1(
{ (float[4][4])
-0.306479, {
-0.520207, { 0.610630320796245, 1.059932357918312, -1.581674311378210, 1.782214448453331 },
0.127906, { 0.191028848211526, -0.813898708757524, 1.516114203870644, 0.395202639476002 },
0.632922, { 0.335142750345279, -0.346586619596529, 0.545382042472336, -0.879268918923072 },
{ 1.417588151657198, 1.450841789070141, 0.219080104196171, 0.378724047481655 }
}
);
-0.782876, const Math::Matrix expectedCofactors1(
0.015264, (float[4][4])
0.337479, {
1.466013, { -2.402679369186782, 2.282452509293019, 1.722732204057644, -0.746939701104385 },
{ -0.687677756877654, 1.168949180331164, -0.985354966837796, -1.334071111592705 },
0.072725, { -5.115621958424845, 4.229724770159009, 2.529000630782808, 1.481632618355891 },
-0.315123, { 0.147480897398694, -2.140677680337111, -1.207189492265546, 0.151236920408051 }
1.613198, }
-0.577377, );
0.962397,
-1.320724,
1.467588,
0.579020
};
const float EXPECTED_RESULTS[4][4] =
{
{ 2.791599, -0.249952, 1.065075, -1.356570 },
{ 3.715943, -1.537511, 0.094812, -0.074520 },
{ 1.034500, -0.731752, -0.920756, -0.196235 },
{ 1.213928, -1.236857, 0.779741, -0.678482 }
};
Math::Matrix mat(TEST_MATRIX);
for (int r = 0; r < 4; ++r) for (int r = 0; r < 4; ++r)
{ {
for (int c = 0; c < 4; ++c) for (int c = 0; c < 4; ++c)
{ {
float ret = mat.Cofactor(r, c); float ret = mat1.Cofactor(r, c);
float exp = EXPECTED_RESULTS[r][c]; float exp = expectedCofactors1.m[4*c+r];
if (! Math::IsEqual(ret, exp, TEST_TOLERANCE)) if (! Math::IsEqual(ret, exp, TEST_TOLERANCE))
{ {
fprintf(stderr, "Cofactor r=%d, c=%d, %f (returned) != %f (expected)\n", r, c, ret, exp); fprintf(stderr, "Cofactors 1 mismatch!\n");
return 4*c+r; fprintf(stderr, "r=%d, c=%d, %f (returned) != %f (expected)\n", r, c, ret, exp);
return __LINE__;
}
}
}
const Math::Matrix mat2(
(float[4][4])
{
{ 0.9845099464982393, -0.9091233416532389, -0.6272243714245945, 0.4645001858944354 },
{ -0.1333308471483736, 0.9128181433725897, -1.0937461393836190, 0.3180936795928376 },
{ -0.0654324396846289, 0.1014641705415945, 1.5107709042683430, -0.0240560430414690 },
{ 0.0179638644093347, -1.0695585982782767, -0.1741250853101032, 1.0803106709464336 }
}
);
const Math::Matrix expectedCofactors2(
(float[4][4])
{
{ 2.0861102207614466, 0.2989010779528912, 0.0746276150537432, 0.2732659822656097 },
{ 0.6850002886584565, 1.5513169659641379, -0.0503743176545917, 1.5163672441575642 },
{ 1.2385556680997216, 1.1827709562505695, 1.2282813085138962, 1.3483789679871401 },
{ -1.0710790241539783, -0.5589604503588883, 0.0100959837872308, 1.1897872684455839 }
}
);
for (int r = 0; r < 4; ++r)
{
for (int c = 0; c < 4; ++c)
{
float ret = mat2.Cofactor(r, c);
float exp = expectedCofactors2.m[4*c+r];
if (! Math::IsEqual(ret, exp, TEST_TOLERANCE))
{
fprintf(stderr, "Cofactors 2 mismatch!\n");
fprintf(stderr, "r=%d, c=%d, %f (returned) != %f (expected)\n", r, c, ret, exp);
return __LINE__;
} }
} }
} }
@ -86,96 +110,105 @@ int TestCofactor()
int TestDet() int TestDet()
{ {
const float TEST_MATRIX[16] = const Math::Matrix mat1(
(float[4][4])
{
{ -0.95880162984708284, 0.24004047608997131, -0.78172309932665407, -0.11604124457222834 },
{ -0.36230592086261376, -0.75778166876017261, 0.33041059404631740, -1.06001391941094836 },
{ 0.00260215210936187, 1.27485610196385113, -0.26149859846418033, -0.59669701186364876 },
{ 0.36899429848485432, 3.01720896813933104, 2.10311476609438719, -1.68627076626448269 }
}
);
const float expectedDet1 = 4.07415413729671;
float ret1 = mat1.Det();
if (! Math::IsEqual(ret1, expectedDet1, TEST_TOLERANCE))
{ {
0.85554, fprintf(stderr, "Det mismatch!\n");
0.11624, fprintf(stderr, "%f (returned) != %f (expected)\n", ret1, expectedDet1);
1.30411, return __LINE__;
0.81467, }
0.49692, const Math::Matrix mat2(
-1.92483, (float[4][4])
-1.33543, {
0.85042, { -1.0860073221346871, 0.9150354098189495, -0.2723201933559999, 0.2922832160271507 },
{ -1.0248331304801788, -2.5081237461125205, -1.0277123574586633, -0.2254690663329798 },
{ -1.4227635282899367, -0.0403846809122684, 0.9216148477171653, 1.2517067488015878 },
{ -0.1160254467152022, 0.8270675274393656, 1.0327218739781614, -0.3674886870220400 }
}
);
-0.16775, const float expectedDet2 = -6.35122307880942;
0.35344,
1.40673,
0.13961,
1.40709, float ret2 = mat2.Det();
0.11731, if (! Math::IsEqual(ret2, expectedDet2, TEST_TOLERANCE))
0.69042,
0.91216
};
const float EXPECTED_RESULT = 0.084360;
float ret = Math::Matrix(TEST_MATRIX).Det();
if (! Math::IsEqual(ret, EXPECTED_RESULT, TEST_TOLERANCE))
{ {
fprintf(stderr, "Det %f (returned) != %f (expected)\n", ret, EXPECTED_RESULT); fprintf(stderr, "Det mismatch!\n");
return 1; fprintf(stderr, "%f (returned) != %f (expected)\n", ret2, expectedDet2);
return __LINE__;
} }
return 0; return 0;
} }
int TestInvert() int TestInverse()
{ {
const float TEST_MATRIX[16] = const Math::Matrix mat1(
(float[4][4])
{
{ -2.2829352811514658, -0.9103222363187888, 0.2792976509411680, -0.7984393573193174 },
{ 2.4823665798689589, -0.0599056759070980, 0.3832364352926366, -1.6404257204372739 },
{ -0.3841952272526398, -0.8377700696457873, -0.3416328338427138, 1.1746577275723329 },
{ 0.1746031241954947, -0.4952532117949962, 0.2155084379835037, -1.6586460437329220 }
}
);
const Math::Matrix expectedInverse1(
(float[4][4])
{
{ -0.119472603171041, 0.331675963276297, 0.187516809009720, -0.137720814290806 },
{ -0.387591686166085, -0.487284946727583, -0.798527541290274, 0.102991635972060 },
{ 2.601905603425902, 2.606899016264679, -0.528006148839176, -4.204703326522837 },
{ 0.441220327151392, 0.519128136207318, 0.189567009205522, -1.194469716136194 }
}
);
Math::Matrix inverse1 = mat1.Inverse();
if (! Math::MatricesEqual(inverse1, expectedInverse1, TEST_TOLERANCE))
{ {
1.4675123, fprintf(stderr, "Inverse 1 mismatch!\n");
-0.2857923, return __LINE__;
-0.0496217, }
-1.2825408,
-0.2804135, const Math::Matrix mat2(
-0.0826255, (float[4][4])
-0.6825495, {
1.1661259, { -0.05464332404298505, -0.64357755258235749, -0.13017671677619302, -0.56742332785888006 },
{ 0.29048383600458222, -0.91517047043724875, 0.84517524415561684, 0.51628195547960565 },
{ 0.00946488004480186, -0.89077382212689293, 0.73565573766341397, -0.15932513521840930 },
{ -1.01244718912499132, -0.27840911963972276, -0.39189681211309862, 1.18315064340192055 }
}
);
0.0032798, const Math::Matrix expectedInverse2(
0.5999200, (float[4][4])
-1.8359883, {
-1.1894424, { 0.771302711132012, 1.587542278361995, -2.003075114445104, -0.592574156227379 },
{ -1.208929259769431, -0.786598967848473, 0.607335305808052, -0.154759693303324 },
{ -1.500037668208218, -0.774300278997914, 1.917800427261255, -0.123268572651291 },
{ -0.121314770937944, 0.916925149209746, -0.935924950785014, 0.260875394250671 }
}
);
-1.1501538, Math::Matrix inverse2 = mat2.Inverse();
-2.8792485,
0.0299345,
0.3730919
};
const float EXPECTED_RESULT[16] = if (! Math::MatricesEqual(inverse2, expectedInverse2, TEST_TOLERANCE))
{ {
0.685863, fprintf(stderr, "Inverse 2 mismatch!\n");
0.562274, return __LINE__;
-0.229722,
-0.132079,
-0.266333,
-0.139862,
0.054211,
-0.305568,
-0.130817,
-0.494076,
-0.358226,
-0.047477,
0.069486,
0.693649,
-0.261074,
-0.081200
};
Math::Matrix mat(TEST_MATRIX);
mat.Invert();
if (! Math::MatricesEqual(mat, EXPECTED_RESULT, TEST_TOLERANCE))
{
fprintf(stderr, "Invert mismatch\n");
return 1;
} }
return 0; return 0;
@ -183,81 +216,78 @@ int TestInvert()
int TestMultiply() int TestMultiply()
{ {
const float TEST_MATRIX_A[16] = const Math::Matrix mat1A(
(float[4][4])
{
{ 0.6561727049162027, -1.4180263627131411, -0.8271026046117423, 2.3919331748512578 },
{ -0.6035665535146352, 0.0150827348790615, -0.7090794192822540, 0.9057604704594814 },
{ -0.9871045001223655, -0.4980646811455065, 0.3806177002298990, 0.1520583649240934 },
{ -0.2721911170792712, 0.7627928194552067, -0.1504091336784158, 0.9747545351840121 }
}
);
const Math::Matrix mat1B(
(float[4][4])
{
{ -0.2643735892448818, -0.7542994492819621, 0.6082322350568750, 0.0581733424861419 },
{ 1.0293246070431237, 0.1979285388251341, -0.2932031385332818, 0.8838407179018929 },
{ 0.3448687251553114, 0.5031654871245456, 0.7554693012922442, -0.4845315903845708 },
{ -1.8662838497278593, -0.7843850624747805, 0.1389026096476257, -1.3686415408300689 }
}
);
const Math::Matrix expectedMultiply1(
(float[4][4])
{
{ -6.382352236417988, -3.067984733682130, 0.522270304251466, -4.088079444498280 },
{ -1.759853366848825, -0.608994052024491, -0.781406179437379, -0.917870775786188 },
{ -0.404226802169062, 0.718232546720114, -0.145688356880835, -0.890167707987175 },
{ -1.013918490922430, -0.483971504099758, -0.367442194643757, -0.602858486133615 }
}
);
Math::Matrix multiply1 = Math::MultiplyMatrices(mat1A, mat1B);
if (! Math::MatricesEqual(multiply1, expectedMultiply1, TEST_TOLERANCE ) )
{ {
-1.931420, fprintf(stderr, "Multiply 1 mismath!\n");
0.843410, return __LINE__;
0.476929, }
-0.493435,
1.425659,
-0.176331,
0.129096,
0.551081,
-0.543530,
-0.190783,
-0.084744,
1.379547,
-0.473377,
1.643398,
0.400539,
0.702937
};
const float TEST_MATRIX_B[16] = const Math::Matrix mat2A(
(float[4][4])
{
{ 0.8697203025776754, 2.1259475710644935, 1.7856691009707812, -2.1563963348328126 },
{ 1.5888074489288735, -0.0794849733953615, 0.7307782768677457, 0.7943129159612630 },
{ 0.2859761537233830, -0.6231231890384962, -0.0496743172880377, -0.8137857518646087 },
{ 1.2670547229512983, -0.5305171374831831, -0.4987412674062375, -1.1257327113869595 }
}
);
const Math::Matrix mat2B(
(float[4][4])
{
{ 1.1321105701165317, 0.1759563504574463, -2.0675778912000418, 1.4840339814245538 },
{ -1.5117280888829916, -0.0933013188828093, -0.2079262944351640, 0.9575727579539316 },
{ 0.3615378398970173, 1.2465163589027248, 1.1326150997082589, 0.9921208694352303 },
{ -0.7357104529373861, -0.4774022005969588, -0.2118739096676499, 1.1427567093270703 }
}
);
const Math::Matrix expectedMultiply2(
(float[4][4])
{
{ 0.00283516267056338, 3.21001319965989307, 0.23910503934370686, 2.63380716363006107 },
{ 1.59868505822469742, 0.81869715594617765, -2.60905981088293570, 3.91445839239110294 },
{ 1.84650099286297942, 0.43504079532852930, -0.34555619012424243, -1.15152951542451487 },
{ 2.88434318563174585, 0.18818239851585700, -2.83579436909308980, -0.40890672198610400 }
}
);
Math::Matrix multiply2 = Math::MultiplyMatrices(mat2A, mat2B);
if (! Math::MatricesEqual(multiply2, expectedMultiply2, TEST_TOLERANCE ) )
{ {
0.3517561, fprintf(stderr, "Multiply 2 mismath!\n");
1.3903778, return __LINE__;
-0.8048254,
-0.4090024,
-1.5542159,
-0.6798636,
1.6003393,
-0.1467117,
0.5043620,
-0.0068779,
2.0697285,
-0.0463650,
0.9605451,
-0.4620149,
1.2525952,
-1.3409909
};
const float EXPECTED_RESULT[16] =
{
1.933875,
-0.467099,
0.251638,
-0.805156,
1.232207,
-1.737383,
-1.023401,
2.496859,
-2.086953,
-0.044468,
0.045688,
2.570036,
-2.559921,
-1.551155,
-0.244802,
0.056808
};
Math::Matrix matA(TEST_MATRIX_A);
Math::Matrix matB(TEST_MATRIX_B);
Math::Matrix mat;
Math::MultiplyMatrices(matA, matB, mat);
if (! Math::MatricesEqual(mat, Math::Matrix(EXPECTED_RESULT), TEST_TOLERANCE ) )
{
fprintf(stderr, "Multiply mismath!\n");
return 1;
} }
return 0; return 0;
@ -265,23 +295,25 @@ int TestMultiply()
int main() int main()
{ {
// Functions to test
int (*TESTS[])() =
{
TestCofactor,
TestDet,
TestInverse,
TestMultiply
};
const int TESTS_SIZE = sizeof(TESTS) / sizeof(*TESTS);
int result = 0; int result = 0;
for (int i = 0; i < TESTS_SIZE; ++i)
{
result = TESTS[i]();
if (result != 0)
return result;
}
result = TestCofactor(); fprintf(stderr, "All tests successful\n");
if (result != 0)
return result;
result = TestDet(); return 0;
if (result != 0)
return result;
result = TestInvert();
if (result != 0)
return result;
result = TestMultiply();
if (result != 0)
return result;
return result;
} }

View File

@ -0,0 +1,126 @@
// * This file is part of the COLOBOT source code
// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
// *
// * This program is free software: you can redistribute it and/or modify
// * it under the terms of the GNU General Public License as published by
// * the Free Software Foundation, either version 3 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 General Public License for more details.
// *
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
// math/test/vector_test.cpp
/* Unit tests for Vector struct
Test data was randomly generated and the expected results
calculated using GNU Octave.
*/
#include "../func.h"
#include "../vector.h"
#include <cstdio>
using namespace std;
const float TEST_TOLERANCE = 1e-6;
int TestLength()
{
Math::Vector vec(-1.288447945923275, 0.681452565308134, -0.633761098985957);
const float expectedLength = 1.58938001708428;
if (! Math::IsEqual(vec.Length(), expectedLength, TEST_TOLERANCE) )
{
fprintf(stderr, "Length mismatch!\n");
return __LINE__;
}
return 0;
}
int TestNormalize()
{
Math::Vector vec(1.848877241804398, -0.157262961268577, -1.963031403332377);
const Math::Vector expectedNormalized(0.6844609421393856, -0.0582193085618106, -0.7267212194481797);
vec.Normalize();
if (! Math::VectorsEqual(vec, expectedNormalized, TEST_TOLERANCE))
{
fprintf(stderr, "Normalize mismatch!\n");
return __LINE__;
}
return 0;
}
int TestDot()
{
Math::Vector vecA(0.8202190530968309, 0.0130926060162780, 0.2411914183883510);
Math::Vector vecB(-0.0524083951404069, 1.5564932716738220, -0.8971342631500536);
float expectedDot = -0.238988896477326;
if (! Math::IsEqual(Math::DotProduct(vecA, vecB), expectedDot, TEST_TOLERANCE) )
{
fprintf(stderr, "Dot product mismatch!\n");
return __LINE__;
}
return 0;
}
int TestCross()
{
Math::Vector vecA(1.37380499798567, 1.18054518384682, 1.95166361293121);
Math::Vector vecB(0.891657855926886, 0.447591335394532, -0.901604070087823);
Math::Vector expectedCross(-1.937932065431669, 2.978844370287636, -0.437739173833581);
Math::Vector expectedReverseCross = -expectedCross;
if (! Math::VectorsEqual(vecA.CrossMultiply(vecB), expectedCross, TEST_TOLERANCE) )
{
fprintf(stderr, "Cross product mismatch!\n");
return __LINE__;
}
if (! Math::VectorsEqual(vecB.CrossMultiply(vecA), expectedReverseCross, TEST_TOLERANCE) )
{
fprintf(stderr, "Reverse cross product mismatch!\n");
return __LINE__;
}
return 0;
}
int main()
{
// Functions to test
int (*TESTS[])() =
{
TestNormalize,
TestDot,
TestCross
};
const int TESTS_SIZE = sizeof(TESTS) / sizeof(*TESTS);
int result = 0;
for (int i = 0; i < TESTS_SIZE; ++i)
{
result = TESTS[i]();
if (result != 0)
return result;
}
fprintf(stderr, "All tests successful\n");
return 0;
}

View File

@ -14,9 +14,9 @@
// * You should have received a copy of the GNU General Public License // * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/. // * along with this program. If not, see http://www.gnu.org/licenses/.
// math/vector.h /** @defgroup MathVectorModule math/vector.h
Contains the Vector struct and related functions.
/* Vector struct and functions */ */
#pragma once #pragma once
@ -25,6 +25,7 @@
#include <cmath> #include <cmath>
/* /*
TODO TODO
@ -53,14 +54,17 @@ BOOL IsSamePlane(D3DVECTOR *plan1, D3DVECTOR *plan2);
namespace Math namespace Math
{ {
/** 3x1 Vector /* @{ */ // start of group
Represents an universal 3x1 vector that can be used in OpenGL and DirectX engines. /** \struct Vector math/vector.h
\brief 3D (3x1) vector
Represents a universal 3x1 vector that can be used in OpenGL and DirectX engines.
Contains the required methods for operating on vectors. Contains the required methods for operating on vectors.
All methods are made inline to maximize optimization. All methods are made inline to maximize optimization.
TODO test Unit tests for the structure and related functions are in module: math/test/vector_test.cpp.
*/ */
struct Vector struct Vector
@ -102,22 +106,28 @@ struct Vector
inline void Normalize() inline void Normalize()
{ {
float l = Length(); float l = Length();
if (Math::IsZero(l))
return;
x /= l; x /= l;
y /= l; y /= l;
z /= l; z /= l;
} }
//! Calculates the cross product with another vector //! Calculates the cross product with another vector
/** \a right right-hand side vector */ /** \a right right-hand side vector
inline void CrossMultiply(const Vector &right) \returns the cross product*/
inline Vector CrossMultiply(const Vector &right) const
{ {
Vector left = *this; float px = y * right.z - z * right.y;
x = left.y * right.z - left.z * right.y; float py = z * right.x - x * right.z;
y = left.z * right.x - left.x * right.z; float pz = x * right.y - y * right.x;
z = left.x * right.y - left.y * right.x; return Vector(px, py, pz);
} }
//! Calculates the dot product with another vector //! Calculates the dot product with another vector
/** \a right right-hand side vector
\returns the dot product */
inline float DotMultiply(const Vector &right) const inline float DotMultiply(const Vector &right) const
{ {
return x * right.x + y * right.y + z * right.z; return x * right.x + y * right.y + z * right.z;
@ -208,6 +218,14 @@ struct Vector
} }
}; };
//! Convenience function for getting normalized vector
inline Vector Normalize(const Vector &v)
{
Vector result = v;
result.Normalize();
return result;
}
//! Convenience function for calculating dot product //! Convenience function for calculating dot product
inline float DotProduct(const Vector &left, const Vector &right) inline float DotProduct(const Vector &left, const Vector &right)
{ {
@ -217,9 +235,17 @@ inline float DotProduct(const Vector &left, const Vector &right)
//! Convenience function for calculating cross product //! Convenience function for calculating cross product
inline Vector CrossProduct(const Vector &left, const Vector &right) inline Vector CrossProduct(const Vector &left, const Vector &right)
{ {
Vector result = left; return left.CrossMultiply(right);
result.CrossMultiply(right);
return result;
} }
//! Checks if two vectors are equal within given \a tolerance
inline bool VectorsEqual(const Vector &a, const Vector &b, float tolerance = Math::TOLERANCE)
{
return Math::IsEqual(a.x, b.x, tolerance)
&& Math::IsEqual(a.y, b.y, tolerance)
&& Math::IsEqual(a.z, b.z, tolerance);
}
/* @} */ // end of group
}; // namespace Math }; // namespace Math