From 60ebc6b3e0e1a9107a22198abdf49f04efdbfd3e Mon Sep 17 00:00:00 2001 From: Jonathan Liu Date: Sat, 21 Jan 2023 12:04:51 -0800 Subject: [PATCH 1/4] change make_vector() to makeVector() --- docs/source/basic_usage.rst | 12 ++++++------ example/basic_examples.cpp | 8 ++++---- include/simplevectors/functions.hpp | 6 +++--- test/testfunctions.cpp | 14 +++++++------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/source/basic_usage.rst b/docs/source/basic_usage.rst index e70c7d6..4625935 100644 --- a/docs/source/basic_usage.rst +++ b/docs/source/basic_usage.rst @@ -36,28 +36,28 @@ Below are examples of zero initialization and initializing with values. svector::Vector2D v2d(2, 4); // <2, 4> svector::Vector3D v3d(2, 4, 5); // <2, 4, 5> -Using ``make_vector()`` +Using ``makeVector()`` ~~~~~~~~~~~~~~~~~~~~~~~ -You can also initialize a vector in a functional manner by using the ``make_vector()`` function. This function can be used to initialize a vector from an ``std::array``, ``std::vector``, or an initializer list. Note that if you are using ``make_vector`` to initialize from a ``std::vector`` or an initializer list, then you need to specify the number of dimensions as a template argument. If you are using an initializer list, you also need to specify the type of the vector elements. +You can also initialize a vector in a functional manner by using the ``makeVector()`` function. This function can be used to initialize a vector from an ``std::array``, ``std::vector``, or an initializer list. Note that if you are using ``makeVector()`` to initialize from a ``std::vector`` or an initializer list, then you need to specify the number of dimensions as a template argument. If you are using an initializer list, you also need to specify the type of the vector elements. .. code-block:: cpp std::array an_std_array = {{1, 2, 3, 5, 2}}; svector::Vector<5> vec_from_std_array = - svector::make_vector(an_std_array); // <1, 2, 3, 5, 2> + svector::makeVector(an_std_array); // <1, 2, 3, 5, 2> std::vector an_std_vector = {1}; svector::Vector2D vec_from_std_vector = - svector::make_vector<2>(an_std_vector); // <1, 0> + svector::makeVector<2>(an_std_vector); // <1, 0> // If there are too few elements inside the std::vector, then the rest of the // dimensions for the vector will be 0. If there are too many, then the vector // truncates the dimensions. svector::Vector2D vec_from_initializer_list = - svector::make_vector<2, double>({1, 4, 5}); // <1, 4> + svector::makeVector<2, double>({1, 4, 5}); // <1, 4> // If there are too few or too many elements inside the initializer list, then - // make_vector() handles it the same way as it would handle a std::vector that + // makeVector() handles it the same way as it would handle a std::vector that // has too few/many elements. Printing diff --git a/example/basic_examples.cpp b/example/basic_examples.cpp index 1435d57..d787839 100644 --- a/example/basic_examples.cpp +++ b/example/basic_examples.cpp @@ -46,19 +46,19 @@ int main() { std::array an_std_array = {{1, 2, 3, 5, 2}}; svector::Vector<5> vec_from_std_array = - svector::make_vector(an_std_array); // <1, 2, 3, 5, 2> + svector::makeVector(an_std_array); // <1, 2, 3, 5, 2> std::vector an_std_vector = {1}; svector::Vector2D vec_from_std_vector = - svector::make_vector<2>(an_std_vector); // <1, 0> + svector::makeVector<2>(an_std_vector); // <1, 0> // If there are too few elements inside the std::vector, then the rest of the // dimensions for the vector will be 0. If there are too many, then the vector // truncates the dimensions. svector::Vector2D vec_from_initializer_list = - svector::make_vector<2, double>({1, 4, 5}); // <1, 4> + svector::makeVector<2, double>({1, 4, 5}); // <1, 4> // If there are too few or too many elements inside the initializer list, then - // make_vector() handles it the same way as it would handle a std::vector that + // makeVector() handles it the same way as it would handle a std::vector that // has too few/many elements. std::cout << vec_from_std_array.toString() << std::endl; // "<1, 2, 3, 5, 2>" diff --git a/include/simplevectors/functions.hpp b/include/simplevectors/functions.hpp index 6d2a3ce..36c821a 100644 --- a/include/simplevectors/functions.hpp +++ b/include/simplevectors/functions.hpp @@ -33,7 +33,7 @@ namespace svector { * @returns A vector whose dimensions reflect the elements in the array. */ template -Vector make_vector(std::array array) { +Vector makeVector(std::array array) { Vector vec; for (std::size_t i = 0; i < D; i++) { vec[i] = array[i]; @@ -57,7 +57,7 @@ Vector make_vector(std::array array) { * @returns A vector whose dimensions reflect the elements in the std::vector. */ template -Vector make_vector(std::vector vector) { +Vector makeVector(std::vector vector) { Vector vec; for (std::size_t i = 0; i < std::min(D, vector.size()); i++) { vec[i] = vector[i]; @@ -82,7 +82,7 @@ Vector make_vector(std::vector vector) { * list. */ template -Vector make_vector(const std::initializer_list args) { +Vector makeVector(const std::initializer_list args) { Vector vec(args); return vec; } diff --git a/test/testfunctions.cpp b/test/testfunctions.cpp index 9bf1aac..859c2c6 100644 --- a/test/testfunctions.cpp +++ b/test/testfunctions.cpp @@ -21,30 +21,30 @@ TEST(MakeVectorTestUtil, MakeVectorArray) { std::array arr = {{1, 2, 3, 5, 2}}; - svector::Vector<5> vec = svector::make_vector(arr); + svector::Vector<5> vec = svector::makeVector(arr); svector::Vector<5> control = {1, 2, 3, 5, 2}; EXPECT_EQ(vec, control); std::array arr2 = {{1, 5}}; - svector::Vector2D vec2 = svector::make_vector(arr2); + svector::Vector2D vec2 = svector::makeVector(arr2); svector::Vector2D control2 = {1, 5}; EXPECT_EQ(vec2, control2); } TEST(MakeVectorTestUtil, MakeVectorVector) { std::vector arr = {{1, 2, 3, 5, 2}}; - svector::Vector<3> vec = svector::make_vector<3>(arr); + svector::Vector<3> vec = svector::makeVector<3>(arr); svector::Vector<3> control = {1, 2, 3}; EXPECT_EQ(vec, control); std::vector arr2 = {1}; - svector::Vector2D vec2 = svector::make_vector<2>(arr2); + svector::Vector2D vec2 = svector::makeVector<2>(arr2); svector::Vector2D control2 = {1, 0}; EXPECT_EQ(vec2, control2); } TEST(MakeVectorTestUtil, MakeVectorInitializerList) { - svector::Vector<5> v = svector::make_vector<5, double>({3, 5, 2, 3.5, 6}); + svector::Vector<5> v = svector::makeVector<5, double>({3, 5, 2, 3.5, 6}); EXPECT_EQ(v[0], 3); EXPECT_EQ(v[1], 5); EXPECT_EQ(v[2], 2); @@ -53,7 +53,7 @@ TEST(MakeVectorTestUtil, MakeVectorInitializerList) { } TEST(MakeVectorTestUtil, MakeVectorInitializerListTooFew) { - svector::Vector<5> v2 = svector::make_vector<5, double>({3, 5, 2}); + svector::Vector<5> v2 = svector::makeVector<5, double>({3, 5, 2}); EXPECT_EQ(v2[0], 3); EXPECT_EQ(v2[1], 5); EXPECT_EQ(v2[2], 2); @@ -63,7 +63,7 @@ TEST(MakeVectorTestUtil, MakeVectorInitializerListTooFew) { TEST(MakeVectorTestUtil, MakeVectorInitializerListTooMany) { svector::Vector<5> v3 = - svector::make_vector<5, double>({3, 5, 2, 3.5, 6, 39, 2, 6}); + svector::makeVector<5, double>({3, 5, 2, 3.5, 6, 39, 2, 6}); EXPECT_EQ(v3[0], 3); EXPECT_EQ(v3[1], 5); EXPECT_EQ(v3[2], 2); From 601af56485f3940597763e5dc32f12055b6c8809 Mon Sep 17 00:00:00 2001 From: Jonathan Liu Date: Sat, 21 Jan 2023 13:18:41 -0800 Subject: [PATCH 2/4] add isZero() function/method --- include/simplevectors/core/vector.hpp | 10 +++++++ include/simplevectors/core/vector3d.hpp | 3 ++ include/simplevectors/embed.h | 35 ++++++++++++++++++++-- include/simplevectors/embed.hpp | 39 ++++++++++++++++++++----- include/simplevectors/functions.hpp | 21 +++++++++++++ 5 files changed, 98 insertions(+), 10 deletions(-) diff --git a/include/simplevectors/core/vector.hpp b/include/simplevectors/core/vector.hpp index 9eacb9a..0053e6a 100644 --- a/include/simplevectors/core/vector.hpp +++ b/include/simplevectors/core/vector.hpp @@ -375,6 +375,9 @@ template class Vector { * * Finds the unit vector with the same direction angle as the current vector. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @returns A new vector representing the normalized vector. */ Vector normalize() const { return (*this) / this->magn(); } @@ -386,6 +389,13 @@ template class Vector { */ constexpr std::size_t numDimensions() const { return D; } + /** + * Determines whether the current vector is a zero vector. + * + * @returns Whether the current vector is a zero vector. + */ + bool isZero() const { return this->magn() == 0; } + /** * Gets a reference to a specific component of the vector given the dimension * number. diff --git a/include/simplevectors/core/vector3d.hpp b/include/simplevectors/core/vector3d.hpp index 581b382..a9c41f1 100644 --- a/include/simplevectors/core/vector3d.hpp +++ b/include/simplevectors/core/vector3d.hpp @@ -136,6 +136,9 @@ class Vector3D : public Vec3_ { * * @see svector::AngleDir * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @returns An angle representing the angle you specified. */ template double angle() const { diff --git a/include/simplevectors/embed.h b/include/simplevectors/embed.h index 6e2e6d9..76150e0 100644 --- a/include/simplevectors/embed.h +++ b/include/simplevectors/embed.h @@ -354,12 +354,22 @@ inline double angle(const EmbVec2D &vec) { return atan2(vec.y, vec.x); } * * Finds the unit vector with the same direction angle as the current vector. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param vec A 2D vector. * * @returns Normalized vector. */ inline EmbVec2D normalize(const EmbVec2D &vec) { return vec / magn(vec); } +/** + * Determines whether a vector is a zero vector. + * + * @returns Whether the given vector is a zero vector. + */ +inline bool isZero(const EmbVec2D &vec) { return magn(vec) == 0; } + /** * Rotates vector by a certain angle. * @@ -519,44 +529,63 @@ inline double magn(const EmbVec3D &vec) { * * Finds the unit vector with the same direction angle as the current vector. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param vec A 3D vector. * * @returns Normalized vector. */ inline EmbVec3D normalize(const EmbVec3D &vec) { return vec / magn(vec); } +/** + * Determines whether a vector is a zero vector. + * + * @returns Whether the given vector is a zero vector. + */ +inline bool isZero(const EmbVec3D &vec) { return magn(vec) == 0; } + /** * Gets α angle. * * α is the angle between the vector and the x-axis. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param vec A 3D Vector. * * @returns α. */ -inline double getAlpha(const EmbVec3D &vec) { return acos(vec.x / magn(vec)); } +inline double alpha(const EmbVec3D &vec) { return acos(vec.x / magn(vec)); } /** * Gets β angle. * * β is the angle between the vector and the y-axis. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param vec A 3D Vector. * * @returns β. */ -inline double getBeta(const EmbVec3D &vec) { return acos(vec.y / magn(vec)); } +inline double beta(const EmbVec3D &vec) { return acos(vec.y / magn(vec)); } /** * Gets γ angle. * * γ is the angle between the vector and the z-axis. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param vec A 3D Vector. * * @returns γ. */ -inline double getGamma(const EmbVec3D &vec) { return acos(vec.z / magn(vec)); } +inline double gamma(const EmbVec3D &vec) { return acos(vec.z / magn(vec)); } /** * Rotates around x-axis. diff --git a/include/simplevectors/embed.hpp b/include/simplevectors/embed.hpp index 9068f4e..4af7256 100644 --- a/include/simplevectors/embed.hpp +++ b/include/simplevectors/embed.hpp @@ -365,12 +365,22 @@ inline double angle(const Vec2D &vec) { return std::atan2(vec.y, vec.x); } * * Finds the unit vector with the same direction angle as the current vector. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param vec A 2D vector. * * @returns Normalized vector. */ inline Vec2D normalize(const Vec2D &vec) { return vec / magn(vec); } +/** + * Determines whether a vector is a zero vector. + * + * @returns Whether the given vector is a zero vector. + */ +inline bool isZero(const Vec2D &vec) { return magn(vec) == 0; } + /** * Rotates vector by a certain angle. * @@ -544,48 +554,63 @@ inline double magn(const Vec3D &vec) { * * Finds the unit vector with the same direction angle as the current vector. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param vec A 3D vector. * * @returns Normalized vector. */ inline Vec3D normalize(const Vec3D &vec) { return vec / magn(vec); } +/** + * Determines whether a vector is a zero vector. + * + * @returns Whether the given vector is a zero vector. + */ +inline bool isZero(const Vec3D &vec) { return magn(vec) == 0; } + /** * Gets α angle. * * α is the angle between the vector and the x-axis. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param vec A 3D Vector. * * @returns α. */ -inline double getAlpha(const Vec3D &vec) { - return std::acos(vec.x / magn(vec)); -} +inline double alpha(const Vec3D &vec) { return std::acos(vec.x / magn(vec)); } /** * Gets β angle. * * β is the angle between the vector and the y-axis. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param vec A 3D Vector. * * @returns β. */ -inline double getBeta(const Vec3D &vec) { return std::acos(vec.y / magn(vec)); } +inline double beta(const Vec3D &vec) { return std::acos(vec.y / magn(vec)); } /** * Gets γ angle. * * γ is the angle between the vector and the z-axis. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param vec A 3D Vector. * * @returns γ. */ -inline double getGamma(const Vec3D &vec) { - return std::acos(vec.z / magn(vec)); -} +inline double gamma(const Vec3D &vec) { return std::acos(vec.z / magn(vec)); } /** * Rotates around x-axis. diff --git a/include/simplevectors/functions.hpp b/include/simplevectors/functions.hpp index 36c821a..6716bf7 100644 --- a/include/simplevectors/functions.hpp +++ b/include/simplevectors/functions.hpp @@ -215,6 +215,9 @@ template inline T magn(const Vector &v) { * * Finds the unit vector with the same direction angle as the current vector. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param v The vector to normalize. * * @returns Normalized vector. @@ -224,6 +227,15 @@ inline Vector normalize(const Vector &v) { return v / magn(v); } +/** + * Determines whether a vector is a zero vector. + * + * @returns Whether the given vector is a zero vector. + */ +template inline bool isZero(const Vector &v) { + return magn(v) == 0; +} + /** * Gets the angle of a 2D vector in radians. * @@ -282,6 +294,9 @@ inline Vector3D cross(const Vector3D &lhs, const Vector3D &rhs) { * * α is the angle between the vector and the x-axis. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param v A 3D vector. * * @returns α @@ -293,6 +308,9 @@ inline double alpha(const Vector3D &v) { return std::acos(x(v) / magn(v)); } * * β is the angle between the vector and the y-axis. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param v A 3D vector. * * @returns β @@ -304,6 +322,9 @@ inline double beta(const Vector3D &v) { return std::acos(y(v) / magn(v)); } * * γ is the angle between the vector and the z-axis. * + * @note This method will result in undefined behavior if the vector is a zero + * vector (if the magnitude equals zero). + * * @param v A 3D vector. * * @returns γ From 1153b65d50d8434314f0d81412d6f94420e46d77 Mon Sep 17 00:00:00 2001 From: Jonathan Liu Date: Sat, 21 Jan 2023 13:18:48 -0800 Subject: [PATCH 3/4] add tests for isZero() --- test/testbase.cpp | 13 +++++++++++++ test/testembed.cpp | 14 +++++++++++--- test/testfunctions.cpp | 13 +++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/test/testbase.cpp b/test/testbase.cpp index 17b813f..88ebc9b 100644 --- a/test/testbase.cpp +++ b/test/testbase.cpp @@ -195,3 +195,16 @@ TEST(AtTestV, OutOfBounds) { svector::Vector<3> v{2, 5, 3}; EXPECT_THROW(v.at(4), std::out_of_range); } + +TEST(IsZeroTestV, IsZeroTestNonZeroDimensionVector) { + svector::Vector<3> v{2, 5, 3}; + EXPECT_TRUE(!v.isZero()); + + svector::Vector<3> v2{0, 0, 0}; + EXPECT_TRUE(v2.isZero()); +} + +TEST(IsZeroTestV, IsZeroTestZeroDimensionVector) { + svector::Vector<0> v; + EXPECT_TRUE(v.isZero()); +} diff --git a/test/testembed.cpp b/test/testembed.cpp index 5bfe8e2..8f767b0 100644 --- a/test/testembed.cpp +++ b/test/testembed.cpp @@ -538,7 +538,7 @@ TEST(EmbedXYMagnitudeAngleMatchTest3D, TestMagnitudeGivenXYZ) { TEST(EmbedXYMagnitudeAngleMatchTest3D, TestAlphaGivenXYZ) { Vec3D vector(-3, 2, -6); - double ang = getAlpha(vector); + double ang = alpha(vector); double ang_r = std::round(ang * 1000.0) / 1000.0; EXPECT_EQ(ang_r, 2.014); @@ -546,7 +546,7 @@ TEST(EmbedXYMagnitudeAngleMatchTest3D, TestAlphaGivenXYZ) { TEST(EmbedXYMagnitudeAngleMatchTest3D, TestBetaGivenXYZ) { Vec3D vector(-3, 2, -6); - double ang = getBeta(vector); + double ang = beta(vector); double ang_r = std::round(ang * 1000.0) / 1000.0; EXPECT_EQ(ang_r, 1.281); @@ -554,7 +554,7 @@ TEST(EmbedXYMagnitudeAngleMatchTest3D, TestBetaGivenXYZ) { TEST(EmbedXYMagnitudeAngleMatchTest3D, TestGammaGivenXYZ) { Vec3D vector(-3, 2, -6); - double ang = getGamma(vector); + double ang = gamma(vector); double ang_r = std::round(ang * 1000.0) / 1000.0; EXPECT_EQ(ang_r, 2.600); @@ -637,3 +637,11 @@ TEST(EmbedRotationTest3D, GammaRotation) { std::round(rotated.z * 1000) / 1000); } } + +TEST(EmbedIsZeroTest, IsZeroTestNonZeroDimensionVector) { + svector::Vec3D v{2, 5, 3}; + EXPECT_TRUE(!isZero(v)); + + svector::Vec3D v2{0, 0, 0}; + EXPECT_TRUE(isZero(v2)); +} diff --git a/test/testfunctions.cpp b/test/testfunctions.cpp index 859c2c6..2803557 100644 --- a/test/testfunctions.cpp +++ b/test/testfunctions.cpp @@ -297,3 +297,16 @@ TEST(RotationTestUtil, GammaRotation) { std::round(rotated.z() * 1000) / 1000); } } + +TEST(IsZeroTestUtil, IsZeroTestNonZeroDimensionVector) { + svector::Vector<5> v{2, 5, 3}; + EXPECT_TRUE(!svector::isZero(v)); + + svector::Vector<5> v2{0}; + EXPECT_TRUE(svector::isZero(v2)); +} + +TEST(IsZeroTestUtil, IsZeroTestZeroDimensionVector) { + svector::Vector<0> v; + EXPECT_TRUE(svector::isZero(v)); +} From 340a9e91639ac5d3f601e0178bf30591310b0418 Mon Sep 17 00:00:00 2001 From: Jonathan Liu Date: Sat, 21 Jan 2023 13:29:58 -0800 Subject: [PATCH 4/4] add docs and update examples for isZero() --- docs/source/basic_usage.rst | 11 +++++++++++ example/basic_examples.cpp | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/docs/source/basic_usage.rst b/docs/source/basic_usage.rst index 4625935..2d8f484 100644 --- a/docs/source/basic_usage.rst +++ b/docs/source/basic_usage.rst @@ -99,12 +99,18 @@ The properties are shown in the code snippet below. std::cout << v3d.angle() << std::endl; // "1.268" std::cout << v3d.angle() << std::endl; // "0.9322" std::cout << v3d.angle() << std::endl; // "0.730" + // NOTE: the angle methods will result in undefined behavior if the magnitude + // of the vector is zero. // set component values v2d.x(4); // v2d is now <4, 4> v3d.y(5); v3d.z(3); // v3d is now <2, 5, 3> + // check if a vector is a zero vector (magnitude is zero) + std::cout << (v2d.isZero() ? "true" : "false") << std::endl; // false + std::cout << (v3d.isZero() ? "true" : "false") << std::endl; // false + Note that the functional equivalent for getting the angles of a 3D vector is slightly different: .. code-block:: cpp @@ -112,6 +118,8 @@ Note that the functional equivalent for getting the angles of a 3D vector is sli std::cout << svector::alpha(v3d) << std::endl; // alpha angle std::cout << svector::beta(v3d) << std::endl; // beta angle std::cout << svector::gamma(v3d) << std::endl; // gamma angle + // NOTE: the angle methods will result in undefined behavior if the magnitude + // of the vector is zero. You can also access the x, y, and z components using the ``[]`` operator. In this case, the 0th index would correspond to the x-value, the 1st index would correspond to the y-value, and the 2nd index would correspond to the z-value. This also works on higher-dimensional vectors. There is no functional equivalent to this operator. @@ -203,6 +211,9 @@ Below shows an example of vector normalization. svector::Vector2D norm2D = unnorm2D.normalize(); // <0.6, 0.8> svector::Vector3D norm3D = unnorm3D.normalize(); // <0.424, 0.566, 0.707> + +**NOTE**: ``normalize()`` will result in undefined behavior if the magnitude of the vector is zero. + Rotation 2D ----------- diff --git a/example/basic_examples.cpp b/example/basic_examples.cpp index d787839..8e1aed7 100644 --- a/example/basic_examples.cpp +++ b/example/basic_examples.cpp @@ -90,12 +90,18 @@ int main() { std::cout << v3d.angle() << std::endl; // "1.268" std::cout << v3d.angle() << std::endl; // "0.9322" std::cout << v3d.angle() << std::endl; // "0.730" + // NOTE: the angle methods will result in undefined behavior if the magnitude + // of the vector is zero. // set component values v2d.x(4); // v2d is now <4, 4> v3d.y(5); v3d.z(3); // v3d is now <2, 5, 3> + // check if a vector is a zero vector (magnitude is zero) + std::cout << (v2d.isZero() ? "true" : "false") << std::endl; // false + std::cout << (v3d.isZero() ? "true" : "false") << std::endl; // false + std::cout << v2d.toString() << std::endl; // "<4.000, 4.000>" std::cout << v3d.toString() << std::endl; // "<2.000, 5.000, 3.000>"