diff --git a/Source/Common/Testing/imstkDataArrayTest.cpp b/Source/Common/Testing/imstkDataArrayTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..28039ffb5c62cf6c43a7c81733a5c806fb9d525a --- /dev/null +++ b/Source/Common/Testing/imstkDataArrayTest.cpp @@ -0,0 +1,173 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "gtest/gtest.h" + +#include "imstkDataArray.h" + +using namespace imstk; + +template<class T> +bool +isEqualTo(const DataArray<T>& original, std::initializer_list<T>&& p) +{ + DataArray<T> other(p); + if (original.size() != other.size()) + { + return false; + } + for (int i = 0; i < original.size(); ++i) + { + if (original[i] != other[i]) + { + return false; + } + } + return true; +} + +TEST(imstkDataArrayTest, Constructors) +{ + EXPECT_NO_THROW(DataArray<int> sample); + + DataArray<int> a; + EXPECT_EQ(0, a.size()); + EXPECT_EQ(1, a.getCapacity()); + + DataArray<int> b{ 0, 1, 2, 3 }; + EXPECT_EQ(4, b.size()); + EXPECT_EQ(4, b.getCapacity()); + EXPECT_TRUE(isEqualTo(b, { 0, 1, 2, 3 })); + + DataArray<int> c(128); + EXPECT_EQ(128, c.size()); + EXPECT_EQ(128, c.getCapacity()); + + DataArray<int> d(std::move(b)); + EXPECT_EQ(4, d.size()); + EXPECT_EQ(4, d.getCapacity()); + EXPECT_TRUE(isEqualTo(d, { 0, 1, 2, 3 })); +} + +TEST(imstkDataArrayTest, Assignment) +{ + DataArray<int> a; + a = { 1, 2, 3, 4 }; + EXPECT_EQ(4, a.size()); + EXPECT_TRUE(isEqualTo(a, { 1, 2, 3, 4 })); + + DataArray<int> b{ 0, 2, 4, 6 }; + + b = a; + EXPECT_TRUE(isEqualTo(b, { 1, 2, 3, 4 })); +} + +TEST(imstkDataArrayTest, Mapping) +{ + std::vector<int> other{ -1, -2, -3 }; + { + DataArray<int> a{ 1, 2, 3, 4 }; + a.setData(other.data(), static_cast<int>(other.size())); + EXPECT_EQ(3, a.size()); + EXPECT_TRUE(isEqualTo(a, { -1, -2, -3 })); + EXPECT_EQ(other.data(), a.getPointer()); + + DataArray<int> b = a; + EXPECT_EQ(3, b.size()); + EXPECT_TRUE(isEqualTo(b, { -1, -2, -3 })); + EXPECT_EQ(other.data(), b.getPointer()); + } +} + +TEST(imstkDataArrayTest, CapacityManagement) +{ + { + DataArray<int> a; + EXPECT_EQ(0, a.size()); + EXPECT_EQ(1, a.getCapacity()); + a.push_back(0); + EXPECT_EQ(1, a.size()); + EXPECT_EQ(1, a.getCapacity()); + for (int i = 1; i < 10; ++i) + { + a.push_back(i); + EXPECT_EQ(i + 1, a.size()); + } + EXPECT_TRUE(isEqualTo(a, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 })); + } + { + DataArray<int> a; + a.resize(100); + EXPECT_EQ(100, a.size()); + EXPECT_EQ(100, a.getCapacity()); + a.resize(50); + EXPECT_EQ(50, a.size()); + EXPECT_EQ(50, a.getCapacity()); + a.resize(0); + EXPECT_EQ(0, a.size()); + EXPECT_EQ(1, a.getCapacity()); + } + { + DataArray<int> a{ 1, 2, 3, 4 }; + a.reserve(256); + EXPECT_EQ(4, a.size()); + EXPECT_EQ(256, a.getCapacity()); + a.reserve(100); + EXPECT_EQ(256, a.getCapacity()); + EXPECT_EQ(4, a.size()); + a.squeeze(); + EXPECT_EQ(4, a.getCapacity()); + EXPECT_EQ(4, a.size()); + } +} + +TEST(imstkDataArrayTest, Iterators) +{ + DataArray<int> a; + for (const auto& val : a) + { + GTEST_FAIL() << "Should not enter here"; + } + + auto itBegin = a.begin(); + auto itEnd = a.end(); + EXPECT_EQ(itBegin, itEnd); + a = { 1, 2, 3, 4 }; + auto it = a.begin(); + itEnd = a.end(); + int expected = 1; + while (it != itEnd) + { + EXPECT_EQ(expected, *it); + ++it; + ++expected; + } +} + +int +imstkDataArrayTest(int argc, char* argv[]) +{ + // Init Google Test + ::testing::InitGoogleTest(&argc, argv); + + // Run tests with gtest + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/Source/Common/Testing/imstkVecDataArrayTest.cpp b/Source/Common/Testing/imstkVecDataArrayTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba654d22f9203a041cc0015601745ad286146c01 --- /dev/null +++ b/Source/Common/Testing/imstkVecDataArrayTest.cpp @@ -0,0 +1,215 @@ +/*========================================================================= + + Library: iMSTK + + Copyright (c) Kitware, Inc. & Center for Modeling, Simulation, + & Imaging in Medicine, Rensselaer Polytechnic Institute. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#include "gtest/gtest.h" + +#include "imstkDataArray.h" +#include "imstkVecDataArray.h" + +using namespace imstk; + +namespace +{ +template<class T, int N> +bool +isEqualTo(const VecDataArray<T, N>& original, std::initializer_list<Eigen::Matrix<T, N, 1>>&& p) +{ + VecDataArray<T, N> other(p); + if (original.size() != other.size()) + { + return false; + } + for (int i = 0; i < original.size(); ++i) + { + if (original[i] != other[i]) + { + return false; + } + } + return true; +} +} + +TEST(imstkVecDataArrayTest, Constructors) +{ + using SampleType = VecDataArray<int, 2>; + EXPECT_NO_THROW(SampleType sample); + + // Note, size and capacity return the underlying total size e.g. for + // VecDataArray<int, 2>, 2 * n + VecDataArray<int, 2> a; + EXPECT_EQ(0, a.size()); + EXPECT_EQ(2, a.getCapacity()); + EXPECT_EQ(2, a.getNumberOfComponents()); + + VecDataArray<int, 2> b{ imstk::Vec2i{ 0, 1 }, { 2, 3 } }; + EXPECT_EQ(2, b.size()); + EXPECT_EQ(4, b.getCapacity()); + EXPECT_TRUE(isEqualTo(b, { imstk::Vec2i{ 0, 1, }, { 2, 3 } })); + + VecDataArray<int, 2> c(128); + EXPECT_EQ(128, c.size()); + EXPECT_EQ(128 * 2, c.getCapacity()); + + auto ptr = b.getPointer(); + VecDataArray<int, 2> d(std::move(b)); + EXPECT_EQ(2, d.size()); + EXPECT_EQ(4, d.getCapacity()); + EXPECT_TRUE(isEqualTo(d, { imstk::Vec2i{ 0, 1, }, { 2, 3 } })); + EXPECT_EQ(ptr, d.getPointer()); +} + +TEST(imstkVecDataArrayTest, Assignment) +{ + VecDataArray<int, 2> a; + a = { imstk::Vec2i{ 1, 2, }, { 3, 4 }, { 5, 6 } }; + EXPECT_EQ(3, a.size()); + EXPECT_EQ(3 * 2, a.getCapacity()); + EXPECT_TRUE(isEqualTo(a, { imstk::Vec2i{ 1, 2, }, { 3, 4 }, { 5, 6 } })); + + VecDataArray<int, 2> b{ imstk::Vec2i{ 0, 2, }, { 4, 6 } }; + + b = a; + EXPECT_TRUE(isEqualTo(b, { imstk::Vec2i{ 1, 2, }, { 3, 4 }, { 5, 6 } })); + EXPECT_EQ(a.size(), b.size()); + EXPECT_EQ(a.getCapacity(), b.getCapacity()); + EXPECT_NE(a.getPointer(), b.getPointer()); +} + +TEST(imstkVecDataArrayTest, Mapping) +{ + std::vector<imstk::Vec2i> other{ { -1, -2, }, { -3, -4 } }; + { + VecDataArray<int, 2> a{ imstk::Vec2i{ 1, 2, } }; + a.setData(other.data(), static_cast<int>(other.size())); + EXPECT_EQ(2, a.size()); + EXPECT_EQ(2 * 2, a.getCapacity()); + EXPECT_TRUE(isEqualTo(a, { imstk::Vec2i{ -1, -2, }, { -3, -4 } })); + EXPECT_EQ(other.data(), a.getPointer()); + + VecDataArray<int, 2> b = a; + EXPECT_EQ(2, b.size()); + EXPECT_TRUE(isEqualTo(b, { imstk::Vec2i{ -1, -2, }, { -3, -4 } })); + EXPECT_EQ(other.data(), b.getPointer()); + } +} + +TEST(imstkVecDataArrayTest, CapacityManagement) +{ + { + VecDataArray<int, 2> a; + EXPECT_EQ(0, a.size()); + EXPECT_EQ(2, a.getCapacity()); + a.push_back({ 0, 0 }); + EXPECT_EQ(1, a.size()); + EXPECT_EQ(2, a.getCapacity()); + for (int i = 1; i < 10; ++i) + { + a.push_back({ i, i }); + EXPECT_EQ(i + 1, a.size()); + } + EXPECT_TRUE(isEqualTo(a, { { 0, 0 }, { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 8, 8 }, { 9, 9 } })); + } + { + VecDataArray<int, 2> a; + a.resize(100); + EXPECT_EQ(100, a.size()); + EXPECT_EQ(100 * 2, a.getCapacity()); + a.resize(50); + EXPECT_EQ(50, a.size()); + EXPECT_EQ(50 * 2, a.getCapacity()); + a.resize(0); + EXPECT_EQ(0, a.size()); + EXPECT_EQ(1 * 2, a.getCapacity()); + } + { + VecDataArray<int, 2> a{ imstk::Vec2i{ 0, 2, }, { 4, 6 } }; + a.reserve(256); + EXPECT_EQ(2, a.size()); + EXPECT_EQ(256 * 2, a.getCapacity()); + a.reserve(100); + EXPECT_EQ(2, a.size()); + EXPECT_EQ(256 * 2, a.getCapacity()); + a.squeeze(); + EXPECT_EQ(2, a.size()); + EXPECT_EQ(2 * 2, a.getCapacity()); + } +} + +TEST(imstkVecDataArrayTest, Iterators) +{ + VecDataArray<int, 2> a; + for (const auto& val : a) + { + GTEST_FAIL() << "Should not enter here"; + } + + auto itBegin = a.begin(); + auto itEnd = a.end(); + EXPECT_EQ(itBegin, itEnd); + a = { imstk::Vec2i{ 1, 1 }, { 2, 2 }, { 3, 3 } }; + auto it = a.begin(); + itEnd = a.end(); + int expected = 1; + while (it != itEnd) + { + EXPECT_EQ(imstk::Vec2i(expected, expected), *it); + ++it; + ++expected; + } +} + +TEST(imstkVecDataArrayTest, ConstructorCast) +{ + VecDataArray<long, 2> a{ Vec2i{ 1, 2 }, { 3, 4 } }; + EXPECT_EQ(Vec2i(1, 2), a[0].cast<int>()); +} + +TEST(imstkVecDataArrayTest, Erase) +{ + VecDataArray<int, 2> a{ imstk::Vec2i{ 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 } }; + a.erase(1); + EXPECT_EQ(3, a.size()); + EXPECT_EQ(8, a.getCapacity()); + EXPECT_TRUE(isEqualTo(a, { imstk::Vec2i{ 1, 1, }, { 3, 3 }, { 4, 4 } })); + EXPECT_EQ(8, a.getCapacity()); + a.erase(-1); + EXPECT_EQ(3, a.size()); + a.erase(20); + EXPECT_EQ(3, a.size()); + a.erase(1); + a.erase(0); + EXPECT_TRUE(isEqualTo(a, { imstk::Vec2i{ 4, 4, } })); + EXPECT_EQ(8, a.getCapacity()); + a.erase(0); + EXPECT_EQ(0, a.size()); + EXPECT_EQ(2, a.getCapacity()); +} + +int +imstkVecDataArrayTest(int argc, char* argv[]) +{ + // Init Google Test + ::testing::InitGoogleTest(&argc, argv); + + // Run tests with gtest + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/Source/Common/imstkDataArray.h b/Source/Common/imstkDataArray.h index 7f23e405bc45184f3b27c7c1af02cfae097d405c..f9f1850788e28f5a412cebcb50d4504341117785 100644 --- a/Source/Common/imstkDataArray.h +++ b/Source/Common/imstkDataArray.h @@ -68,9 +68,9 @@ public: pointer operator->() { return ptr_; } - bool operator==(const self_type& rhs) { return ptr_ == rhs.ptr_; } + bool operator==(const self_type& rhs) const { return ptr_ == rhs.ptr_; } - bool operator!=(const self_type& rhs) { return ptr_ != rhs.ptr_; } + bool operator!=(const self_type& rhs) const { return ptr_ != rhs.ptr_; } private: pointer ptr_; @@ -102,9 +102,9 @@ public: const pointer operator->() { return ptr_; } - bool operator==(const self_type& rhs) { return ptr_ == rhs.ptr_; } + bool operator==(const self_type& rhs) const { return ptr_ == rhs.ptr_; } - bool operator!=(const self_type& rhs) { return ptr_ != rhs.ptr_; } + bool operator!=(const self_type& rhs) const { return ptr_ != rhs.ptr_; } private: pointer ptr_; @@ -133,7 +133,7 @@ public: /// \brief Constructs from intializer list /// template<typename U> - DataArray(std::initializer_list<U> list) : AbstractDataArray(size), m_mapped(false), m_data(new T[list.size()]) + DataArray(std::initializer_list<U> list) : AbstractDataArray(static_cast<int>(list.size())), m_mapped(false), m_data(new T[list.size()]) { int j = 0; for (auto i : list) @@ -163,7 +163,7 @@ public: } } - DataArray(const DataArray&& other) + DataArray(DataArray&& other) { m_mapped = other.m_mapped; m_size = other.m_size; @@ -189,8 +189,14 @@ public: inline void resize(const int size) override { // Can't resize a mapped vector - if (m_mapped || size == m_size) + if (m_mapped) + { + return; + } + + if (size == m_capacity) { + m_size = m_capacity; return; } @@ -242,10 +248,9 @@ public: } const int newSize = m_size + 1; - if (newSize > m_capacity) // If the new size exceeds capacity + if (newSize > m_capacity) // If the new size exceeds capacity { - m_capacity *= 2; - resize(m_capacity); // Conservative/copies values + resize(m_capacity * 2); // Conservative/copies values } m_size = newSize; m_data[newSize - 1] = val; @@ -260,10 +265,9 @@ public: } const int newSize = m_size + 1; - if (newSize > m_capacity) // If the new size exceeds capacity + if (newSize > m_capacity) // If the new size exceeds capacity { - m_capacity *= 2; - resize(m_capacity); // Conservative/copies values + resize(m_capacity * 2); // Conservative/copies values } m_size = newSize; m_data[newSize - 1] = val; @@ -280,15 +284,17 @@ public: /// /// \brief Allocates extra capacity, for the number of values, conservative reallocate /// - inline void reserve(const int size) override + inline void reserve(const int capacity) override { if (m_mapped) { return; } + if (capacity <= m_capacity) { return; } + const int currSize = m_size; - resize(size); // Reallocate + resize(capacity); // Reallocate m_size = currSize; // Keep current size } diff --git a/Source/Common/imstkVecDataArray.h b/Source/Common/imstkVecDataArray.h index f321496cca8a49a38c8e9a7412e43318d25483e8..75b6c864461f2eabff5055cdcf84fe25712d2c5d 100644 --- a/Source/Common/imstkVecDataArray.h +++ b/Source/Common/imstkVecDataArray.h @@ -69,9 +69,9 @@ public: pointer operator->() { return ptr_; } - bool operator==(const self_type& rhs) { return ptr_ == rhs.ptr_; } + bool operator==(const self_type& rhs) const { return ptr_ == rhs.ptr_; } - bool operator!=(const self_type& rhs) { return ptr_ != rhs.ptr_; } + bool operator!=(const self_type& rhs) const { return ptr_ != rhs.ptr_; } private: pointer ptr_; @@ -103,9 +103,9 @@ public: const pointer operator->() { return ptr_; } - bool operator==(const self_type& rhs) { return ptr_ == rhs.ptr_; } + bool operator==(const self_type& rhs) const { return ptr_ == rhs.ptr_; } - bool operator!=(const self_type& rhs) { return ptr_ != rhs.ptr_; } + bool operator!=(const self_type& rhs) const { return ptr_ != rhs.ptr_; } private: pointer ptr_; @@ -126,20 +126,21 @@ public: /// \brief Constructs from intializer list /// template<typename U, int M> - VecDataArray(std::initializer_list<Eigen::Matrix<U, M, 1>> list) : DataArray<T>(list.size() * N), m_vecSize(list.size()), m_vecCapacity(list.size()), + VecDataArray(std::initializer_list<Eigen::Matrix<U, M, 1>> list) : DataArray<T>(static_cast<int>(list.size() * N)), + m_vecSize(static_cast<int>(list.size())), + m_vecCapacity(static_cast<int>(list.size())), m_dataCast(reinterpret_cast<VecType*>(DataArray<T>::m_data)) { int j = 0; for (auto i : list) { - m_dataCast[j] = i; + m_dataCast[j] = i.cast<T>(); j++; } } VecDataArray(const VecDataArray& other) { - // Copy the buffer instead of the pointer DataArray<T>::m_mapped = other.m_mapped; AbstractDataArray::m_size = other.m_size; AbstractDataArray::m_capacity = other.m_capacity; @@ -158,7 +159,7 @@ public: m_dataCast = reinterpret_cast<VecType*>(DataArray<T>::m_data); } - VecDataArray(const VecDataArray&& other) + VecDataArray(VecDataArray&& other) { DataArray<T>::m_mapped = other.m_mapped; AbstractDataArray::m_size = other.m_size; @@ -167,8 +168,8 @@ public: m_vecCapacity = other.m_vecCapacity; AbstractDataArray::m_scalarType = other.m_scalarType; DataArray<T>::m_data = other.m_data; // Take the others buffer - other.m_data = new T[N]; // Back to default - other.m_dataCast = reinterpret_cast<VecType>(other.m_data); + m_dataCast = other.m_dataCast; + other.m_mapped = true; } virtual ~VecDataArray() override = default; @@ -180,7 +181,7 @@ public: inline void resize(const int size) override { // Can't resize a mapped vector - if (DataArray<T>::m_mapped || size == m_vecSize) + if (DataArray<T>::m_mapped || size == m_vecCapacity) { return; } @@ -218,10 +219,9 @@ public: } const int newVecSize = m_vecSize + 1; - if (newVecSize > m_vecCapacity) // If the new size exceeds capacity + if (newVecSize > m_vecCapacity) // If the new size exceeds capacity { - m_vecCapacity *= 2; - VecDataArray::resize(m_vecCapacity); // Conservative/copies values + VecDataArray::resize(m_vecCapacity * 2); // Conservative/copies values } m_vecSize = newVecSize; AbstractDataArray::m_size = newVecSize * N; @@ -237,10 +237,9 @@ public: } const int newVecSize = m_vecSize + 1; - if (newVecSize > m_vecCapacity) // If the new size exceeds capacity + if (newVecSize > m_vecCapacity) // If the new size exceeds capacity { - m_vecCapacity *= 2; - VecDataArray::resize(m_vecCapacity); // Conservative/copies values + VecDataArray::resize(m_vecCapacity * 2); // Conservative/copies values } m_vecSize = newVecSize; AbstractDataArray::m_size = newVecSize * N; @@ -265,6 +264,8 @@ public: return; } + if (size < m_vecCapacity) { return; } + const int currVecSize = m_vecSize; const int currSize = AbstractDataArray::m_size; this->resize(size); // Resize to desired capacity @@ -293,30 +294,15 @@ public: return; } - // Allocate data, hold pointer to old data for copying - T* oldData = DataArray<T>::m_data; - DataArray<T>::m_data = new T[newSize]; - m_dataCast = reinterpret_cast<VecType*>(DataArray<T>::m_data); - const int pos = vecPos * N; // Position in type T instead of VecType - // Copy left side, unless deleting far left element - if (vecPos != 0) - { - std::copy_n(oldData, pos, DataArray<T>::m_data); - } - // Copy right side, unless deleting far right element if (vecPos != m_vecSize - 1) { - std::copy_n(oldData + pos + N, AbstractDataArray::m_size - pos - N, DataArray<T>::m_data + pos); + std::copy_n(m_data + pos + N, AbstractDataArray::m_size - pos - N, DataArray<T>::m_data + pos); } m_vecSize--; AbstractDataArray::m_size -= N; - - m_vecCapacity = m_vecSize; - AbstractDataArray::m_capacity = AbstractDataArray::m_size; - delete[] oldData; } /// diff --git a/Source/Controllers/imstkRigidObjectController.h b/Source/Controllers/imstkRigidObjectController.h index 5e984cb9f4c2077667a80d4da554ad9e024f0760..ca33943737d80aec07b62826841d946b7afc891c 100644 --- a/Source/Controllers/imstkRigidObjectController.h +++ b/Source/Controllers/imstkRigidObjectController.h @@ -105,6 +105,16 @@ public: int getSmoothingKernelSize() const { return m_smoothingKernelSize; } void setSmoothingKernelSize(const int kernelSize) { m_smoothingKernelSize = kernelSize; } + /// + /// \brief Return the currently applied force + /// + const Vec3d& getForce() const { return fS; } + + /// + /// \brief Return the currently applied torque + /// + const Vec3d& getTorque() const { return tS; } + public: /// /// \brief Update controlled scene object using latest tracking information diff --git a/Source/Rendering/VTKRenderer/RenderDelegate/imstkVTKdebugTrianglesRenderDelegate.cpp b/Source/Rendering/VTKRenderer/RenderDelegate/imstkVTKdebugTrianglesRenderDelegate.cpp index 5c7513024e990519a89489c4c3afee4ff0d490b0..ff823267ba15bd2ef965aecc4355a285572fc320 100644 --- a/Source/Rendering/VTKRenderer/RenderDelegate/imstkVTKdebugTrianglesRenderDelegate.cpp +++ b/Source/Rendering/VTKRenderer/RenderDelegate/imstkVTKdebugTrianglesRenderDelegate.cpp @@ -76,6 +76,7 @@ VTKdbgTrianglesRenderDelegate::processEvents() if (dbgTriangles->isModified()) { + dbgTriangles->setDataModified(false); m_paddedVertexArray->SetArray(dbgTriangles->getVertexBufferPtr(), dbgTriangles->getNumVertices() * 3, 1);