commit 2ecc14f46ca25332cff05644365087dadf9baa58 Author: Kyle Isom Date: Sat Aug 3 02:03:44 2019 +0000 Initial import. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8548ded --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/build/ +/cmake-build-*/ +/stage/ +/package/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..46e7077 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 3.10) +cmake_policy(SET CMP0048 NEW) + + +## CONFIG + +project(wrnav VERSION 0.0.1 LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_compile_options(-Werror -Wall -g -O0) + +if(DEFINED ENV{CMAKE_GCOV}) +add_compile_options(-fprofile-arcs -ftest-coverage) +# Need CMake 3.15+. +add_link_options(-fprofile-arcs -ftest-coverage) +endif() + +find_package(PkgConfig) +pkg_search_module(GTEST REQUIRED gtest_main) + +include_directories(include) + + +## BUILD + +# add_library(LIBNAME +# SOURCES +# ) +# +# add_executable(EXE_NAME MAIN) +# target_link_libraries(EXE_NAME LIBNAMES) +# add_dependencies(EXE_NAME LIBNAMES) + + +## INSTALL + +install(DIRECTORY include/${PROJECT_NAME} + DESTINATION include/ + FILES_MATCHING PATTERN "*.h") + + +## TEST + +# From Modern CMake: +# https://cliutils.gitlab.io/modern-cmake/chapters/testing/googletest.html +include(CTest) +set(TEST_EXECS) +macro(package_add_gtest TESTNAME) + add_executable(${TESTNAME} ${ARGN}) + target_link_libraries(${TESTNAME} ${GTEST_LDFLAGS}) + target_compile_options(${TESTNAME} PUBLIC ${GTEST_CFLAGS}) + add_test(NAME ${TESTNAME} COMMAND ${TESTNAME}) + set_target_properties(${TESTNAME} PROPERTIES + FOLDER tests + RUNTIME_OUTPUT_DIRECTORY tests) + list(APPEND TEST_EXECS ${TESTNAME}) +endmacro() + +package_add_gtest(vector_test test/vector_test.cc) + +add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --verbose DEPENDS ${TEST_EXECS}) + + +## DEPLOY + +include(CMakePack.txt) diff --git a/CMakePack.txt b/CMakePack.txt new file mode 100644 index 0000000..21d63d8 --- /dev/null +++ b/CMakePack.txt @@ -0,0 +1,18 @@ +# build a CPack driven installer package +include(InstallRequiredSystemLibraries) +set(CPACK_RESOURCE_FILE_LICENSE + "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt") +set(CPACK_PACKAGE_VERSION_MAJOR "${${PROJECT_NAME}_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${${PROJECT_NAME}_VERSION_MINOR}") + +# Debian settings + set(CPACK_DEBIAN_PACKAGE_MAINTAINER "K. Isom") + set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The Shimmering Clarity C++ library") + set(CPACK_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION}) + set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc++1 (>= 3.7.0-1)") +# set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA + set(CPACK_DEBIAN_PACKAGE_SECTION devel) + +# actually do the thing + set(CPACK_GENERATOR DEB) + include (CPack) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..a9e4ccc --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright 2019 Kyle Isom + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/include/wrnav/geom/vector.h b/include/wrnav/geom/vector.h new file mode 100644 index 0000000..8fc9c58 --- /dev/null +++ b/include/wrnav/geom/vector.h @@ -0,0 +1,180 @@ +#ifndef __WRNAV_GEOM_VECTOR_H +#define __WRNAV_GEOM_VECTOR_H + + +#include +#include +#include +#include +#include +#include + +#include + + +namespace wr { +namespace geom { + +template +class Vector { +public: + Vector() { wr::util::DefaultEpsilon(this->epsilon); } + + Vector(std::initializer_list ilst) + { + assert(ilst.size() == N); + + wr::util::DefaultEpsilon(this->epsilon); + std::copy(ilst.begin(), ilst.end(), this->arr.begin()); + } + + + T magnitude() const { + T result = 0; + + for (size_t i = 0; i < N; i++) { + result += (this->arr[i] * this->arr[i]); + } + return std::sqrt(result); + }; + + + void + setEpsilon(T epsilon) + { + this->epsilon = epsilon; + } + + + bool + isZero() const + { + for (size_t i = 0; i < N; i++) { + if (!wr::util::WithinTolerance(this->arr[i], 0.0, this->epsilon)) { + return false; + } + } + return true; + } + + + Vector + unitVector() const + { + return *this / this->magnitude(); + } + + + T + angle(const Vector &rhs) const + { + Vector unitA = this->unitVector(); + Vector unitB = rhs.unitVector(); + + return std::acos(unitA * unitB); + } + + + Vector operator+(const Vector &rhs) const { + Vector vec; + + for (size_t i = 0; i < N; i++) { + vec.arr[i] = this->arr[i] + rhs.arr[i]; + } + + return vec; + } + + + Vector operator-(const Vector &rhs) const { + Vector vec; + + for (size_t i = 0; i < N; i++) { + vec.arr[i] = this->arr[i] - rhs.arr[i]; + } + + return vec; + } + + + // Scalar multiplication. + Vector operator*(const T k) const { + Vector vec; + + for (size_t i = 0; i < N; i++) { + vec.arr[i] = this->arr[i] * k; + } + + return vec; + } + + + // Scalar division. + Vector operator/(const T k) const { + Vector vec; + + for (size_t i = 0; i < N; i++) { + vec.arr[i] = this->arr[i] / k; + } + + return vec; + } + + + // Dot product. + T operator*(const Vector &rhs) const { + T result = 0; + + for (size_t i = 0; i < N; i++) { + result += (this->arr[i] * rhs.arr[i]); + } + + return result; + } + + + bool operator==(const Vector &rhs) const { + for (size_t i = 0; iarr[i], rhs.arr[i], this->epsilon)) { + return false; + } + } + return true; + } + + + bool operator!=(const Vector &rhs) const { + return !(*this == rhs); + } + + + friend std::ostream& operator<<(std::ostream& outs, const Vector& vec) { + outs << "<"; + for (size_t i = 0; i < N; i++) { + outs << vec.arr[i]; + if (i < (N-1)) { + outs << ", "; + } + } + outs << ">"; + return outs; + } + +private: + static const size_t dim = N; + T epsilon; + std::array arr; +}; + + +typedef Vector Vector3f; +typedef Vector Vector4f; +typedef Vector Vector3d; +typedef Vector Vector4d; + + +} // namespace geom +} // namespace wr + + +#endif // __WRNAV_GEOM_VECTOR_H diff --git a/include/wrnav/util/math.h b/include/wrnav/util/math.h new file mode 100644 index 0000000..36e85f9 --- /dev/null +++ b/include/wrnav/util/math.h @@ -0,0 +1,39 @@ +#ifndef __WRNAV_UTIL_MATH_H +#define __WRNAV_UTIL_MATH_H + + +namespace wr { +namespace util { + + +const double Epsilon_double = 0.0001; +const float Epsilon_float = 0.0001; + + +void +DefaultEpsilon(double &epsilon) +{ + epsilon = Epsilon_double; +} + + +void +DefaultEpsilon(float &epsilon) +{ + epsilon = Epsilon_float; +} + + +template +T +WithinTolerance(T a, T b, T epsilon) +{ + return std::abs(a - b) < epsilon; +} + + +} // namespace util +} // namespace wr + + +#endif // __WRNAV_UTIL_MATH_H diff --git a/test/vector_test.cc b/test/vector_test.cc new file mode 100644 index 0000000..eb623cc --- /dev/null +++ b/test/vector_test.cc @@ -0,0 +1,196 @@ +#include +#include +#include + +using namespace std; +using namespace wr; + + +TEST(Vector3FloatTests, Magnitude) +{ + geom::Vector3f v3f {1.0, -2.0, 3.0}; + const float expected = 3.74165738677394; + + EXPECT_FLOAT_EQ(v3f.magnitude(), expected); +} + + +TEST(Vector3FloatTests, Equality) +{ + geom::Vector3f a {1.0, 2.0, 3.0}; + geom::Vector3f b {1.0, 2.0, 3.0}; + geom::Vector3f c {1.0, 2.0, 1.0}; + + EXPECT_EQ(a, b); + EXPECT_EQ(b, a); + EXPECT_NE(a, c); + EXPECT_NE(b, c); +} + + +TEST(Vector3FloatTests, Addition) +{ + geom::Vector3f a {1.0, 2.0, 3.0}; + geom::Vector3f b {4.0, 5.0, 6.0}; + geom::Vector3f expected {5.0, 7.0, 9.0}; + + EXPECT_EQ(a+b, expected); +} + + +TEST(Vector3FloatTests, Subtraction) +{ + geom::Vector3f a {1.0, 2.0, 3.0}; + geom::Vector3f b {4.0, 5.0, 6.0}; + geom::Vector3f c {5.0, 7.0, 9.0}; + + EXPECT_EQ(c-b, a); +} + + +TEST(Vector3FloatTests, ScalarMultiplication) +{ + geom::Vector3f a {1.0, 2.0, 3.0}; + geom::Vector3f expected {3.0, 6.0, 9.0}; + + EXPECT_EQ(a * 3.0, expected); +} + + +TEST(Vector3FloatTests, ScalarDivision) +{ + geom::Vector3f a {1.0, 2.0, 3.0}; + geom::Vector3f b {3.0, 6.0, 9.0}; + + EXPECT_EQ(b / 3.0, a); +} + + +TEST(Vector3FloatTests, DotProduct) +{ + geom::Vector3f a {1.0, 2.0, 3.0}; + geom::Vector3f b {4.0, 5.0, 6.0}; + + EXPECT_FLOAT_EQ(a * b, 32.0); +} + + +TEST(Vector3FloatTests, UnitVector) +{ + // Test values randomly generated and calculated with numpy. + geom::Vector3f vec3 {5.320264018493507, 5.6541812891273935, 1.9233435162644652}; + geom::Vector3f unit {0.6651669556972103, 0.7069150218815566, 0.24046636539587804}; + + EXPECT_EQ(vec3.unitVector(), unit); +} + + +TEST(Vector3FloatTests, Angle) +{ + geom::Vector3f a {0.3977933061361172, 8.053980094436525, 8.1287759943773}; + geom::Vector3f b {9.817895298608196, 4.034166890407462, 4.37628316513266}; + geom::Vector3f c {7.35, 0.221, 5.188}; + geom::Vector3f d {2.751, 8.259, 3.985}; + + EXPECT_FLOAT_EQ(a.angle(b), 0.9914540426033251); + EXPECT_NEAR(c.angle(d), 1.052, 0.001); +} + + +TEST(Vector3DoubleTests, Magnitude) +{ + geom::Vector3d v3d{1.0, -2.0, 3.0}; + const double expected = 3.74165738677394; + + EXPECT_DOUBLE_EQ(v3d.magnitude(), expected); +} + + +TEST(Vector3DoubleTests, Equality) +{ + geom::Vector3d a {1.0, 2.0, 3.0}; + geom::Vector3d b {1.0, 2.0, 3.0}; + geom::Vector3d c {1.0, 2.0, 1.0}; + + EXPECT_EQ(a, b); + EXPECT_EQ(b, a); + EXPECT_NE(a, c); + EXPECT_NE(b, c); +} + + +TEST(Vector3DoubleTests, Addition) +{ + geom::Vector3d a {1.0, 2.0, 3.0}; + geom::Vector3d b {4.0, 5.0, 6.0}; + geom::Vector3d expected {5.0, 7.0, 9.0}; + + EXPECT_EQ(a+b, expected); +} + + +TEST(Vector3DoubleTests, Subtraction) +{ + geom::Vector3d a {1.0, 2.0, 3.0}; + geom::Vector3d b {4.0, 5.0, 6.0}; + geom::Vector3d c {5.0, 7.0, 9.0}; + + EXPECT_EQ(c-b, a); +} + + +TEST(Vector3DoubleTests, ScalarMultiplication) +{ + geom::Vector3d a {1.0, 2.0, 3.0}; + geom::Vector3d expected {3.0, 6.0, 9.0}; + + EXPECT_EQ(a * 3.0, expected); +} + + +TEST(Vector3DoubleTests, ScalarDivision) +{ + geom::Vector3d a {1.0, 2.0, 3.0}; + geom::Vector3d b {3.0, 6.0, 9.0}; + + EXPECT_EQ(b / 3.0, a); +} + + +TEST(Vector3DoubleTests, DotProduct) +{ + geom::Vector3d a {1.0, 2.0, 3.0}; + geom::Vector3d b {4.0, 5.0, 6.0}; + + EXPECT_DOUBLE_EQ(a * b, 32.0); +} + + +TEST(Vector3DoubleTests, UnitVector) +{ + // Test values randomly generated and calculated with numpy. + geom::Vector3d vec3 {5.320264018493507, 5.6541812891273935, 1.9233435162644652}; + geom::Vector3d unit {0.6651669556972103, 0.7069150218815566, 0.24046636539587804}; + + EXPECT_EQ(vec3.unitVector(), unit); +} + + +TEST(Vector3DoubleTests, Angle) +{ + geom::Vector3d a {0.3977933061361172, 8.053980094436525, 8.1287759943773}; + geom::Vector3d b {9.817895298608196, 4.034166890407462, 4.37628316513266}; + geom::Vector3d c {7.35, 0.221, 5.188}; + geom::Vector3d d {2.751, 8.259, 3.985}; + + EXPECT_DOUBLE_EQ(a.angle(b), 0.9914540426033251); + EXPECT_NEAR(c.angle(d), 1.052, 0.001); +} + + +int +main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}