Applied polar decomposition patch. Fixes Issue 621. Thanks to Christian for the report, Joshua for the fix, Dongsoo for checking the fix.
Applied picking cloth patch. Fixes Issue 646. Thanks to Dongsoo. Applied patch Softbody updateConstraints. Fixes Issue 503. Thanks to Dave Bruce Phillips and Dongsoo. Fix various warnigns under Mac OSX.
This commit is contained in:
@@ -8,11 +8,24 @@ INCLUDE_DIRECTORIES(
|
||||
)
|
||||
|
||||
LINK_LIBRARIES(
|
||||
cppunit BulletMultiThreaded BulletDynamics BulletCollision LinearMath
|
||||
cppunit
|
||||
BulletMultiThreaded
|
||||
BulletSoftBody
|
||||
BulletDynamics
|
||||
BulletCollision
|
||||
LinearMath
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE(AppBulletUnitTests
|
||||
Main.cpp
|
||||
TestBulletOnly.h
|
||||
TestLinearMath.h
|
||||
)
|
||||
Main.cpp
|
||||
TestBulletOnly.h
|
||||
TestLinearMath.h
|
||||
TestCholeskyDecomposition.cpp
|
||||
TestCholeskyDecomposition.h
|
||||
TestPolarDecomposition.cpp
|
||||
TestPolarDecomposition.h
|
||||
btCholeskyDecomposition.cpp
|
||||
btCholeskyDecomposition.h
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -7,9 +7,15 @@
|
||||
|
||||
#include "TestBulletOnly.h"
|
||||
#include "TestLinearMath.h"
|
||||
#include "TestPolarDecomposition.h"
|
||||
#include "TestCholeskyDecomposition.h"
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION( TestLinearMath );
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION( TestBulletOnly );
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION( TestPolarDecomposition );
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION( TestCholeskyDecomposition );
|
||||
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION( TestLinearMath );
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION( TestBulletOnly );
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
|
||||
68
UnitTests/BulletUnitTests/TestCholeskyDecomposition.cpp
Normal file
68
UnitTests/BulletUnitTests/TestCholeskyDecomposition.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "TestCholeskyDecomposition.h"
|
||||
#include "btCholeskyDecomposition.h"
|
||||
|
||||
void TestCholeskyDecomposition::testZeroMatrix()
|
||||
{
|
||||
const btMatrix3x3 A(0,0,0,0,0,0,0,0,0);
|
||||
const int result = choleskyDecompose(A, L);
|
||||
|
||||
// The zero matrix is not positive definite so the decomposition does not
|
||||
// exist.
|
||||
CPPUNIT_ASSERT(result != 0);
|
||||
}
|
||||
|
||||
void TestCholeskyDecomposition::testIdentityMatrix()
|
||||
{
|
||||
const btMatrix3x3 A = I;
|
||||
const int result = choleskyDecompose(A, L);
|
||||
|
||||
// The identity is a special case where the result should also be the
|
||||
// identity.
|
||||
CPPUNIT_ASSERT(result == 0);
|
||||
CPPUNIT_ASSERT(equal(L, I));
|
||||
}
|
||||
|
||||
void TestCholeskyDecomposition::testPositiveDefiniteMatrix()
|
||||
{
|
||||
const btMatrix3x3 M(3,0,0,1,2,0,3,2,1);
|
||||
const btMatrix3x3 A = M * M.transpose();
|
||||
const int result = choleskyDecompose(A, L);
|
||||
|
||||
// By construction, the resulting decomposition of A should be equal to M
|
||||
CPPUNIT_ASSERT(result == 0);
|
||||
CPPUNIT_ASSERT(equal(L, M));
|
||||
CPPUNIT_ASSERT(equal(A, L * L.transpose()));
|
||||
}
|
||||
|
||||
void TestCholeskyDecomposition::testPositiveSemiDefiniteMatrix()
|
||||
{
|
||||
const btMatrix3x3 M(3,0,0,1,0,0,3,2,1);
|
||||
const btMatrix3x3 A = M * M.transpose();
|
||||
const int result = choleskyDecompose(A, L);
|
||||
|
||||
// The matrix is semi definite, i.e. one of the eigenvalues is zero, so the
|
||||
// Cholesky decomposition does not exist.
|
||||
CPPUNIT_ASSERT(result != 0);
|
||||
}
|
||||
|
||||
void TestCholeskyDecomposition::testNegativeDefiniteMatrix()
|
||||
{
|
||||
const btMatrix3x3 M(3,0,0,1,2,0,3,2,1);
|
||||
const btMatrix3x3 A = M * M.transpose() * (-1.0);
|
||||
const int result = choleskyDecompose(A, L);
|
||||
|
||||
// The matrix is negative definite, i.e. all of the eigenvalues are negative,
|
||||
// so the Cholesky decomposition does not exist.
|
||||
CPPUNIT_ASSERT(result != 0);
|
||||
}
|
||||
|
||||
bool TestCholeskyDecomposition::equal(const btMatrix3x3& A, const btMatrix3x3& B) const
|
||||
{
|
||||
for (unsigned int i = 0; i < 3; ++i)
|
||||
for (unsigned int j = 0; j < 3; ++j)
|
||||
if (btFabs(A[i][j] - B[i][j]) > SIMD_EPSILON)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
52
UnitTests/BulletUnitTests/TestCholeskyDecomposition.h
Normal file
52
UnitTests/BulletUnitTests/TestCholeskyDecomposition.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef TESTCHOLESKYDECOMPOSITION_H
|
||||
#define TESTCHOLESKYDECOMPOSITION_H
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <LinearMath/btMatrix3x3.h>
|
||||
|
||||
class TestCholeskyDecomposition : public CppUnit::TestFixture
|
||||
{
|
||||
public:
|
||||
|
||||
void setUp()
|
||||
{
|
||||
I.setIdentity();
|
||||
}
|
||||
|
||||
void tearDown()
|
||||
{
|
||||
}
|
||||
|
||||
void testZeroMatrix();
|
||||
void testIdentityMatrix();
|
||||
void testPositiveDefiniteMatrix();
|
||||
void testPositiveSemiDefiniteMatrix();
|
||||
void testNegativeDefiniteMatrix();
|
||||
|
||||
CPPUNIT_TEST_SUITE(TestCholeskyDecomposition);
|
||||
CPPUNIT_TEST(testZeroMatrix);
|
||||
CPPUNIT_TEST(testIdentityMatrix);
|
||||
CPPUNIT_TEST(testPositiveDefiniteMatrix);
|
||||
CPPUNIT_TEST(testPositiveSemiDefiniteMatrix);
|
||||
CPPUNIT_TEST(testNegativeDefiniteMatrix);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns TRUE if the specified matrices are equal and FALSE otherwise.
|
||||
*
|
||||
* @param A - the first matrix to be tested.
|
||||
* @param B - the second matrix to be tested.
|
||||
*
|
||||
* @return a boolean indicating whether the specified matrix is symmetric.
|
||||
*/
|
||||
bool equal(const btMatrix3x3& A, const btMatrix3x3& B) const;
|
||||
|
||||
private:
|
||||
btMatrix3x3 L;
|
||||
btMatrix3x3 I;
|
||||
};
|
||||
|
||||
#endif // TESTCHOLESKYDECOMPOSITION_H
|
||||
|
||||
@@ -106,9 +106,9 @@ public:
|
||||
vec.safeNormalize();
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, vec.length2(), 1e-6 );
|
||||
|
||||
vec.setValue(1e-20,0,0);
|
||||
vec.normalize();
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, vec.length2(), 1e-5 );
|
||||
//vec.setValue(1e-20,0,0);
|
||||
//vec.normalize();
|
||||
//CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, vec.length2(), 1e-5 );
|
||||
|
||||
vec.setValue(1e19,0,0);
|
||||
vec.normalize();
|
||||
|
||||
103
UnitTests/BulletUnitTests/TestPolarDecomposition.cpp
Normal file
103
UnitTests/BulletUnitTests/TestPolarDecomposition.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "TestPolarDecomposition.h"
|
||||
#include "BulletSoftBody/btSoftBodyInternals.h"
|
||||
#include "LinearMath/btPolarDecomposition.h"
|
||||
#include "btCholeskyDecomposition.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const int MAX_ITERATIONS = btPolarDecomposition::DEFAULT_MAX_ITERATIONS;
|
||||
const btScalar TOLERANCE = btPolarDecomposition::DEFAULT_TOLERANCE;
|
||||
}
|
||||
|
||||
void TestPolarDecomposition::testZeroMatrix()
|
||||
{
|
||||
const btMatrix3x3 A(0,0,0,0,0,0,0,0,0);
|
||||
const int iterations = PolarDecompose(A, U, H);
|
||||
|
||||
// The decomposition should fail because the zero matrix is singular
|
||||
CPPUNIT_ASSERT(iterations == MAX_ITERATIONS);
|
||||
}
|
||||
|
||||
void TestPolarDecomposition::testSingularMatrix()
|
||||
{
|
||||
const btMatrix3x3 A(1,0,0,1,0,0,0,0,1);
|
||||
|
||||
const int iterations = PolarDecompose(A, U, H);
|
||||
|
||||
// The cdecomposition should fail because the matrix is singular.
|
||||
CPPUNIT_ASSERT(iterations == MAX_ITERATIONS);
|
||||
}
|
||||
|
||||
void TestPolarDecomposition::testPoorlyConditionedMatrix()
|
||||
{
|
||||
const btScalar e = btScalar(TOLERANCE);
|
||||
const btMatrix3x3 A(1,0,0,1,e,0,0,0,1);
|
||||
|
||||
const int iterations = PolarDecompose(A, U, H);
|
||||
|
||||
// The decomposition should succeed, however, the matrix is poorly
|
||||
// conditioned, i.e. as 'e' approaches zero, the matrix approaches a singular
|
||||
// matrix (no inverse).
|
||||
CPPUNIT_ASSERT(iterations < MAX_ITERATIONS);
|
||||
CPPUNIT_ASSERT(equal(A, U * H));
|
||||
CPPUNIT_ASSERT(isOrthogonal(U));
|
||||
CPPUNIT_ASSERT(isSymmetric(H));
|
||||
CPPUNIT_ASSERT(isPositiveDefinite(H));
|
||||
}
|
||||
|
||||
void TestPolarDecomposition::testIdentityMatrix()
|
||||
{
|
||||
const btMatrix3x3 A = I;
|
||||
const int iterations = PolarDecompose(A, U, H);
|
||||
|
||||
// The identity is a special case. The decomposition should succeed and both
|
||||
// the U and H matrices should be equal to the identity.
|
||||
CPPUNIT_ASSERT(iterations < MAX_ITERATIONS);
|
||||
CPPUNIT_ASSERT(equal(A, U * H));
|
||||
CPPUNIT_ASSERT(equal(U, I));
|
||||
CPPUNIT_ASSERT(equal(H, I));
|
||||
}
|
||||
|
||||
void TestPolarDecomposition::testNonSingularMatrix()
|
||||
{
|
||||
const btMatrix3x3 A(4, 6, 6, 9, 2, 0, 1, 6, 0);
|
||||
const int iterations = PolarDecompose(A, U, H);
|
||||
|
||||
// The decomposition should succeed so that A = U*H, where U is orthogonal and
|
||||
// H is symmetric and positive definite.
|
||||
CPPUNIT_ASSERT(iterations < MAX_ITERATIONS);
|
||||
CPPUNIT_ASSERT(equal(A, U * H));
|
||||
CPPUNIT_ASSERT(isOrthogonal(U));
|
||||
CPPUNIT_ASSERT(isSymmetric(H));
|
||||
CPPUNIT_ASSERT(isPositiveDefinite(H));
|
||||
}
|
||||
|
||||
bool TestPolarDecomposition::equal(const btMatrix3x3& A, const btMatrix3x3& B) const
|
||||
{
|
||||
for (unsigned int i = 0; i < 3; ++i)
|
||||
for (unsigned int j = 0; j < 3; ++j)
|
||||
if (btFabs(A[i][j] - B[i][j]) > TOLERANCE)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TestPolarDecomposition::isOrthogonal(const btMatrix3x3& A) const
|
||||
{
|
||||
return equal(A.transpose() * A, I);
|
||||
}
|
||||
|
||||
bool TestPolarDecomposition::isPositiveDefinite(const btMatrix3x3& A) const
|
||||
{
|
||||
static btMatrix3x3 storage;
|
||||
return (0 == choleskyDecompose(A, storage));
|
||||
}
|
||||
|
||||
bool TestPolarDecomposition::isSymmetric(const btMatrix3x3& A) const
|
||||
{
|
||||
return
|
||||
(btFabs(A[0][1] - A[1][0]) < TOLERANCE) &&
|
||||
(btFabs(A[0][2] - A[2][0]) < TOLERANCE) &&
|
||||
(btFabs(A[1][2] - A[2][1]) < TOLERANCE);
|
||||
}
|
||||
|
||||
88
UnitTests/BulletUnitTests/TestPolarDecomposition.h
Normal file
88
UnitTests/BulletUnitTests/TestPolarDecomposition.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef TESTPOLARDECOMPOSITION_H
|
||||
#define TESTPOLARDECOMPOSITION_H
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <LinearMath/btMatrix3x3.h>
|
||||
|
||||
class TestPolarDecomposition : public CppUnit::TestFixture
|
||||
{
|
||||
public:
|
||||
|
||||
void setUp()
|
||||
{
|
||||
I.setIdentity();
|
||||
}
|
||||
|
||||
void tearDown()
|
||||
{
|
||||
}
|
||||
|
||||
void testPoorlyConditionedMatrix();
|
||||
void testSingularMatrix();
|
||||
void testNonSingularMatrix();
|
||||
void testIdentityMatrix();
|
||||
void testZeroMatrix();
|
||||
|
||||
CPPUNIT_TEST_SUITE(TestPolarDecomposition);
|
||||
CPPUNIT_TEST(testZeroMatrix);
|
||||
CPPUNIT_TEST(testSingularMatrix);
|
||||
CPPUNIT_TEST(testPoorlyConditionedMatrix);
|
||||
CPPUNIT_TEST(testIdentityMatrix);
|
||||
CPPUNIT_TEST(testNonSingularMatrix);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns TRUE if the specified matrix is orthogonal and FALSE otherwise.
|
||||
* Orthogonality is checked by pre-multiplying the matrix by its transpose
|
||||
* and comparing the result to the identity matrix.
|
||||
*
|
||||
* @param A - the matrix that is being tested
|
||||
*
|
||||
* @return a boolean indicating whether the specified matrix is orthogonal.
|
||||
*/
|
||||
bool isOrthogonal(const btMatrix3x3& A) const;
|
||||
|
||||
/**
|
||||
* Returns TRUE if the specified matrix is symmetric and FALSE otherwise.
|
||||
* The symmetry of the matrix is determined by simplying testing the
|
||||
* equality of the three off-diagonal elements.
|
||||
*
|
||||
* @param A - the matrix that is being tested.
|
||||
*
|
||||
* @return a boolean indicating whether the specified matrix is symmetric.
|
||||
*/
|
||||
bool isSymmetric(const btMatrix3x3& A) const;
|
||||
|
||||
/**
|
||||
* Returns TRUE if the specified matrix is positive definite and FALSE
|
||||
* otherwise. The positive definiteness of the matrix is determined by
|
||||
* calculating the Cholesky decomposition of the matrix; If the Cholesky
|
||||
* decomposition exists, the original matrix is positive definite.
|
||||
*
|
||||
* @param A - the matrix that is being tested.
|
||||
*
|
||||
* @return a boolean indicating whether the specified matrix is positive
|
||||
* definite.
|
||||
*/
|
||||
bool isPositiveDefinite(const btMatrix3x3& A) const;
|
||||
|
||||
/**
|
||||
* Returns TRUE if the specified matrices are equal and FALSE otherwise.
|
||||
*
|
||||
* @param A - the first matrix to be tested.
|
||||
* @param B - the second matrix to be tested.
|
||||
*
|
||||
* @return a boolean indicating whether the specified matrix is symmetric.
|
||||
*/
|
||||
bool equal(const btMatrix3x3& A, const btMatrix3x3& B) const;
|
||||
|
||||
private:
|
||||
btMatrix3x3 U;
|
||||
btMatrix3x3 H;
|
||||
btMatrix3x3 I;
|
||||
};
|
||||
|
||||
#endif // TESTPOLARDECOMPOSITION_H
|
||||
|
||||
40
UnitTests/BulletUnitTests/btCholeskyDecomposition.cpp
Normal file
40
UnitTests/BulletUnitTests/btCholeskyDecomposition.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "btCholeskyDecomposition.h"
|
||||
|
||||
int choleskyDecompose(const btMatrix3x3& A, btMatrix3x3& L)
|
||||
{
|
||||
// TODO Check that the A matrix is symmetric
|
||||
|
||||
// Iterate over the elements of the upper triangle of A
|
||||
for (unsigned int i = 0; i < 3; ++i)
|
||||
{
|
||||
for (unsigned int j = i; j < 3; ++j)
|
||||
{
|
||||
btScalar sum = A[i][j];
|
||||
for (unsigned int k = 0; k < i; ++k)
|
||||
{
|
||||
sum -= L[i][k] * L[j][k];
|
||||
}
|
||||
|
||||
if (i != j)
|
||||
{
|
||||
L[j][i] = sum / L[i][i];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sum <= btScalar(0))
|
||||
{
|
||||
return btCholeskyDecomposition::FAILURE_POSITIVE_DEFINITE;
|
||||
}
|
||||
|
||||
L[i][i] = sqrt(sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
L[0][1] = btScalar(0);
|
||||
L[0][2] = btScalar(0);
|
||||
L[1][2] = btScalar(0);
|
||||
|
||||
return btCholeskyDecomposition::SUCCESS;
|
||||
}
|
||||
|
||||
19
UnitTests/BulletUnitTests/btCholeskyDecomposition.h
Normal file
19
UnitTests/BulletUnitTests/btCholeskyDecomposition.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef BTCHOLESKYDECOMPOSITION_H
|
||||
#define BTCHOLESKYDECOMPOSITION_H
|
||||
|
||||
#include "LinearMath/btMatrix3x3.h"
|
||||
|
||||
struct btCholeskyDecomposition
|
||||
{
|
||||
enum Result
|
||||
{
|
||||
SUCCESS,
|
||||
FAILURE_SYMMETRY,
|
||||
FAILURE_POSITIVE_DEFINITE
|
||||
};
|
||||
};
|
||||
|
||||
int choleskyDecompose(const btMatrix3x3& A, btMatrix3x3& L);
|
||||
|
||||
#endif // BTCHOLESKYDECOMPOSITION_H
|
||||
|
||||
Reference in New Issue
Block a user