Add SLERP test and euler2quat.

This commit is contained in:
Kyle Isom 2019-08-06 00:46:02 -07:00
parent be75f67ab8
commit be0d5f9b71
6 changed files with 137 additions and 27 deletions

View File

@ -29,11 +29,16 @@ include_directories(include)
file(GLOB_RECURSE ${PROJECT_NAME}_HEADERS include/**.h)
file(GLOB_RECURSE ${PROJECT_NAME}_SOURCES src/*.cc)
message("${PROJECT_NAME}_SOURCES -> libwrmath")
message("${${PROJECT_NAME}_SOURCES} -> libwrmath")
## BUILD
add_library(lib${PROJECT_NAME} ${${PROJECT_NAME}_SOURCES})
add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SOURCES})
add_executable(euler2quat tools/euler2quat.cc)
target_link_libraries(euler2quat ${PROJECT_NAME})
set_target_properties(${TESTNAME} PROPERTIES
FOLDER bin
RUNTIME_OUTPUT_DIRECTORY bin)
## INSTALL
@ -62,7 +67,7 @@ include(CTest)
set(TEST_EXECS)
macro(package_add_gtest TESTNAME)
add_executable(${TESTNAME} ${ARGN})
target_link_libraries(${TESTNAME} gtest_main lib${PROJECT_NAME})
target_link_libraries(${TESTNAME} gtest_main ${PROJECT_NAME})
target_compile_options(${TESTNAME} PUBLIC ${GTEST_CFLAGS})
add_test(NAME ${TESTNAME} COMMAND ${TESTNAME})
set_target_properties(${TESTNAME} PROPERTIES

View File

@ -1,7 +1,7 @@
find_package(Doxygen REQUIRED)
# Find all the public headers
get_target_property(WRMATH_PUBLIC_HEADER_DIR libwrmath INTERFACE_INCLUDE_DIRECTORIES)
get_target_property(WRMATH_PUBLIC_HEADER_DIR wrmath INTERFACE_INCLUDE_DIRECTORIES)
file(GLOB_RECURSE WRMATH_PUBLIC_HEADERS ${WRMATH_PUBLIC_HEADER_DIR}/*.h)
#This will be the main output of our command

View File

@ -7,6 +7,7 @@
#include <cassert>
#include <cmath>
#include <initializer_list>
#include <iostream>
#include <ostream>
#include <wrmath/geom/vector.h>
#include <wrmath/math.h>
@ -123,8 +124,25 @@ public:
}
/// Compute the dot product of two quaternions.
///
/// \param other Another quaternion.
/// \return The dot product between the two quaternions.
T
dot(const Quaternion<T> &other) const
{
double innerProduct = this->v[0] * other.v[0];
innerProduct += (this->v[1] * other.v[1]);
innerProduct += (this->v[2] * other.v[2]);
innerProduct += (this->w * other.w);
return innerProduct;
}
/// Compute the norm of a quaternion. Treating the Quaternion as a
/// Vector<T, 4>, it's the same as computing the magnitude.
///
/// @return A non-negative real number.
T
norm() const
@ -140,6 +158,15 @@ public:
}
/// Return the unit quaternion.
///
/// \return The unit quaternion.
Quaternion
unitQuaternion()
{
return *this / this->norm();
}
/// Compute the conjugate of a quaternion.
/// @return The conjugate of this quaternion.
Quaternion
@ -393,7 +420,11 @@ Quaterniond quaterniond_from_euler(Vector3d euler);
/// \return A Quaternion representing the linear interpolation of the
/// two quaternions.
template <typename T>
Quaternion<T> LERP(Quaternion<T> p, Quaternion<T> q, T t);
Quaternion<T>
LERP(Quaternion<T> p, Quaternion<T> q, T t)
{
return (p + (q - p) * t).unitQuaternion();
}
/// ShortestSLERP computes the shortest distance spherical linear
@ -402,13 +433,30 @@ Quaternion<T> LERP(Quaternion<T> p, Quaternion<T> q, T t);
///
/// \tparam T
/// \param p The starting quaternion.
/// \param q The ending quaternion.
/// \param q The ending quaternion.Short
/// \param t The fraction of the distance between the two quaternions
/// to interpolate.
/// \return A Quaternion representing the shortest path between two
/// quaternions.
template <typename T>
Quaternion<T> ShortestSLERP(Quaternion<T> p, Quaternion<T> q, T t);
Quaternion<T>
ShortestSLERP(Quaternion<T> p, Quaternion<T> q, T t)
{
assert(p.isUnitQuaternion());
assert(q.isUnitQuaternion());
T dp = p.dot(q);
T sign = dp < 0.0 ? -1.0 : 1.0;
T omega = std::acos(dp * sign);
T sin_omega = std::sin(omega); // Compute once.
if (dp > 0.99999) {
return LERP(p, q * sign, t);
}
return (p * std::sin((1.0 - t) * omega) / sin_omega) +
(q * sign * std::sin(omega*t) / sin_omega);
}
/// Run a quick self test to exercise basic functionality of the Quaternion

View File

@ -65,26 +65,6 @@ quaterniond_from_euler(Vector3d euler)
}
template <typename T>
Quaternion<T>
LERP(Quaternion<T> p, Quaternion<T> q, T t)
{
return p + (q - p) * t;
}
template <typename T>
Quaternion<T>
ShortestSLERP(Quaternion<T> p, Quaternion<T> q, T t)
{
T innerProduct = p.dot(q);
T sign = innerProduct >= 0.0 ? -1.0 : 1.0;
T acip = std::acos(innerProduct);
return (p * std::sin((T)1.0 - t) * acip + p * sign * std::sin(t * acip)) / std::sin(acip);
}
void
Quaternion_SelfTest()
{

View File

@ -107,6 +107,19 @@ TEST(Quaterniond, Rotate)
}
TEST(Quaterniond, ShortestSLERP)
{
geom::Quaterniond p = geom::Quaterniond {0.382683, 0, 0, 0.92388};
geom::Quaterniond q = geom::Quaterniond {-0.382683, 0, 0, 0.92388};
geom::Quaterniond r = geom::Quaterniond {0, 0, 0, 1};
EXPECT_EQ(geom::ShortestSLERP(p, q, 0.0), p);
EXPECT_EQ(geom::ShortestSLERP(p, q, 1.0), q);
EXPECT_EQ(geom::ShortestSLERP(p, q, 0.5), r);
}
TEST(Quaterniond, Unit)
{
geom::Quaterniond q(geom::Vector4d{0.5773502691896258, 0.5773502691896258, 0.5773502691896258, 0.0});

64
tools/euler2quat.cc Normal file
View File

@ -0,0 +1,64 @@
#include <cstdlib>
#include <iostream>
#include <string>
#include <wrmath/math.h>
#include <wrmath/geom/quaternion.h>
using namespace std;
using namespace wr;
static void
usage(ostream& outs)
{
outs << "Print conversions between Euler angles and quaternions." << endl;
outs << "Usage: euler2quat yaw pitch roll" << endl;
outs << " euler2quat x y z w" << endl;
}
static void
convertEulerToQuat(char **argv)
{
double yaw = math::DegreesToRadiansD(stod(string(argv[0])));
double pitch = math::DegreesToRadiansD(stod(string(argv[1])));
double roll = math::DegreesToRadiansD(stod(string(argv[2])));
geom::Vector3d euler {yaw, pitch, roll};
auto quaternion = geom::quaterniond_from_euler(euler);
cout << "Quaternion: " << quaternion.asVector() << endl;
}
static void
convertQuatToEuler(char **argv)
{
double x = stod(string(argv[0]));
double y = stod(string(argv[1]));
double z = stod(string(argv[1]));
double w = stod(string(argv[1]));
geom::Quaterniond quaternion {x, y, z, w};
auto euler = quaternion.euler() * (180.0 / M_PI);
cout << "Euler ZYX: " << euler << endl;
}
int
main(int argc, char **argv)
{
if ((argc != 4) && (argc != 5)) {
usage(cerr);
return EXIT_FAILURE;
}
argv++;
if (argc == 4) {
convertEulerToQuat(argv);
}
else {
convertQuatToEuler(argv);
}
}