From 55c84292a1186031a7ebc0713e83f8f5c7cc7592 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 29 Jan 2026 14:00:48 +0900 Subject: [PATCH 1/5] remove useless templates and use std clamp --- .../src/sofa/type/fixed_array_algorithms.h | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/Sofa/framework/Type/src/sofa/type/fixed_array_algorithms.h b/Sofa/framework/Type/src/sofa/type/fixed_array_algorithms.h index 951fb579a04..77eebe93450 100644 --- a/Sofa/framework/Type/src/sofa/type/fixed_array_algorithms.h +++ b/Sofa/framework/Type/src/sofa/type/fixed_array_algorithms.h @@ -21,31 +21,25 @@ ******************************************************************************/ #pragma once -namespace sofa::type::pairwise -{ +#include -/// @brief clamp a single value. This function should be removed when std::clamp will be available -template -const T& stdclamp( const T& v, const T& lo, const T& hi ) +namespace sofa::type::pairwise { - assert( !(hi < lo) ); - return (v < lo) ? lo : (hi < v) ? hi : v; -} /// @brief clamp all the values of a fixed_array to be within a given interval. -template -T clamp(const T& in, const TT& minValue, const TT& maxValue) +template +T clamp(const T& in, const typename T::value_type& minValue, const typename T::value_type& maxValue) { T result {}; for(typename T::size_type i=0; i < typename T::size_type(TN); ++i) { - result[i] = stdclamp(in[i], minValue, maxValue); + result[i] = std::clamp(in[i], minValue, maxValue); } return result; } /// @brief pairwise add of two fixed_array -template +template constexpr T operator+(const T& l, const T& r) { T result {}; @@ -57,7 +51,7 @@ constexpr T operator+(const T& l, const T& r) } /// @brief pairwise subtract of two fixed_array -template +template constexpr T operator-(const T& l, const T& r) { T result {}; @@ -69,7 +63,7 @@ constexpr T operator-(const T& l, const T& r) } /// @brief multiply from l the r components. -template +template T operator*(const T& r, const typename T::value_type& f) { T result {}; @@ -81,7 +75,7 @@ T operator*(const T& r, const typename T::value_type& f) } /// @brief multiply from l the r components. -template +template T operator/(const T& r, const typename T::value_type& f) { T result {}; From d1e127b0b769d58285fe8673bdf3577053b8e402 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 29 Jan 2026 14:04:00 +0900 Subject: [PATCH 2/5] use some modern std functions --- Sofa/framework/Type/src/sofa/type/Vec.h | 28 ++++++++++--------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/Sofa/framework/Type/src/sofa/type/Vec.h b/Sofa/framework/Type/src/sofa/type/Vec.h index e8b62853fc1..38769be49ef 100644 --- a/Sofa/framework/Type/src/sofa/type/Vec.h +++ b/Sofa/framework/Type/src/sofa/type/Vec.h @@ -32,6 +32,7 @@ #include #include #include +#include #define EQUALITY_THRESHOLD 1e-6 @@ -257,8 +258,7 @@ class Vec // assign one value to all elements constexpr void assign(const ValueType& value) noexcept { - for (size_type i = 0; i < N; i++) - elems[i] = value; + std::fill_n(this->elems.data(), N, value); } /// Sets every element to 0. @@ -472,10 +472,7 @@ class Vec /// Squared norm. constexpr ValueType norm2() const noexcept { - ValueType r = this->elems[0]*this->elems[0]; - for (Size i=1; ielems[i]*this->elems[i]; - return r; + return std::inner_product(elems.begin(), elems.end(), elems.begin(), ValueType{}); } /// Euclidean norm. @@ -583,10 +580,7 @@ class Vec /// sum of all elements of the vector constexpr ValueType sum() const noexcept { - ValueType sum = ValueType(0.0); - for (Size i=0; ielems[i]; - return sum; + return std::accumulate(elems.begin(), elems.end(), ValueType{}); } @@ -597,8 +591,8 @@ class Vec { if constexpr (std::is_floating_point_v) { - return std::equal(this->elems.begin(), this->elems.end(), b.elems.begin(), - [](auto x, auto y) { return std::abs(x - y) < EQUALITY_THRESHOLD; }); + constexpr auto equalTest = [](auto x, auto y) { return std::abs(x - y) < EQUALITY_THRESHOLD; }; + return std::equal(this->elems.begin(), this->elems.end(), b.elems.begin(), equalTest ); } else { @@ -680,19 +674,19 @@ class Vec return elems.end(); } - constexpr reference front() + constexpr reference front() noexcept { return elems[0]; } - constexpr const_reference front() const + constexpr const_reference front() const noexcept { return elems[0]; } - constexpr reference back() + constexpr reference back() noexcept { return elems[N - 1]; } - constexpr const_reference back() const + constexpr const_reference back() const noexcept { return elems[N - 1]; } @@ -717,7 +711,7 @@ class VecNoInit : public Vec {} constexpr VecNoInit(Vec&& v) noexcept - : Vec(v) + : Vec(std::move(v)) {} using Vec::Vec; From 8afcd7d63a9c012aef9cb0d36c33610e8674449b Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 29 Jan 2026 15:12:59 +0900 Subject: [PATCH 3/5] add unit tests for the whole vec class --- Sofa/framework/Type/test/VecTypes_test.cpp | 414 ++++++++++++++++++++- 1 file changed, 413 insertions(+), 1 deletion(-) diff --git a/Sofa/framework/Type/test/VecTypes_test.cpp b/Sofa/framework/Type/test/VecTypes_test.cpp index a9ef11aaa9f..9f9b97b0d3d 100644 --- a/Sofa/framework/Type/test/VecTypes_test.cpp +++ b/Sofa/framework/Type/test/VecTypes_test.cpp @@ -124,7 +124,7 @@ TEST(VecTest, toVecN) constexpr IDontKnowWhatVecItIs autotypevec_ref {1u, 2u, 3u, 4u, 5u, 0u, 0u}; EXPECT_TRUE(autotypevec == autotypevec_ref); - + // test toVec3 constexpr sofa::type::Vec3 vec3r = sofa::type::toVec3(vec5i); constexpr sofa::type::Vec3 vec3r_ref {1.0_sreal, 2.0_sreal, 3.0_sreal}; @@ -137,3 +137,415 @@ TEST(VecTest, toVecN) EXPECT_TRUE(vec9ld == vec9ld_ref); } + +TEST(VecTest, NOINITConstructor) +{ + sofa::type::Vec<3, double> v(sofa::type::NOINIT); + [[maybe_unused]] auto x = v[0]; + EXPECT_NO_THROW(v[1] = 5.0); +} + +TEST(VecTest, singleElementConstructor) +{ + sofa::type::Vec<1, double> v(5.0); + EXPECT_EQ(v[0], 5.0); +} + +TEST(VecTest, multiElementConstructor) +{ + sofa::type::Vec<3, double> v(1.0, 2.0, 3.0); + EXPECT_EQ(v[0], 1.0); + EXPECT_EQ(v[1], 2.0); + EXPECT_EQ(v[2], 3.0); +} + +TEST(VecTest, copyConstructor) +{ + sofa::type::Vec<3, double> v1(1.0, 2.0, 3.0); + sofa::type::Vec<3, double> v2(v1); + EXPECT_EQ(v2[0], 1.0); + EXPECT_EQ(v2[1], 2.0); + EXPECT_EQ(v2[2], 3.0); +} + +TEST(VecTest, moveConstructor) +{ + sofa::type::Vec<3, double> v1(1.0, 2.0, 3.0); + sofa::type::Vec<3, double> v2(std::move(v1)); + EXPECT_EQ(v2[0], 1.0); + EXPECT_EQ(v2[1], 2.0); + EXPECT_NEAR(v2[2], 3.0, 1e-6); +} + +TEST(VecTest, constructorFromFixedArray) +{ + sofa::type::fixed_array arr = {1.0, 2.0, 3.0}; + sofa::type::Vec<3, double> v(arr); + EXPECT_EQ(v[0], 1.0); + EXPECT_EQ(v[1], 2.0); + EXPECT_EQ(v[2], 3.0); +} + +TEST(VecTest, constructorFromDifferentSize) +{ + sofa::type::Vec<3, double> v1(1.0, 2.0, 3.0); + sofa::type::Vec<5, double> v2(v1); + EXPECT_EQ(v2[0], 1.0); + EXPECT_EQ(v2[1], 2.0); + EXPECT_EQ(v2[2], 3.0); + EXPECT_EQ(v2[3], 0.0); + EXPECT_EQ(v2[4], 0.0); +} + +TEST(VecTest, setMethod) +{ + sofa::type::Vec<3, double> v; + v.set(1.0, 2.0, 3.0); + EXPECT_EQ(v[0], 1.0); + EXPECT_EQ(v[1], 2.0); + EXPECT_EQ(v[2], 3.0); +} + +TEST(VecTest, setFromOtherVec) +{ + sofa::type::Vec<3, double> v1(1.0, 2.0, 3.0); + sofa::type::Vec<5, double> v; + v.set(v1); + EXPECT_EQ(v[0], 1.0); + EXPECT_EQ(v[1], 2.0); + EXPECT_EQ(v[2], 3.0); +} + +TEST(VecTest, elementAccessOperatorParen) +{ + sofa::type::Vec<3, double> v(1.0, 2.0, 3.0); + EXPECT_EQ(v(0), 1.0); + EXPECT_EQ(v(1), 2.0); + EXPECT_EQ(v(2), 3.0); +} + +TEST(VecTest, accessorsXYZW) +{ + sofa::type::Vec<4, double> v(1.0, 2.0, 3.0, 4.0); + EXPECT_EQ(v.x(), 1.0); + EXPECT_EQ(v.y(), 2.0); + EXPECT_EQ(v.z(), 3.0); + EXPECT_EQ(v.w(), 4.0); + + const sofa::type::Vec<4, double> cv(1.0, 2.0, 3.0, 4.0); + EXPECT_EQ(cv.x(), 1.0); + EXPECT_EQ(cv.y(), 2.0); + EXPECT_EQ(cv.z(), 3.0); + EXPECT_EQ(cv.w(), 4.0); +} + +TEST(VecTest, pointerAccess) +{ + sofa::type::Vec<3, double> v(1.0, 2.0, 3.0); + const double* p = v.ptr(); + EXPECT_EQ(p[0], 1.0); + EXPECT_EQ(p[1], 2.0); + EXPECT_EQ(p[2], 3.0); + + double* np = v.ptr(); + np[0] = 10.0; + EXPECT_EQ(v[0], 10.0); +} + +TEST(VecTest, dataAccess) +{ + sofa::type::Vec<3, double> v(1.0, 2.0, 3.0); + const double* p = v.data(); + EXPECT_EQ(p[0], 1.0); + EXPECT_EQ(p[1], 2.0); + EXPECT_EQ(p[2], 3.0); +} + +TEST(VecTest, fill) +{ + sofa::type::Vec<3, double> v; + v.fill(5.0); + EXPECT_EQ(v[0], 5.0); + EXPECT_EQ(v[1], 5.0); + EXPECT_EQ(v[2], 5.0); +} + +TEST(VecTest, assign) +{ + sofa::type::Vec<3, double> v; + v.assign(7.0); + EXPECT_EQ(v[0], 7.0); + EXPECT_EQ(v[1], 7.0); + EXPECT_EQ(v[2], 7.0); +} + +TEST(VecTest, clear) +{ + sofa::type::Vec<3, double> v(5.0, 6.0, 7.0); + v.clear(); + EXPECT_EQ(v[0], 0.0); + EXPECT_EQ(v[1], 0.0); + EXPECT_EQ(v[2], 0.0); +} + +TEST(VecTest, addition) +{ + sofa::type::Vec<3, double> v1(1.0, 2.0, 3.0); + sofa::type::Vec<3, double> v2(4.0, 5.0, 6.0); + auto v3 = v1 + v2; + EXPECT_EQ(v3[0], 5.0); + EXPECT_EQ(v3[1], 7.0); + EXPECT_EQ(v3[2], 9.0); +} + +TEST(VecTest, additionAssignment) +{ + sofa::type::Vec<3, double> v1(1.0, 2.0, 3.0); + sofa::type::Vec<3, double> v2(4.0, 5.0, 6.0); + v1 += v2; + EXPECT_EQ(v1[0], 5.0); + EXPECT_EQ(v1[1], 7.0); + EXPECT_EQ(v1[2], 9.0); +} + +TEST(VecTest, subtraction) +{ + sofa::type::Vec<3, double> v1(5.0, 7.0, 9.0); + sofa::type::Vec<3, double> v2(4.0, 5.0, 6.0); + auto v3 = v1 - v2; + EXPECT_EQ(v3[0], 1.0); + EXPECT_EQ(v3[1], 2.0); + EXPECT_EQ(v3[2], 3.0); +} + +TEST(VecTest, subtractionAssignment) +{ + sofa::type::Vec<3, double> v1(5.0, 7.0, 9.0); + sofa::type::Vec<3, double> v2(4.0, 5.0, 6.0); + v1 -= v2; + EXPECT_EQ(v1[0], 1.0); + EXPECT_EQ(v1[1], 2.0); + EXPECT_EQ(v1[2], 3.0); +} + +TEST(VecTest, scalarMultiplication) +{ + sofa::type::Vec<3, double> v(1.0, 2.0, 3.0); + auto r = v * 2.0; + EXPECT_EQ(r[0], 2.0); + EXPECT_EQ(r[1], 4.0); + EXPECT_EQ(r[2], 6.0); +} + +TEST(VecTest, scalarMultiplicationAssignment) +{ + sofa::type::Vec<3, double> v(1.0, 2.0, 3.0); + v *= 2.0; + EXPECT_EQ(v[0], 2.0); + EXPECT_EQ(v[1], 4.0); + EXPECT_EQ(v[2], 6.0); +} + +TEST(VecTest, scalarDivision) +{ + sofa::type::Vec<3, double> v(2.0, 4.0, 6.0); + auto r = v / 2.0; + EXPECT_EQ(r[0], 1.0); + EXPECT_EQ(r[1], 2.0); + EXPECT_EQ(r[2], 3.0); +} + +TEST(VecTest, scalarDivisionAssignment) +{ + sofa::type::Vec<3, double> v(2.0, 4.0, 6.0); + v /= 2.0; + EXPECT_EQ(v[0], 1.0); + EXPECT_EQ(v[1], 2.0); + EXPECT_EQ(v[2], 3.0); +} + +TEST(VecTest, dotProduct) +{ + sofa::type::Vec<3, double> v1(1.0, 2.0, 3.0); + sofa::type::Vec<3, double> v2(4.0, 5.0, 6.0); + EXPECT_EQ(v1 * v2, 32.0); +} + +TEST(VecTest, linearProduct) +{ + sofa::type::Vec<3, double> v1(2.0, 4.0, 6.0); + sofa::type::Vec<3, double> v2(3.0, 5.0, 7.0); + auto r = v1.linearProduct(v2); + EXPECT_EQ(r[0], 6.0); + EXPECT_EQ(r[1], 20.0); + EXPECT_EQ(r[2], 42.0); +} + +TEST(VecTest, linearDivision) +{ + sofa::type::Vec<3, double> v1(6.0, 10.0, 14.0); + sofa::type::Vec<3, double> v2(2.0, 5.0, 7.0); + auto r = v1.linearDivision(v2); + EXPECT_EQ(r[0], 3.0); + EXPECT_EQ(r[1], 2.0); + EXPECT_EQ(r[2], 2.0); +} + +TEST(VecTest, norm2) +{ + sofa::type::Vec<3, double> v(3.0, 4.0, 0.0); + EXPECT_EQ(v.norm2(), 25.0); +} + +TEST(VecTest, norm) +{ + sofa::type::Vec<3, double> v(3.0, 4.0, 0.0); + EXPECT_NEAR(v.norm(), 5.0, 1e-10); +} + +TEST(VecTest, normalize) +{ + sofa::type::Vec<3, double> v(3.0, 4.0, 0.0); + bool result = v.normalize(); + EXPECT_TRUE(result); + EXPECT_NEAR(v[0], 0.6, 1e-6); + EXPECT_NEAR(v[1], 0.8, 1e-6); + EXPECT_NEAR(v.norm(), 1.0, 1e-10); +} + +TEST(VecTest, normalizeTooSmall) +{ + sofa::type::Vec<3, double> v1(1e-6, 2e-6, 3e-6); + sofa::type::Vec<3, double> v2 = v1; + bool result = v1.normalize(); // threshold is really too small by default + EXPECT_TRUE(result); + + bool result2 = v2.normalize(1e-5); // more reasonable threshold + EXPECT_FALSE(result2); +} + +TEST(VecTest, normalized) +{ + sofa::type::Vec<3, double> v(3.0, 4.0, 0.0); + auto n = v.normalized(); + EXPECT_NEAR(n[0], 0.6, 1e-6); + EXPECT_NEAR(n[1], 0.8, 1e-6); +} + +TEST(VecTest, isNormalized) +{ + sofa::type::Vec<3, double> v(1.0, 0.0, 0.0); + EXPECT_TRUE(v.isNormalized()); + + sofa::type::Vec<3, double> v2(2.0, 0.0, 0.0); + EXPECT_FALSE(v2.isNormalized()); +} + +TEST(VecTest, normalizeWithFailsafe) +{ + sofa::type::Vec<3, double> v1(1e-8, 2e-8, 3e-8); + sofa::type::Vec<3, double> v2 = v1; + sofa::type::Vec<3, double> failsafe(7.0, 8.0, 9.0); + v1.normalize(failsafe); // threshold is really too small by default + EXPECT_NE(v1[0], 7.0); + EXPECT_NE(v1[1], 8.0); + EXPECT_NE(v1[2], 9.0); + + v2.normalize(failsafe, 1e-5); // more reasonable threshold + EXPECT_EQ(v2[0], 7.0); + EXPECT_EQ(v2[1], 8.0); + EXPECT_EQ(v2[2], 9.0); +} + +TEST(VecTest, crossProduct) +{ + sofa::type::Vec<3, double> a(1.0, 0.0, 0.0); + sofa::type::Vec<3, double> b(0.0, 1.0, 0.0); + auto c = sofa::type::cross(a, b); + EXPECT_EQ(c[0], 0.0); + EXPECT_EQ(c[1], 0.0); + EXPECT_EQ(c[2], 1.0); +} + +TEST(VecTest, sum) +{ + sofa::type::Vec<3, double> v(1.0, 2.0, 3.0); + EXPECT_EQ(v.sum(), 6.0); +} + +TEST(VecTest, equalityWithThreshold) +{ + sofa::type::Vec<3, double> v1(1.0000001, 2.0000001, 3.0000001); + sofa::type::Vec<3, double> v2(1.0, 2.0, 3.0); + EXPECT_TRUE(v1 == v2); +} + +TEST(VecTest, inequalityWithThreshold) +{ + sofa::type::Vec<3, double> v1(1.1, 2.1, 3.1); + sofa::type::Vec<3, double> v2(1.0, 2.0, 3.0); + EXPECT_TRUE(v1 != v2); +} + +TEST(VecTest, iterators) +{ + sofa::type::Vec<3, double> v(1.0, 2.0, 3.0); + auto it = v.begin(); + EXPECT_EQ(*it, 1.0); + ++it; + EXPECT_EQ(*it, 2.0); + ++it; + EXPECT_EQ(*it, 3.0); +} + +TEST(VecTest, constIterators) +{ + const sofa::type::Vec<3, double> v(1.0, 2.0, 3.0); + auto it = v.begin(); + EXPECT_EQ(*it, 1.0); + ++it; + EXPECT_EQ(*it, 2.0); + ++it; + EXPECT_EQ(*it, 3.0); +} + +TEST(VecTest, front) +{ + sofa::type::Vec<3, double> v(5.0, 6.0, 7.0); + EXPECT_EQ(v.front(), 5.0); +} + +TEST(VecTest, back) +{ + sofa::type::Vec<3, double> v(5.0, 6.0, 7.0); + EXPECT_EQ(v.back(), 7.0); +} + +TEST(VecTest, getSubVector) +{ + sofa::type::Vec<5, double> v(1.0, 2.0, 3.0, 4.0, 5.0); + sofa::type::Vec<2, double> sub; + v.getsub(1, sub); + EXPECT_EQ(sub[0], 2.0); + EXPECT_EQ(sub[1], 3.0); +} + +TEST(VecTest, getSubScalar) +{ + sofa::type::Vec<3, double> v(5.0, 6.0, 7.0); + double scalar; + v.getsub(1, scalar); + EXPECT_EQ(scalar, 6.0); +} + +TEST(VecTest, staticSize) +{ + constexpr sofa::Size s = sofa::type::Vec<3, double>::static_size; + EXPECT_EQ(s, 3u); +} + +TEST(VecTest, sizeMethod) +{ + constexpr sofa::Size s = sofa::type::Vec<5, double>::size(); + EXPECT_EQ(s, 5); +} From ac384715d834dfe8af14e1240e80b4097beb915b Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Fri, 30 Jan 2026 13:35:56 +0900 Subject: [PATCH 4/5] Update Sofa/framework/Type/test/VecTypes_test.cpp Co-authored-by: Themis Skamagkis <70031729+th-skam@users.noreply.github.com> --- Sofa/framework/Type/test/VecTypes_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sofa/framework/Type/test/VecTypes_test.cpp b/Sofa/framework/Type/test/VecTypes_test.cpp index 9f9b97b0d3d..1049a6abb42 100644 --- a/Sofa/framework/Type/test/VecTypes_test.cpp +++ b/Sofa/framework/Type/test/VecTypes_test.cpp @@ -174,7 +174,7 @@ TEST(VecTest, moveConstructor) sofa::type::Vec<3, double> v2(std::move(v1)); EXPECT_EQ(v2[0], 1.0); EXPECT_EQ(v2[1], 2.0); - EXPECT_NEAR(v2[2], 3.0, 1e-6); + EXPECT_EQ(v2[2], 3.0); } TEST(VecTest, constructorFromFixedArray) From 1975d342ba6c4a4285f8fd1381e39fa9c01353c3 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Fri, 6 Feb 2026 13:14:59 +0900 Subject: [PATCH 5/5] revert back with explicit loops, and comments --- Sofa/framework/Type/src/sofa/type/Vec.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Sofa/framework/Type/src/sofa/type/Vec.h b/Sofa/framework/Type/src/sofa/type/Vec.h index 38769be49ef..db5f39260cb 100644 --- a/Sofa/framework/Type/src/sofa/type/Vec.h +++ b/Sofa/framework/Type/src/sofa/type/Vec.h @@ -472,7 +472,14 @@ class Vec /// Squared norm. constexpr ValueType norm2() const noexcept { - return std::inner_product(elems.begin(), elems.end(), elems.begin(), ValueType{}); + // The STL function is slower when not compiling in full optimization. + // Therefore, the debugging would be slower. + // return std::inner_product(elems.begin(), elems.end(), elems.begin(), ValueType{}); + + ValueType r = this->elems[0]*this->elems[0]; + for (Size i=1; ielems[i]*this->elems[i]; + return r; } /// Euclidean norm. @@ -580,7 +587,13 @@ class Vec /// sum of all elements of the vector constexpr ValueType sum() const noexcept { - return std::accumulate(elems.begin(), elems.end(), ValueType{}); + // The STL function is slower when not compiling in full optimization. + // Therefore, the debugging would be slower. + // return std::accumulate(elems.begin(), elems.end(), ValueType{}); + ValueType sum = static_cast(0.0); + for (Size i=0; ielems[i]; + return sum; }