From d191cc6fdb639e0f12a9a921e365f14c4955d519 Mon Sep 17 00:00:00 2001 From: bartanakin Date: Sun, 4 May 2025 11:21:36 +0200 Subject: [PATCH] #67 --- CMakeLists.txt | 23 +-- Makefile | 10 +- bin/create_class.bash | 161 +++++++++++------- include/Application.h | 2 + .../CheckCollisionVisitorInterface.h | 2 +- .../CircleAABBCheckCollisionVisitor.h | 2 +- .../CircleCircleCheckCollisionVisitor.h | 2 +- .../OBB_AABBCheckCollisionVisitor.h | 2 +- include/Dynamics/DynamicsDTOCollection.h | 2 + .../ConstantPointMassDistributionCalculator.h | 16 ++ .../MassDistributionCalculatorInterface.h | 20 +++ .../SoftBody/SoftBodyDynamicsInterface.h | 29 ++++ .../SoftBody/StVenantKirchhoffDynamics.h | 12 ++ .../UpdateStrategy/SoftBodyStrategy.h | 22 ++- .../UpdateStrategy/UpdateStrategyManager.h | 8 +- include/Geometrics/Point.h | 15 ++ include/Geometrics/Quaternion.h | 2 +- include/Geometrics/Vector.h | 14 ++ include/Graphics/OpenGL_Bridge/Vertex.h | 15 +- include/Objects/Soft/Mesh.h | 6 +- include/Objects/Soft/MeshLoader/MshData.h | 25 --- .../Objects/Soft/MeshLoader/MshDataLoader.h | 12 -- .../Soft/MshLoader/DirichletConditionPlugin.h | 12 ++ .../Objects/Soft/MshLoader/ElementsPlugin.h | 12 ++ .../Objects/Soft/MshLoader/EntitiesPlugin.h | 12 ++ .../Objects/Soft/MshLoader/MeshFormatPlugin.h | 15 ++ include/Objects/Soft/MshLoader/MshData.h | 53 ++++++ .../Objects/Soft/MshLoader/MshDataLoader.h | 18 ++ .../MshDataLoaderInterface.h | 4 +- .../Soft/MshLoader/MshLoaderPluginInterface.h | 16 ++ include/Objects/Soft/MshLoader/NodesPlugin.h | 12 ++ .../DirichletConditionParserDecorator.h | 15 ++ .../{MeshLoader => MshParser}/MshDataParser.h | 4 +- .../MshDataParserInterface.h | 8 +- .../SurfaceFacesMshParserDecorator.h | 2 +- .../TetrahedralElementLoaderDecorator.h | 15 ++ include/Objects/Soft/Node.h | 23 ++- include/Objects/Soft/SoftObject.h | 4 + include/Objects/Soft/TetrahedralElement.h | 18 +- include/SceneLoader/SoftObjectJsonDecoder.h | 30 +++- include/Utilities/MatrixUtilities.h | 16 ++ include/pch.h | 1 + .../AABB_AABBCheckCollisionVisitor.cpp | 2 +- .../CircleAABBCheckCollisionVisitor.cpp | 4 +- .../CircleCircleCheckCollisionVisitor.cpp | 2 +- .../OBB_AABBCheckCollisionVisitor.cpp | 2 +- lib/Dynamics/CMakeLists.txt | 3 + lib/Dynamics/Mass/CMakeLists.txt | 3 + ...onstantPointMassDistributionCalculator.cpp | 59 +++++++ lib/Dynamics/SoftBody/CMakeLists.txt | 4 + .../SoftBody/SoftBodyDynamicsInterface.cpp | 25 +++ .../SoftBody/StVenantKirchhoffDynamics.cpp | 87 ++++++++++ lib/Dynamics/UpdateStrategy/CMakeLists.txt | 3 + .../UpdateStrategy/SoftBodyStrategy.cpp | 118 +++++++++++++ lib/Geometrics/Transformation.cpp | 2 +- lib/Graphics/OpenGL_Bridge.cpp | 4 +- .../OpenGL_Bridge/AttributeArrayGuard.cpp | 4 +- lib/Graphics/OpenGL_Bridge/PV_BufferGuard.cpp | 9 +- lib/Graphics/SpriteBuilder/SpriteMerger.cpp | 6 +- lib/Objects/Soft/CMakeLists.txt | 3 +- lib/Objects/Soft/Mesh.cpp | 3 +- lib/Objects/Soft/MeshLoader/MshDataLoader.cpp | 87 ---------- lib/Objects/Soft/MeshLoader/MshDataParser.cpp | 53 ------ lib/Objects/Soft/MshLoader/CMakeLists.txt | 8 + .../MshLoader/DirichletConditionPlugin.cpp | 22 +++ lib/Objects/Soft/MshLoader/ElementsPlugin.cpp | 41 +++++ lib/Objects/Soft/MshLoader/EntitiesPlugin.cpp | 41 +++++ .../Soft/MshLoader/MeshFormatPlugin.cpp | 21 +++ lib/Objects/Soft/MshLoader/MshDataLoader.cpp | 36 ++++ lib/Objects/Soft/MshLoader/NodesPlugin.cpp | 29 ++++ .../{MeshLoader => MshParser}/CMakeLists.txt | 5 +- .../DirichletConditionParserDecorator.cpp | 75 ++++++++ lib/Objects/Soft/MshParser/MshDataParser.cpp | 24 +++ .../SurfaceFacesMshParserDecorator.cpp | 14 +- .../TetrahedralElementLoaderDecoratorTest.cpp | 56 ++++++ lib/Objects/Soft/SoftObject.cpp | 31 +++- lib/Objects/Soft/TetrahedralElement.cpp | 60 +++++-- lib/Utilities/CMakeLists.txt | 1 + lib/Utilities/MatrixUtilities.cpp | 24 +++ scripts/Matrices/latex.txt | 0 .../Matrices/matrix_from_latex_to_white.py | 16 ++ .../Matrices/matrix_from_white_to_latex.py | 29 ++++ scripts/Matrices/white.txt | 0 test/CMakeLists.txt | 17 ++ test/Dynamics/CMakeLists.txt | 1 + test/Dynamics/SoftBody/CMakeLists.txt | 4 + .../SoftBody/SoftBodyDynamicsTest.cpp | 58 +++++++ .../StVenantKirchhoffDynamicsTest.cpp | 5 + test/Objects/CMakeLists.txt | 1 + test/Objects/Soft/CMakeLists.txt | 1 + test/Objects/Soft/MshLoader/CMakeLists.txt | 8 + .../DirichletConditionParserDecoratorTest.cpp | 64 +++++++ .../DirichletConditionPluginTest.cpp | 50 ++++++ .../Soft/MshLoader/ElementsPluginTest.cpp | 89 ++++++++++ .../Soft/MshLoader/EntitiesPluginTest.cpp | 85 +++++++++ .../Soft/MshLoader/MshDataParserMock.h | 11 ++ .../Soft/MshLoader/NodesPluginTest.cpp | 80 +++++++++ .../TetrahedralElementLoaderDecorator.cpp | 55 ++++++ test/Utilities/CMakeLists.txt | 3 + test/Utilities/MatrixUtilitiesTest.cpp | 51 ++++++ test/main.cpp | 0 test/run/CMakeLists.txt | 13 -- test/run/main.cpp | 12 -- 103 files changed, 1960 insertions(+), 356 deletions(-) create mode 100644 include/Dynamics/Mass/ConstantPointMassDistributionCalculator.h create mode 100644 include/Dynamics/Mass/MassDistributionCalculatorInterface.h create mode 100644 include/Dynamics/SoftBody/SoftBodyDynamicsInterface.h create mode 100644 include/Dynamics/SoftBody/StVenantKirchhoffDynamics.h delete mode 100644 include/Objects/Soft/MeshLoader/MshData.h delete mode 100644 include/Objects/Soft/MeshLoader/MshDataLoader.h create mode 100644 include/Objects/Soft/MshLoader/DirichletConditionPlugin.h create mode 100644 include/Objects/Soft/MshLoader/ElementsPlugin.h create mode 100644 include/Objects/Soft/MshLoader/EntitiesPlugin.h create mode 100644 include/Objects/Soft/MshLoader/MeshFormatPlugin.h create mode 100644 include/Objects/Soft/MshLoader/MshData.h create mode 100644 include/Objects/Soft/MshLoader/MshDataLoader.h rename include/Objects/Soft/{MeshLoader => MshLoader}/MshDataLoaderInterface.h (71%) create mode 100644 include/Objects/Soft/MshLoader/MshLoaderPluginInterface.h create mode 100644 include/Objects/Soft/MshLoader/NodesPlugin.h create mode 100644 include/Objects/Soft/MshParser/DirichletConditionParserDecorator.h rename include/Objects/Soft/{MeshLoader => MshParser}/MshDataParser.h (63%) rename include/Objects/Soft/{MeshLoader => MshParser}/MshDataParserInterface.h (51%) rename include/Objects/Soft/{MeshLoader => MshParser}/SurfaceFacesMshParserDecorator.h (88%) create mode 100644 include/Objects/Soft/MshParser/TetrahedralElementLoaderDecorator.h create mode 100644 include/Utilities/MatrixUtilities.h create mode 100644 lib/Dynamics/Mass/CMakeLists.txt create mode 100644 lib/Dynamics/Mass/ConstantPointMassDistributionCalculator.cpp create mode 100644 lib/Dynamics/SoftBody/CMakeLists.txt create mode 100644 lib/Dynamics/SoftBody/SoftBodyDynamicsInterface.cpp create mode 100644 lib/Dynamics/SoftBody/StVenantKirchhoffDynamics.cpp create mode 100644 lib/Dynamics/UpdateStrategy/CMakeLists.txt create mode 100644 lib/Dynamics/UpdateStrategy/SoftBodyStrategy.cpp delete mode 100644 lib/Objects/Soft/MeshLoader/MshDataLoader.cpp delete mode 100644 lib/Objects/Soft/MeshLoader/MshDataParser.cpp create mode 100644 lib/Objects/Soft/MshLoader/CMakeLists.txt create mode 100644 lib/Objects/Soft/MshLoader/DirichletConditionPlugin.cpp create mode 100644 lib/Objects/Soft/MshLoader/ElementsPlugin.cpp create mode 100644 lib/Objects/Soft/MshLoader/EntitiesPlugin.cpp create mode 100644 lib/Objects/Soft/MshLoader/MeshFormatPlugin.cpp create mode 100644 lib/Objects/Soft/MshLoader/MshDataLoader.cpp create mode 100644 lib/Objects/Soft/MshLoader/NodesPlugin.cpp rename lib/Objects/Soft/{MeshLoader => MshParser}/CMakeLists.txt (53%) create mode 100644 lib/Objects/Soft/MshParser/DirichletConditionParserDecorator.cpp create mode 100644 lib/Objects/Soft/MshParser/MshDataParser.cpp rename lib/Objects/Soft/{MeshLoader => MshParser}/SurfaceFacesMshParserDecorator.cpp (58%) create mode 100644 lib/Objects/Soft/MshParser/TetrahedralElementLoaderDecoratorTest.cpp create mode 100644 lib/Utilities/MatrixUtilities.cpp create mode 100644 scripts/Matrices/latex.txt create mode 100644 scripts/Matrices/matrix_from_latex_to_white.py create mode 100644 scripts/Matrices/matrix_from_white_to_latex.py create mode 100644 scripts/Matrices/white.txt create mode 100644 test/CMakeLists.txt create mode 100644 test/Dynamics/CMakeLists.txt create mode 100644 test/Dynamics/SoftBody/CMakeLists.txt create mode 100644 test/Dynamics/SoftBody/SoftBodyDynamicsTest.cpp create mode 100644 test/Dynamics/SoftBody/StVenantKirchhoffDynamicsTest.cpp create mode 100644 test/Objects/CMakeLists.txt create mode 100644 test/Objects/Soft/CMakeLists.txt create mode 100644 test/Objects/Soft/MshLoader/CMakeLists.txt create mode 100644 test/Objects/Soft/MshLoader/DirichletConditionParserDecoratorTest.cpp create mode 100644 test/Objects/Soft/MshLoader/DirichletConditionPluginTest.cpp create mode 100644 test/Objects/Soft/MshLoader/ElementsPluginTest.cpp create mode 100644 test/Objects/Soft/MshLoader/EntitiesPluginTest.cpp create mode 100644 test/Objects/Soft/MshLoader/MshDataParserMock.h create mode 100644 test/Objects/Soft/MshLoader/NodesPluginTest.cpp create mode 100644 test/Objects/Soft/MshLoader/TetrahedralElementLoaderDecorator.cpp create mode 100644 test/Utilities/CMakeLists.txt create mode 100644 test/Utilities/MatrixUtilitiesTest.cpp create mode 100644 test/main.cpp delete mode 100644 test/run/CMakeLists.txt delete mode 100644 test/run/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 22c3420..038916f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,11 @@ FetchContent_Declare(json ) FetchContent_MakeAvailable(json) +FetchContent_Declare(googletest + URL https://github.com/google/googletest/archive/refs/heads/main.zip +) +FetchContent_MakeAvailable(googletest) + add_subdirectory(lib) if (USE_OPEN_GL) @@ -53,10 +58,8 @@ if (USE_OPEN_GL) ) FetchContent_MakeAvailable(glad) - link_libraries(OpenGL::GL glfw glm glad) target_link_libraries(${LIBRARY_NAME} PUBLIC glfw) target_link_libraries(${LIBRARY_NAME} PUBLIC glad) -# target_link_libraries(${LIBRARY_NAME} PUBLIC OpenGL::GL) endif () @@ -70,6 +73,7 @@ endif() target_link_libraries(${LIBRARY_NAME} PUBLIC eigen) target_link_libraries(${LIBRARY_NAME} PUBLIC nlohmann_json::nlohmann_json) +target_include_directories(${LIBRARY_NAME} PUBLIC "${BARTA_ENGINE_DIR}/include") target_compile_features(${LIBRARY_NAME} PRIVATE cxx_std_20) @@ -86,12 +90,9 @@ install( LIBRARY DESTINATION lib ) -enable_testing() -#add_subdirectory(test/run ) -# -#install( -# TARGETS ${RUN_TEST_NAME} -# ARCHIVE DESTINATION lib -# LIBRARY DESTINATION lib -# RUNTIME DESTINATION bin -#) \ No newline at end of file +set(TESTS_TARGET_NAME BartaTests) + +add_executable(${TESTS_TARGET_NAME} test/main.cpp) +add_subdirectory(test) + +gtest_discover_tests(${TESTS_TARGET_NAME}) \ No newline at end of file diff --git a/Makefile b/Makefile index 9a36b0c..4335e85 100644 --- a/Makefile +++ b/Makefile @@ -6,13 +6,19 @@ configure: cmake -B build -S . -G "Unix Makefiles" build_p: - cmake --build build --target BartaEngine_lib --config Debug + cmake --build cmake-build-debug --target BartaEngine_lib --config Debug -j 18 -- + +build_tests: + cmake --build cmake-build-debug --target BartaEngine_lib --config Debug -j 18 -- clang_format: - cmake --build build --target run_clang_format -j 18 -- + cmake --build cmake-build-debug --target run_clang_format -j 18 -- # clang tidy doesn't work woth pch and g++ # clang_tidy_barta_engine: # cmake --build build --target BartaEngine_clangtidy run: ./build/bin/run_sandbox + +run_tests: + ./cmake-build-debug/test/BartaTests diff --git a/bin/create_class.bash b/bin/create_class.bash index b82803b..733cbfa 100755 --- a/bin/create_class.bash +++ b/bin/create_class.bash @@ -7,88 +7,119 @@ fi classname="$1" root_path="/home/bartanakin/repos/BartaEngine" -current_path="$(pwd)" +current_dir="$(pwd)" -if [[ "$current_path" != "$root_path/include"* ]]; then +if [[ "$current_dir" != "$root_path/include"* ]]; then echo "Error: This script must be run within a subdirectory of $root_path." exit 1 fi -relative_path="$(echo "$current_path" | sed -n 's|.*\/include/||p')" -rootDirectoryNamespace="Barta::$(echo "$relative_path" | sed 's|/|::|g')" +relative_dir="$(echo "$current_dir" | sed -n 's|.*\/include/||p')" +rootDirectoryNamespace="Barta::$(echo "$relative_dir" | sed 's|/|::|g')" # Create the header file -header_dir="$root_path/include/$relative_path" -header_file="$header_dir/$classname.h" +header_dir="$root_path/include/$relative_dir" +header_file="$root_path/include/$relative_dir/$classname.h" mkdir -p "$header_dir" -echo "#pragma once" > "$header_file" -echo "#include " >> "$header_file" -echo "" >> "$header_file" -echo "namespace $rootDirectoryNamespace {" >> "$header_file" -echo " class $classname {" >> "$header_file" -echo " };" >> "$header_file" -echo "}" >> "$header_file" - -echo "Header file '$header_file' created successfully." - -# Create the source directory if it doesn't exist -source_dir="$root_path/lib/$relative_path" -parent_source_dir="$(dirname "$source_dir")" - -if [ ! -d "$source_dir" ]; then - mkdir -p "$source_dir" - echo "Created directory '$source_dir'" - - # Ensure the parent directory has a CMakeLists.txt - parent_cmake_file="$parent_source_dir/CMakeLists.txt" - if [ ! -f "$parent_cmake_file" ]; then - touch "$parent_cmake_file" - echo "Created parent CMakeLists.txt at '$parent_cmake_file'" +if [ ! -f "$header_file" ]; then + echo "#pragma once" > "$header_file" + echo "#include " >> "$header_file" + echo "" >> "$header_file" + echo "namespace $rootDirectoryNamespace {" >> "$header_file" + echo "class $classname {};" >> "$header_file" + echo "}" >> "$header_file" + + echo "Header file '$header_file' created successfully." +else + echo "Header file '$header_file' already exists." +fi + +# Traverse up the directory tree to ensure all CMakeLists.txt files exist and include subdirs +add_cmakelists_recursively() { + local root_dir="$1" + local relative_dir="$2" + local classname="$3" + local target_name="$4" + local dir="$root_dir/$relative_dir" + + mkdir -p "$dir" + + # Ensure base CMakeLists.txt exists + base_cmake="$dir/CMakeLists.txt" + if [ ! -f "$base_cmake" ]; then + touch "$base_cmake" + echo "Created base CMakeLists.txt at $base_cmake" fi - # Add add_subdirectory if not already present - dir_name="$(basename "$source_dir")" - if ! grep -q "add_subdirectory($dir_name)" "$parent_cmake_file"; then - echo "Adding add_subdirectory($dir_name) to $parent_cmake_file..." - echo "" >> "$parent_cmake_file" - echo "add_subdirectory($dir_name)" >> "$parent_cmake_file" + # Ensure the source file is added to target_sources + if ! grep -q "target_sources(\${${target_name}} PUBLIC" "$base_cmake"; then + echo "Adding target_sources block to CMakeLists.txt..." + echo -e "target_sources(\${${target_name}} PUBLIC\n $classname.cpp\n)" >> "$base_cmake" + else + if ! grep -q "$classname.cpp" "$base_cmake"; then + echo "Adding $classname.cpp to target_sources..." + sed -i "/target_sources(\${${target_name}} PUBLIC/a \ $classname.cpp" "$base_cmake" + else + echo "$classname.cpp is already in CMakeLists.txt." + fi fi -fi -# Define the CMakeLists.txt file path -cmake_file="" -if [ -f "$source_dir/CMakeLists.txt" ]; then - cmake_file="$source_dir/CMakeLists.txt" -else - cmake_file="$source_dir/CMakeLists.txt" - touch "$cmake_file" - echo "Created empty CMakeLists.txt in '$source_dir'" -fi + while [ "$dir" != "$root_dir" ]; do + parent_dir="$(dirname "$dir")" + dir_name="$(basename "$dir")" -# Create the source file -source_file="$source_dir/$classname.cpp" + cmake_file="$parent_dir/CMakeLists.txt" + if [ ! -f "$cmake_file" ]; then + touch "$cmake_file" + echo "Created CMakeLists.txt at $cmake_file" + fi + + if ! grep -q "add_subdirectory($dir_name)" "$cmake_file"; then + echo "" >> "$cmake_file" + echo "add_subdirectory($dir_name)" >> "$cmake_file" + echo "Linked $dir_name in $cmake_file" + fi -echo "#include <$relative_path/$classname.h>" > "$source_file" -echo "" >> "$source_file" -echo "namespace $rootDirectoryNamespace {" >> "$source_file" -echo "}" >> "$source_file" + dir="$parent_dir" + done -echo "Source file '$source_file' created successfully." + echo "CMakeLists.txt updated successfully." +} -# Ensure the source file is added to target_sources -if ! grep -q "target_sources(\${LIBRARY_NAME} PUBLIC" "$cmake_file"; then - echo "Adding target_sources block to CMakeLists.txt..." - echo -e "target_sources(\${LIBRARY_NAME} PUBLIC\n $classname.cpp\n)" >> "$cmake_file" + +add_cmakelists_recursively "$root_path/lib" "$relative_dir" "$classname" "LIBRARY_NAME" + +# Create the source file +lib_file_path="$root_path/lib/$relative_dir/$classname.cpp" + +if [ ! -f "$lib_file_path" ]; then + echo "#include <$relative_dir/$classname.h>" > "$lib_file_path" + echo "" >> "$lib_file_path" + echo "namespace $rootDirectoryNamespace {" >> "$lib_file_path" + echo "}" >> "$lib_file_path" + + echo "Source file '$lib_file_path' created successfully." else - # Check if the source file is already in the list - if ! grep -q "$classname.cpp" "$cmake_file"; then - echo "Adding $classname.cpp to target_sources..." - sed -i "/target_sources(\${LIBRARY_NAME} PUBLIC/a \ $classname.cpp" "$cmake_file" - else - echo "$classname.cpp is already in CMakeLists.txt." - fi + echo "Source file '$lib_file_path' already exists." +fi +add_cmakelists_recursively "$root_path/test" "$relative_dir" "${classname}Test" "TESTS_TARGET_NAME" + +# Create the test file +test_file_path="$root_path/test/$relative_dir/${classname}Test.cpp" +if [ ! -f "$test_file_path" ]; then + echo "#include " > "$test_file_path" + echo "#include " >> "$test_file_path" + echo "#include <$relative_dir/$classname.h>" >> "$test_file_path" + echo "" >> "$test_file_path" + echo "using namespace $rootDirectoryNamespace;" >> "$test_file_path" + echo "" >> "$test_file_path" + echo "TEST(${classname}Test, ExampleTest) {" >> "$test_file_path" + echo " EXPECT_TRUE(true);" >> "$test_file_path" + echo "}" >> "$test_file_path" + + echo "Test file '$test_file_path' created successfully." +else + echo "Test file '$test_file_path' already exists." fi - -echo "CMakeLists.txt updated successfully." diff --git a/include/Application.h b/include/Application.h index 0244cce..48be998 100644 --- a/include/Application.h +++ b/include/Application.h @@ -37,6 +37,8 @@ templategraphicsBridge->createWindow(Vector2f(700.f, 700.f), this->windowName); + this->timer.restart(); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); while (this->isRunning() && this->graphicsBridge->logEvents(*(this->eventLogger))) { this->timer.restart(); while (!timer.finished()) { diff --git a/include/Collisions/CheckCollisionVisitorInterface.h b/include/Collisions/CheckCollisionVisitorInterface.h index b4a8ff8..46a4646 100644 --- a/include/Collisions/CheckCollisionVisitorInterface.h +++ b/include/Collisions/CheckCollisionVisitorInterface.h @@ -12,6 +12,6 @@ class CheckCollisionVisitorInterface { virtual CollisionTestResult checkStaticCollision(CollisionTestResultBuilder& collisionTestResultBuilder) const = 0; - virtual CollisionTestResult checkDynamicCollision(const float delta_time, CollisionTestResultBuilder& collisionTestResultBuilder) const = 0; + virtual CollisionTestResult checkDynamicCollision(PrecisionType delta_time, CollisionTestResultBuilder& collisionTestResultBuilder) const = 0; }; } diff --git a/include/Collisions/CheckCollisionVisitors/CircleAABBCheckCollisionVisitor.h b/include/Collisions/CheckCollisionVisitors/CircleAABBCheckCollisionVisitor.h index f4f27b6..552ff24 100644 --- a/include/Collisions/CheckCollisionVisitors/CircleAABBCheckCollisionVisitor.h +++ b/include/Collisions/CheckCollisionVisitors/CircleAABBCheckCollisionVisitor.h @@ -14,7 +14,7 @@ class CircleAABBCheckCollisionVisitor: public CheckCollisionVisitorInterface { CollisionTestResult checkStaticCollision(CollisionTestResultBuilder& collisionTestResultBuilder) const override; - CollisionTestResult checkDynamicCollision(float delta_time, CollisionTestResultBuilder& collisionTestResultBuilder) const override; + CollisionTestResult checkDynamicCollision(PrecisionType delta_time, CollisionTestResultBuilder& collisionTestResultBuilder) const override; private: const Circle circle; diff --git a/include/Collisions/CheckCollisionVisitors/CircleCircleCheckCollisionVisitor.h b/include/Collisions/CheckCollisionVisitors/CircleCircleCheckCollisionVisitor.h index dfb3038..19ddfcd 100644 --- a/include/Collisions/CheckCollisionVisitors/CircleCircleCheckCollisionVisitor.h +++ b/include/Collisions/CheckCollisionVisitors/CircleCircleCheckCollisionVisitor.h @@ -12,7 +12,7 @@ class CircleCircleCheckCollisionVisitor: public CheckCollisionVisitorInterface { CollisionTestResult checkStaticCollision(CollisionTestResultBuilder& collisionTestResultBuilder) const override; - CollisionTestResult checkDynamicCollision(float delta_time, CollisionTestResultBuilder& collisionTestResultBuilder) const override; + CollisionTestResult checkDynamicCollision(PrecisionType delta_time, CollisionTestResultBuilder& collisionTestResultBuilder) const override; private: const Circle circle1; diff --git a/include/Collisions/CheckCollisionVisitors/OBB_AABBCheckCollisionVisitor.h b/include/Collisions/CheckCollisionVisitors/OBB_AABBCheckCollisionVisitor.h index 6c22ae5..34b9db0 100644 --- a/include/Collisions/CheckCollisionVisitors/OBB_AABBCheckCollisionVisitor.h +++ b/include/Collisions/CheckCollisionVisitors/OBB_AABBCheckCollisionVisitor.h @@ -14,7 +14,7 @@ class OBB_AABBCheckCollisionVisitor: public CheckCollisionVisitorInterface { CollisionTestResult checkStaticCollision(CollisionTestResultBuilder& collisionTestResultBuilder) const override; - CollisionTestResult checkDynamicCollision(float deltaTime, CollisionTestResultBuilder& collisionTestResultBuilder) const override; + CollisionTestResult checkDynamicCollision(PrecisionType deltaTime, CollisionTestResultBuilder& collisionTestResultBuilder) const override; Point calculateCollisionPoint() const; diff --git a/include/Dynamics/DynamicsDTOCollection.h b/include/Dynamics/DynamicsDTOCollection.h index a6c4d49..e1cfc5c 100644 --- a/include/Dynamics/DynamicsDTOCollection.h +++ b/include/Dynamics/DynamicsDTOCollection.h @@ -20,6 +20,8 @@ class DynamicsDTOCollection { using DynamicsDTOArray = std::array; DynamicsDTOArray dynamicsDTOs; + DynamicsDTOCollection() noexcept = default; + explicit DynamicsDTOCollection( std::array dynamicsDTOs ): diff --git a/include/Dynamics/Mass/ConstantPointMassDistributionCalculator.h b/include/Dynamics/Mass/ConstantPointMassDistributionCalculator.h new file mode 100644 index 0000000..9b1dd1b --- /dev/null +++ b/include/Dynamics/Mass/ConstantPointMassDistributionCalculator.h @@ -0,0 +1,16 @@ +#pragma once +#include "MassDistributionCalculatorInterface.h" +#include "Objects/Soft/SoftObject.h" +#include + +namespace Barta::Dynamics::Mass { +class ConstantPointMassDistributionCalculator: public virtual MassDistributionCalculatorInterface { +public: + ConstantPointMassDistributionCalculator() = default; + + void prepareMassDistribution(Objects::Soft::SoftObject& object) override; + + SoftBody::StiffnessMatrixType assembleInverseMassMatrix(const Objects::Soft::Mesh& mesh, const Objects::Soft::NodalVectorType& positions) + override; +}; +} diff --git a/include/Dynamics/Mass/MassDistributionCalculatorInterface.h b/include/Dynamics/Mass/MassDistributionCalculatorInterface.h new file mode 100644 index 0000000..933aeab --- /dev/null +++ b/include/Dynamics/Mass/MassDistributionCalculatorInterface.h @@ -0,0 +1,20 @@ +#pragma once +#include "Dynamics/SoftBody/SoftBodyDynamicsInterface.h" +#include "Objects/Soft/Mesh.h" +#include "Objects/Soft/SoftObject.h" +#include + +namespace Barta::Dynamics::Mass { +class MassDistributionCalculatorInterface { +public: + MassDistributionCalculatorInterface() noexcept = default; + virtual ~MassDistributionCalculatorInterface() noexcept = default; + + virtual void prepareMassDistribution(Objects::Soft::SoftObject& object) = 0; + + virtual SoftBody::StiffnessMatrixType assembleInverseMassMatrix( + const Objects::Soft::Mesh& mesh, + const Objects::Soft::NodalVectorType& positions + ) = 0; +}; +} diff --git a/include/Dynamics/SoftBody/SoftBodyDynamicsInterface.h b/include/Dynamics/SoftBody/SoftBodyDynamicsInterface.h new file mode 100644 index 0000000..12762c9 --- /dev/null +++ b/include/Dynamics/SoftBody/SoftBodyDynamicsInterface.h @@ -0,0 +1,29 @@ +#pragma once +#include "Geometrics/Quaternion.h" +#include "Objects/Soft/Mesh.h" +#include "Objects/Soft/TetrahedralElement.h" +#include "Utilities/MatrixUtilities.h" +#include + +namespace Barta::Dynamics::SoftBody { + +using StiffnessMatrixType = Eigen::MatrixX; +using NodalVectorType = Objects::Soft::NodalVectorType; + +class SoftBodyDynamicsInterface { +public: + SoftBodyDynamicsInterface() = default; + virtual ~SoftBodyDynamicsInterface() = default; + + virtual StiffnessMatrixType assembleTangentStiffnessMatrix(const Objects::Soft::Mesh& elements, const NodalVectorType& nodesPositions) = 0; + + virtual NodalVectorType calculateNodalForces(const Objects::Soft::Mesh& elements, const NodalVectorType& nodesPositions) = 0; + + static void emplace12Matrix( + const Utils::Matrix12Type& m12, + const std::array& nodeIndices, + StiffnessMatrixType& stiffnessMatrix, + unsigned int block3x3Count + ); +}; +} diff --git a/include/Dynamics/SoftBody/StVenantKirchhoffDynamics.h b/include/Dynamics/SoftBody/StVenantKirchhoffDynamics.h new file mode 100644 index 0000000..ed3d23b --- /dev/null +++ b/include/Dynamics/SoftBody/StVenantKirchhoffDynamics.h @@ -0,0 +1,12 @@ +#pragma once +#include "SoftBodyDynamicsInterface.h" +#include + +namespace Barta::Dynamics::SoftBody { +class StVenantKirchhoffDynamics: public virtual SoftBodyDynamicsInterface { +public: + StiffnessMatrixType assembleTangentStiffnessMatrix(const Objects::Soft::Mesh& elements, const NodalVectorType& nodesPositions) override; + + NodalVectorType calculateNodalForces(const Objects::Soft::Mesh& elements, const NodalVectorType& nodesPositions) override; +}; +} diff --git a/include/Dynamics/UpdateStrategy/SoftBodyStrategy.h b/include/Dynamics/UpdateStrategy/SoftBodyStrategy.h index 5542340..6f60267 100644 --- a/include/Dynamics/UpdateStrategy/SoftBodyStrategy.h +++ b/include/Dynamics/UpdateStrategy/SoftBodyStrategy.h @@ -1,4 +1,7 @@ #pragma once +#include "Dynamics/Mass/MassDistributionCalculatorInterface.h" +#include "Dynamics/SoftBody/SoftBodyDynamicsInterface.h" +#include "Objects/Soft/SoftObject.h" #include #include #include @@ -6,12 +9,17 @@ namespace Barta::Dynamics::UpdateStrategy { class SoftBodyStrategy { // TODO public: + explicit SoftBodyStrategy( + std::unique_ptr softBodyDynamics, + std::unique_ptr massCalculator + ) noexcept; + template void prepare( ObjectType& object, float time ) { - std::cout << "TODO - prepare" << std::endl; + throw std::runtime_error("This method is for Soft Bodies only. Define you specialization in order to use it."); } template @@ -19,9 +27,19 @@ class SoftBodyStrategy { // TODO ObjectType& object, bool doForward ) { - std::cout << "TODO - update" << std::endl; + throw std::runtime_error("This method is for Soft Bodies only. Define you specialization in order to use it."); } + +private: + std::unique_ptr softBodyDynamics; + std::unique_ptr massCalculator; }; static_assert(UpdateStrategyConcept); } + +template<> +void Barta::Dynamics::UpdateStrategy::SoftBodyStrategy::prepare(Objects::Soft::SoftObject& object, float time); + +template<> +void Barta::Dynamics::UpdateStrategy::SoftBodyStrategy::update(Objects::Soft::SoftObject& object, bool doForward); diff --git a/include/Dynamics/UpdateStrategy/UpdateStrategyManager.h b/include/Dynamics/UpdateStrategy/UpdateStrategyManager.h index 171c889..ff73009 100644 --- a/include/Dynamics/UpdateStrategy/UpdateStrategyManager.h +++ b/include/Dynamics/UpdateStrategy/UpdateStrategyManager.h @@ -11,8 +11,8 @@ class UpdateStrategyManager: public UpdateStrategyManager requires UpdateStrategyConcept explicit UpdateStrategyManager( - UpdateStrategyForConstructor updateStrategyForConstructor, - RestForConstructor... restForConstructor + UpdateStrategyForConstructor&& updateStrategyForConstructor, + RestForConstructor&&... restForConstructor ) noexcept: UpdateStrategyManager(std::forward(updateStrategyForConstructor)), UpdateStrategyManager(std::forward(restForConstructor...)) {} @@ -40,9 +40,9 @@ template requires UpdateStrategyConcept class UpdateStrategyManager { public: explicit UpdateStrategyManager( - UpdateStrategy updateStrategy + UpdateStrategy&& updateStrategy ) noexcept: - updateStrategy(std::move(updateStrategy)) {} + updateStrategy(std::forward(updateStrategy)) {} template requires ListManagerConcept void prepare( diff --git a/include/Geometrics/Point.h b/include/Geometrics/Point.h index 04777dc..7a109c3 100644 --- a/include/Geometrics/Point.h +++ b/include/Geometrics/Point.h @@ -24,6 +24,12 @@ class Point { PrecisionType z() const noexcept { return this->base.z(); } + PrecisionType& x() noexcept { return this->base.x(); } + + PrecisionType& y() noexcept { return this->base.y(); } + + PrecisionType& z() noexcept { return this->base.z(); } + PrecisionType& operator[]( int i ) noexcept { @@ -106,6 +112,15 @@ inline Point& operator+=( return p; } +inline Point& operator-=( + Point& p, + const Vector& v +) { + p = p - v; + + return p; +} + inline Point operator+( const Vector& v, const Point& p diff --git a/include/Geometrics/Quaternion.h b/include/Geometrics/Quaternion.h index 1a8d146..42b4925 100644 --- a/include/Geometrics/Quaternion.h +++ b/include/Geometrics/Quaternion.h @@ -3,7 +3,7 @@ namespace Barta { -using PrecisionType = float; +using PrecisionType = double; using Matrix = Eigen::Matrix; class Quaternion: public Eigen::Quaternion { diff --git a/include/Geometrics/Vector.h b/include/Geometrics/Vector.h index 1aff786..86ca63e 100644 --- a/include/Geometrics/Vector.h +++ b/include/Geometrics/Vector.h @@ -29,6 +29,12 @@ class Vector { PrecisionType z() const noexcept { return this->base.z(); } + PrecisionType& x() noexcept { return this->base.x(); } + + PrecisionType& y() noexcept { return this->base.y(); } + + PrecisionType& z() noexcept { return this->base.z(); } + PrecisionType& operator[]( int i ) noexcept { @@ -125,6 +131,14 @@ class Vector { return *this; } + Vector& operator/=( + const PrecisionType& scalar + ) noexcept { + *this = *this / scalar; + + return *this; + } + Vector zeroised() const noexcept { auto vector = *this; for (PrecisionType& value: vector.base) { diff --git a/include/Graphics/OpenGL_Bridge/Vertex.h b/include/Graphics/OpenGL_Bridge/Vertex.h index e99ecb0..0c281aa 100644 --- a/include/Graphics/OpenGL_Bridge/Vertex.h +++ b/include/Graphics/OpenGL_Bridge/Vertex.h @@ -5,7 +5,8 @@ namespace Barta::Graphics::OpenGL_Bridge { struct Vertex { - Point position; + float x, y, z; + float w = 1.f; float red; float green; float blue; @@ -14,7 +15,9 @@ struct Vertex { Point position, Color color ) noexcept: - position(position), + x(position.x()), + y(position.y()), + z(position.z()), red(Vertex::convertColorFormat(color.r)), green(Vertex::convertColorFormat(color.g)), blue(Vertex::convertColorFormat(color.b)) {} @@ -25,7 +28,9 @@ struct Vertex { float green, float blue ) noexcept: - position(position), + x(position.x()), + y(position.y()), + z(position.z()), red(red), green(green), blue(blue) {} @@ -41,6 +46,8 @@ inline Vertex operator*( const Matrix& M, const Vertex& v ) noexcept { - return {M * v.position, v.red, v.green, v.blue}; + Point p = {v.x, v.y, v.z}; + + return {M * p, v.red, v.green, v.blue}; } } diff --git a/include/Objects/Soft/Mesh.h b/include/Objects/Soft/Mesh.h index 3db4b1b..00ae20a 100644 --- a/include/Objects/Soft/Mesh.h +++ b/include/Objects/Soft/Mesh.h @@ -5,9 +5,13 @@ namespace Barta::Objects::Soft { struct Mesh { - std::vector nodes; + std::vector nodes; // All Dirichlet nodes are in the back of the vector std::vector elements; + unsigned int dirichletNodeCount; + Mesh() = default; Mesh(std::vector nodes, std::vector elements) noexcept; + + size_t getNodalVectorSize() const noexcept { return 3 * (this->nodes.size() - this->dirichletNodeCount); } }; } diff --git a/include/Objects/Soft/MeshLoader/MshData.h b/include/Objects/Soft/MeshLoader/MshData.h deleted file mode 100644 index 38276c7..0000000 --- a/include/Objects/Soft/MeshLoader/MshData.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include - -namespace Barta::Objects::Soft::MeshLoader { - -struct MshData { - enum class ElementType { - TETRAHEDRON = 4 - }; - - struct Node3D { - unsigned int tag; - float x, y, z; - }; - - struct Element { - ElementType type; - std::array nodeIndices; - }; - - std::string version; - std::vector nodes; - std::vector elements; -}; -} diff --git a/include/Objects/Soft/MeshLoader/MshDataLoader.h b/include/Objects/Soft/MeshLoader/MshDataLoader.h deleted file mode 100644 index 15b237d..0000000 --- a/include/Objects/Soft/MeshLoader/MshDataLoader.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include -#include - -namespace Barta::Objects::Soft::MeshLoader { -class MshDataLoader: public MshDataLoaderInterface { - static constexpr std::array ALLOWED_VERSIONS = {"4.1"}; - -public: - MshData loadMshDataFromFile(const std::filesystem::path& filePath) const override; -}; -} diff --git a/include/Objects/Soft/MshLoader/DirichletConditionPlugin.h b/include/Objects/Soft/MshLoader/DirichletConditionPlugin.h new file mode 100644 index 0000000..c21ac32 --- /dev/null +++ b/include/Objects/Soft/MshLoader/DirichletConditionPlugin.h @@ -0,0 +1,12 @@ +#pragma once +#include "MshLoaderPluginInterface.h" +#include + +namespace Barta::Objects::Soft::MshLoader { +class DirichletConditionPlugin: public virtual MshLoaderPluginInterface { +public: + void loadMshData(std::istream& in, MshData& mshData) override; + + std::string getSectionKey() const override { return "$PhysicalNames"; } +}; +} diff --git a/include/Objects/Soft/MshLoader/ElementsPlugin.h b/include/Objects/Soft/MshLoader/ElementsPlugin.h new file mode 100644 index 0000000..375aeb5 --- /dev/null +++ b/include/Objects/Soft/MshLoader/ElementsPlugin.h @@ -0,0 +1,12 @@ +#pragma once +#include "MshLoaderPluginInterface.h" +#include + +namespace Barta::Objects::Soft::MshLoader { +class ElementsPlugin: public virtual MshLoaderPluginInterface { +public: + void loadMshData(std::istream& in, MshData& mshData) override; + + std::string getSectionKey() const override { return "$Elements"; } +}; +} diff --git a/include/Objects/Soft/MshLoader/EntitiesPlugin.h b/include/Objects/Soft/MshLoader/EntitiesPlugin.h new file mode 100644 index 0000000..4ecccd4 --- /dev/null +++ b/include/Objects/Soft/MshLoader/EntitiesPlugin.h @@ -0,0 +1,12 @@ +#pragma once +#include "MshLoaderPluginInterface.h" +#include + +namespace Barta::Objects::Soft::MshLoader { +class EntitiesPlugin: public virtual MshLoaderPluginInterface { +public: + void loadMshData(std::istream& in, MshData& mshData) override; + + std::string getSectionKey() const override { return "$Entities"; } +}; +} diff --git a/include/Objects/Soft/MshLoader/MeshFormatPlugin.h b/include/Objects/Soft/MshLoader/MeshFormatPlugin.h new file mode 100644 index 0000000..04fa28e --- /dev/null +++ b/include/Objects/Soft/MshLoader/MeshFormatPlugin.h @@ -0,0 +1,15 @@ +#pragma once +#include "MshLoaderPluginInterface.h" +#include + +namespace Barta::Objects::Soft::MshLoader { +class MeshFormatPlugin: public virtual MshLoaderPluginInterface { +public: + void loadMshData(std::istream& in, MshData& mshData) override; + + std::string getSectionKey() const override { return "$MeshFormat"; } + +private: + static constexpr std::array ALLOWED_VERSIONS = {"4.1"}; +}; +} diff --git a/include/Objects/Soft/MshLoader/MshData.h b/include/Objects/Soft/MshLoader/MshData.h new file mode 100644 index 0000000..55b6609 --- /dev/null +++ b/include/Objects/Soft/MshLoader/MshData.h @@ -0,0 +1,53 @@ +#pragma once +#include + +namespace Barta::Objects::Soft::MshLoader { + +struct MshData { + enum class ElementType { + TRIANGLE = 2, + TETRAHEDRON = 4 + }; + + enum class EntityDimension { + POINT = 0, + LINE = 1, + SURFACE = 2, + VOLUME = 3 + }; + + struct PhysicalGroup { + unsigned int tag; + unsigned int dim; + std::string name; + }; + + struct Entity { + unsigned int tag; + float x_min, y_min, z_min; + float x_max, y_max, z_max; + std::vector physicalGroupTags; + }; + + struct Node3D { + unsigned int tag; + float x, y, z; + }; + + struct TetrahedralElement { + std::array nodeTags; + }; + + struct TriangleElement { + unsigned int entityTag; + std::array nodeTags; + }; + + std::string version; + std::vector physicalGroups; + std::map> entities; + std::vector nodes; + std::vector tetrahedralElements; + std::vector triangleElements; +}; +} diff --git a/include/Objects/Soft/MshLoader/MshDataLoader.h b/include/Objects/Soft/MshLoader/MshDataLoader.h new file mode 100644 index 0000000..12da219 --- /dev/null +++ b/include/Objects/Soft/MshLoader/MshDataLoader.h @@ -0,0 +1,18 @@ +#pragma once +#include "MshDataLoaderInterface.h" +#include "MshLoaderPluginInterface.h" +#include + +namespace Barta::Objects::Soft::MshLoader { +class MshDataLoader: public MshDataLoaderInterface { + static constexpr std::array ALLOWED_VERSIONS = {"4.1"}; + +public: + MshData loadMshDataFromFile(const std::filesystem::path& filePath) const override; + + void registerPlugin(std::unique_ptr plugin); + +private: + std::vector> plugins; +}; +} diff --git a/include/Objects/Soft/MeshLoader/MshDataLoaderInterface.h b/include/Objects/Soft/MshLoader/MshDataLoaderInterface.h similarity index 71% rename from include/Objects/Soft/MeshLoader/MshDataLoaderInterface.h rename to include/Objects/Soft/MshLoader/MshDataLoaderInterface.h index 3b9395d..a87e8c6 100644 --- a/include/Objects/Soft/MeshLoader/MshDataLoaderInterface.h +++ b/include/Objects/Soft/MshLoader/MshDataLoaderInterface.h @@ -1,8 +1,8 @@ #pragma once +#include "MshData.h" #include -#include -namespace Barta::Objects::Soft::MeshLoader { +namespace Barta::Objects::Soft::MshLoader { class MshDataLoaderInterface { public: virtual ~MshDataLoaderInterface() = default; diff --git a/include/Objects/Soft/MshLoader/MshLoaderPluginInterface.h b/include/Objects/Soft/MshLoader/MshLoaderPluginInterface.h new file mode 100644 index 0000000..788829d --- /dev/null +++ b/include/Objects/Soft/MshLoader/MshLoaderPluginInterface.h @@ -0,0 +1,16 @@ +#pragma once + +#include "MshData.h" +#include + +namespace Barta::Objects::Soft::MshLoader { + +class MshLoaderPluginInterface { +public: + virtual ~MshLoaderPluginInterface() = default; + + virtual void loadMshData(std::istream& in, MshData& mshData) = 0; + virtual std::string getSectionKey() const = 0; +}; + +} diff --git a/include/Objects/Soft/MshLoader/NodesPlugin.h b/include/Objects/Soft/MshLoader/NodesPlugin.h new file mode 100644 index 0000000..fde531f --- /dev/null +++ b/include/Objects/Soft/MshLoader/NodesPlugin.h @@ -0,0 +1,12 @@ +#pragma once +#include "MshLoaderPluginInterface.h" +#include + +namespace Barta::Objects::Soft::MshLoader { +class NodesPlugin: public virtual MshLoaderPluginInterface { +public: + void loadMshData(std::istream& in, MshData& mshData) override; + + std::string getSectionKey() const override { return "$Nodes"; } +}; +} diff --git a/include/Objects/Soft/MshParser/DirichletConditionParserDecorator.h b/include/Objects/Soft/MshParser/DirichletConditionParserDecorator.h new file mode 100644 index 0000000..bfbe752 --- /dev/null +++ b/include/Objects/Soft/MshParser/DirichletConditionParserDecorator.h @@ -0,0 +1,15 @@ +#pragma once +#include "MshDataParserInterface.h" +#include + +namespace Barta::Objects::Soft::MshParser { +class DirichletConditionParserDecorator: public virtual MshDataParserInterface { +public: + explicit DirichletConditionParserDecorator(MshDataParserInterface& decorated) noexcept; + + Mesh parse(const MshData& mshData) const override; + +private: + MshDataParserInterface& decorated; +}; +} diff --git a/include/Objects/Soft/MeshLoader/MshDataParser.h b/include/Objects/Soft/MshParser/MshDataParser.h similarity index 63% rename from include/Objects/Soft/MeshLoader/MshDataParser.h rename to include/Objects/Soft/MshParser/MshDataParser.h index ff76d19..d80f574 100644 --- a/include/Objects/Soft/MeshLoader/MshDataParser.h +++ b/include/Objects/Soft/MshParser/MshDataParser.h @@ -1,8 +1,8 @@ #pragma once -#include +#include #include -namespace Barta::Objects::Soft::MeshLoader { +namespace Barta::Objects::Soft::MshParser { class MshDataParser: public MshDataParserInterface { public: MshDataParser() = default; diff --git a/include/Objects/Soft/MeshLoader/MshDataParserInterface.h b/include/Objects/Soft/MshParser/MshDataParserInterface.h similarity index 51% rename from include/Objects/Soft/MeshLoader/MshDataParserInterface.h rename to include/Objects/Soft/MshParser/MshDataParserInterface.h index 2e4f945..ac8b865 100644 --- a/include/Objects/Soft/MeshLoader/MshDataParserInterface.h +++ b/include/Objects/Soft/MshParser/MshDataParserInterface.h @@ -1,14 +1,16 @@ #pragma once #include -#include +#include #include -namespace Barta::Objects::Soft::MeshLoader { +namespace Barta::Objects::Soft::MshParser { +using MshLoader::MshData; + class MshDataParserInterface { public: MshDataParserInterface() = default; virtual ~MshDataParserInterface() = default; - virtual Mesh parse(const MshData& mshData) const = 0; + virtual Mesh parse(const MshLoader::MshData& mshData) const = 0; }; } diff --git a/include/Objects/Soft/MeshLoader/SurfaceFacesMshParserDecorator.h b/include/Objects/Soft/MshParser/SurfaceFacesMshParserDecorator.h similarity index 88% rename from include/Objects/Soft/MeshLoader/SurfaceFacesMshParserDecorator.h rename to include/Objects/Soft/MshParser/SurfaceFacesMshParserDecorator.h index d6e2e7a..95be2f2 100644 --- a/include/Objects/Soft/MeshLoader/SurfaceFacesMshParserDecorator.h +++ b/include/Objects/Soft/MshParser/SurfaceFacesMshParserDecorator.h @@ -2,7 +2,7 @@ #include "MshDataParserInterface.h" #include -namespace Barta::Objects::Soft::MeshLoader { +namespace Barta::Objects::Soft::MshParser { class SurfaceFacesMshParserDecorator: public MshDataParserInterface { public: SurfaceFacesMshParserDecorator(MshDataParserInterface& decorated) noexcept; diff --git a/include/Objects/Soft/MshParser/TetrahedralElementLoaderDecorator.h b/include/Objects/Soft/MshParser/TetrahedralElementLoaderDecorator.h new file mode 100644 index 0000000..4b6b9bc --- /dev/null +++ b/include/Objects/Soft/MshParser/TetrahedralElementLoaderDecorator.h @@ -0,0 +1,15 @@ +#pragma once +#include "MshDataParserInterface.h" +#include + +namespace Barta::Objects::Soft::MshParser { +class TetrahedralElementLoaderDecorator: public virtual MshDataParserInterface { +public: + TetrahedralElementLoaderDecorator(MshDataParserInterface& decorated) noexcept; + + Mesh parse(const MshData& mshData) const override; + +private: + MshDataParserInterface& decorated; +}; +} diff --git a/include/Objects/Soft/Node.h b/include/Objects/Soft/Node.h index c73d757..4874d59 100644 --- a/include/Objects/Soft/Node.h +++ b/include/Objects/Soft/Node.h @@ -4,8 +4,29 @@ namespace Barta::Objects::Soft { struct Node { - std::string label; + Point restPosition; DynamicsDTOCollection dynamicsDTOCollection; + unsigned int tag; bool isZeroDirichlet; + + Node() noexcept = default; + + Node( + DynamicsDTOCollection dynamicsDTOCollection, + unsigned int tag, + bool isZeroDirichlet + ) noexcept: + restPosition(dynamicsDTOCollection.dynamicsDTOs[DynamicsDTOIteration::CURRENT].massCenter), + tag(tag), + dynamicsDTOCollection(dynamicsDTOCollection), + isZeroDirichlet(isZeroDirichlet) {} + + Node( + Point massCenter + ) noexcept: + restPosition(massCenter), + dynamicsDTOCollection(DynamicsDTO{massCenter}), + tag(0), + isZeroDirichlet(false) {} }; } diff --git a/include/Objects/Soft/SoftObject.h b/include/Objects/Soft/SoftObject.h index d306b4c..c648731 100644 --- a/include/Objects/Soft/SoftObject.h +++ b/include/Objects/Soft/SoftObject.h @@ -28,6 +28,10 @@ class SoftObject: public virtual GraphicsDataAwareInterface, public virtual Hitb GraphicsDataList getGraphicsData() override; Vector getForce(DynamicsDTOIteration positionIteration, DynamicsDTOIteration velocityIteration); + + Mesh& getMesh() noexcept; + + const Mesh& getMesh() const noexcept; }; static_assert(Barta::Dynamics::UpdateStrategy::DynamicsAwareConcept); diff --git a/include/Objects/Soft/TetrahedralElement.h b/include/Objects/Soft/TetrahedralElement.h index e176083..df8989d 100644 --- a/include/Objects/Soft/TetrahedralElement.h +++ b/include/Objects/Soft/TetrahedralElement.h @@ -1,11 +1,19 @@ #pragma once #include +#include #include namespace Barta::Objects::Soft { +using Barta::Utils::Matrix3Type; +using NodalVectorType = Eigen::VectorX; + class TetrahedralElement { - std::array nodes; + std::array nodeIndices; std::array isFaceASurface; + Eigen::Matrix referenceShapeMatrix; + +private: + Eigen::Vector3 retrieveNodePosition(const std::vector& nodes, const NodalVectorType& nodesPositions, unsigned int i) const; public: static constexpr auto FACE_INDICES_CONTAINER = std::array, 4>({ @@ -14,12 +22,16 @@ class TetrahedralElement { std::array{1, 3, 2}, std::array{2, 3, 0}, }); - TetrahedralElement(Node& n1, Node& n2, Node& n3, Node& n4) noexcept; + TetrahedralElement(const std::vector& nodes, unsigned int n1, unsigned int n2, unsigned int n3, unsigned int n4) noexcept; void setIsFaceSurface(unsigned char index, bool isSurface); bool getIsFaceSurface(unsigned char index) const noexcept; - const Node& operator[](unsigned char index) const noexcept; + const std::array& getNodeIndices() const noexcept; + + Matrix3Type getInverseReferenceShapeMatrix() const noexcept; + + Matrix3Type calculateShapeMatrix(const std::vector& nodes, const NodalVectorType& nodesPositions = NodalVectorType{0}) const; }; } diff --git a/include/SceneLoader/SoftObjectJsonDecoder.h b/include/SceneLoader/SoftObjectJsonDecoder.h index 9f97e28..9015539 100644 --- a/include/SceneLoader/SoftObjectJsonDecoder.h +++ b/include/SceneLoader/SoftObjectJsonDecoder.h @@ -1,7 +1,14 @@ #pragma once -#include "Objects/Soft/MeshLoader/MshDataParser.h" -#include "Objects/Soft/MeshLoader/SurfaceFacesMshParserDecorator.h" -#include +#include "Objects/Soft/MshLoader/DirichletConditionPlugin.h" +#include "Objects/Soft/MshLoader/ElementsPlugin.h" +#include "Objects/Soft/MshLoader/EntitiesPlugin.h" +#include "Objects/Soft/MshLoader/MeshFormatPlugin.h" +#include "Objects/Soft/MshLoader/NodesPlugin.h" +#include "Objects/Soft/MshParser/DirichletConditionParserDecorator.h" +#include "Objects/Soft/MshParser/MshDataParser.h" +#include "Objects/Soft/MshParser/SurfaceFacesMshParserDecorator.h" +#include "Objects/Soft/MshParser/TetrahedralElementLoaderDecorator.h" +#include #include #include #include @@ -33,13 +40,22 @@ class Barta::SceneLoader::ObjectJsonDecoder { } if (meshType.get() == "msh") { - auto mshDecoder = Objects::Soft::MeshLoader::MshDataLoader(); - auto meshParser = Objects::Soft::MeshLoader::MshDataParser(); - auto meshParserDecorator = Objects::Soft::MeshLoader::SurfaceFacesMshParserDecorator(meshParser); + auto mshDecoder = Objects::Soft::MshLoader::MshDataLoader(); + + mshDecoder.registerPlugin(std::make_unique()); + mshDecoder.registerPlugin(std::make_unique()); + mshDecoder.registerPlugin(std::make_unique()); + mshDecoder.registerPlugin(std::make_unique()); + mshDecoder.registerPlugin(std::make_unique()); + + auto meshParser = Objects::Soft::MshParser::MshDataParser(); + auto meshParserDecorator1 = Objects::Soft::MshParser::DirichletConditionParserDecorator(meshParser); + auto meshParserDecorator2 = Objects::Soft::MshParser::TetrahedralElementLoaderDecorator(meshParserDecorator1); + auto meshParserDecorator3 = Objects::Soft::MshParser::SurfaceFacesMshParserDecorator(meshParserDecorator2); std::string fileName = json.at("sourceFileName"); return new SoftObject( - meshParserDecorator.parse(mshDecoder.loadMshDataFromFile(std::filesystem::path("meshes") / std::filesystem::path(fileName))), + meshParserDecorator3.parse(mshDecoder.loadMshDataFromFile(std::filesystem::path("meshes") / std::filesystem::path(fileName))), dynamicsDto ); } diff --git a/include/Utilities/MatrixUtilities.h b/include/Utilities/MatrixUtilities.h new file mode 100644 index 0000000..7cf079e --- /dev/null +++ b/include/Utilities/MatrixUtilities.h @@ -0,0 +1,16 @@ +#pragma once +#include +#include + +namespace Barta::Utils { + +using Matrix3Type = Eigen::Matrix; +using Matrix9Type = Eigen::Matrix; +using Matrix12Type = Eigen::Matrix; + +Matrix9Type createEquationFromAXB(const Matrix3Type& A, const Matrix3Type& B); + +Matrix9Type createEquationFromTrace(const Matrix3Type& A, const Matrix3Type& B, const Matrix3Type& C); + +Matrix12Type create12MatrixFrom9MatrixAndThirdNewtonLaw(const Matrix9Type& M9); +} diff --git a/include/pch.h b/include/pch.h index 265ebc6..10166a7 100644 --- a/include/pch.h +++ b/include/pch.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/Collisions/CheckCollisionVisitors/AABB_AABBCheckCollisionVisitor.cpp b/lib/Collisions/CheckCollisionVisitors/AABB_AABBCheckCollisionVisitor.cpp index 98d6076..21989a7 100644 --- a/lib/Collisions/CheckCollisionVisitors/AABB_AABBCheckCollisionVisitor.cpp +++ b/lib/Collisions/CheckCollisionVisitors/AABB_AABBCheckCollisionVisitor.cpp @@ -30,7 +30,7 @@ CollisionTestResult AABB_AABBCheckCollisionVisitor::checkStaticCollision( } CollisionTestResult AABB_AABBCheckCollisionVisitor::checkDynamicCollision( - const float delta_time, + const PrecisionType delta_time, CollisionTestResultBuilder& collisionTestResultBuilder ) const { auto staticCollisionResult = this->checkStaticCollision(collisionTestResultBuilder); diff --git a/lib/Collisions/CheckCollisionVisitors/CircleAABBCheckCollisionVisitor.cpp b/lib/Collisions/CheckCollisionVisitors/CircleAABBCheckCollisionVisitor.cpp index 8444acb..645b68d 100644 --- a/lib/Collisions/CheckCollisionVisitors/CircleAABBCheckCollisionVisitor.cpp +++ b/lib/Collisions/CheckCollisionVisitors/CircleAABBCheckCollisionVisitor.cpp @@ -74,7 +74,7 @@ CollisionTestResult CircleAABBCheckCollisionVisitor::checkStaticCollision( #pragma GCC diagnostic pop CollisionTestResult CircleAABBCheckCollisionVisitor::checkDynamicCollision( - const float delta_time, + const PrecisionType delta_time, CollisionTestResultBuilder& collisionTestResultBuilder ) const { auto staticResult = this->checkStaticCollision(collisionTestResultBuilder); @@ -145,7 +145,7 @@ Point CircleAABBCheckCollisionVisitor::matchCornerCenter( } Vector CircleAABBCheckCollisionVisitor::calculateNormVector( - const float delta_time + const PrecisionType delta_time ) const { std::vector possibleNormalVectors = { {0.f, -1.f}, diff --git a/lib/Collisions/CheckCollisionVisitors/CircleCircleCheckCollisionVisitor.cpp b/lib/Collisions/CheckCollisionVisitors/CircleCircleCheckCollisionVisitor.cpp index 5232ef7..70afea5 100644 --- a/lib/Collisions/CheckCollisionVisitors/CircleCircleCheckCollisionVisitor.cpp +++ b/lib/Collisions/CheckCollisionVisitors/CircleCircleCheckCollisionVisitor.cpp @@ -42,7 +42,7 @@ CollisionTestResult CircleCircleCheckCollisionVisitor::checkStaticCollision( #pragma GCC diagnostic pop CollisionTestResult CircleCircleCheckCollisionVisitor::checkDynamicCollision( - const float delta_time, + const PrecisionType delta_time, CollisionTestResultBuilder& collisionTestResultBuilder ) const { auto staticResult = this->checkStaticCollision(collisionTestResultBuilder); diff --git a/lib/Collisions/CheckCollisionVisitors/OBB_AABBCheckCollisionVisitor.cpp b/lib/Collisions/CheckCollisionVisitors/OBB_AABBCheckCollisionVisitor.cpp index 525a65e..53b26e6 100644 --- a/lib/Collisions/CheckCollisionVisitors/OBB_AABBCheckCollisionVisitor.cpp +++ b/lib/Collisions/CheckCollisionVisitors/OBB_AABBCheckCollisionVisitor.cpp @@ -80,7 +80,7 @@ Barta::CollisionTestResult Barta::OBB_AABBCheckCollisionVisitor::checkStaticColl } Barta::CollisionTestResult Barta::OBB_AABBCheckCollisionVisitor::checkDynamicCollision( - float deltaTime, + PrecisionType deltaTime, CollisionTestResultBuilder& collisionTestResultBuilder ) const { // throw std::runtime_error("Not implemented"); diff --git a/lib/Dynamics/CMakeLists.txt b/lib/Dynamics/CMakeLists.txt index f2c530e..255453f 100644 --- a/lib/Dynamics/CMakeLists.txt +++ b/lib/Dynamics/CMakeLists.txt @@ -1 +1,4 @@ add_subdirectory(Timers) +add_subdirectory(Mass) +add_subdirectory(SoftBody) +add_subdirectory(UpdateStrategy) diff --git a/lib/Dynamics/Mass/CMakeLists.txt b/lib/Dynamics/Mass/CMakeLists.txt new file mode 100644 index 0000000..5a617fc --- /dev/null +++ b/lib/Dynamics/Mass/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${LIBRARY_NAME} PUBLIC + ConstantPointMassDistributionCalculator.cpp +) diff --git a/lib/Dynamics/Mass/ConstantPointMassDistributionCalculator.cpp b/lib/Dynamics/Mass/ConstantPointMassDistributionCalculator.cpp new file mode 100644 index 0000000..f7ca1e5 --- /dev/null +++ b/lib/Dynamics/Mass/ConstantPointMassDistributionCalculator.cpp @@ -0,0 +1,59 @@ +#include +#include "Objects/Soft/SoftObject.h" +#include "Utilities/DynamicsIteration.h" + +namespace Barta::Dynamics::Mass { +void ConstantPointMassDistributionCalculator::prepareMassDistribution( + Objects::Soft::SoftObject& object +) { + auto massCenterTranslation = Vector::Zero(); + PrecisionType totalMass = 0.; + unsigned int i = 0; // TODO change to range loop with index using std::view::enumerate + for (const auto& node: object.getMesh().nodes) { + auto& dynamicsData = node.dynamicsDTOCollection.dynamicsDTOs[DynamicsDTOIteration::NEXT]; + + PrecisionType mass = 1. / dynamicsData.inverseMass; + totalMass += mass; + massCenterTranslation += mass * dynamicsData.massCenter.toVector(); + + ++i; + } + + massCenterTranslation /= totalMass; + massCenterTranslation = massCenterTranslation.zeroised(); + i = 0; // TODO change to range loop with index using std::view::enumerate + for (auto& node: object.getMesh().nodes) { + auto& dynamicsData = node.dynamicsDTOCollection.dynamicsDTOs[DynamicsDTOIteration::NEXT]; + + dynamicsData.massCenter -= massCenterTranslation; + + ++i; + } + + Utils::extractNextDynamics(object).massCenter += Transformation::rotation(Utils::extractCurrentDynamics(object).rotation) * massCenterTranslation; +} + +SoftBody::StiffnessMatrixType ConstantPointMassDistributionCalculator::assembleInverseMassMatrix( + const Objects::Soft::Mesh& mesh, + const Objects::Soft::NodalVectorType& positions +) { + SoftBody::StiffnessMatrixType massMatrix = SoftBody::StiffnessMatrixType::Zero(mesh.getNodalVectorSize(), mesh.getNodalVectorSize()); + unsigned int i = 0; + for (const auto& node: mesh.nodes) { + if (node.isZeroDirichlet) { + continue; + } + + auto dynamicsData = node.dynamicsDTOCollection.dynamicsDTOs[DynamicsDTOIteration::CURRENT]; + + massMatrix(3 * i, 3 * i) = dynamicsData.inverseMass; + massMatrix(3 * i + 1, 3 * i + 1) = dynamicsData.inverseMass; + massMatrix(3 * i + 2, 3 * i + 2) = dynamicsData.inverseMass; + + ++i; + } + + return massMatrix; +} + +} diff --git a/lib/Dynamics/SoftBody/CMakeLists.txt b/lib/Dynamics/SoftBody/CMakeLists.txt new file mode 100644 index 0000000..d000406 --- /dev/null +++ b/lib/Dynamics/SoftBody/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIBRARY_NAME} PUBLIC + StVenantKirchhoffDynamics.cpp + SoftBodyDynamicsInterface.cpp +) diff --git a/lib/Dynamics/SoftBody/SoftBodyDynamicsInterface.cpp b/lib/Dynamics/SoftBody/SoftBodyDynamicsInterface.cpp new file mode 100644 index 0000000..4d0c162 --- /dev/null +++ b/lib/Dynamics/SoftBody/SoftBodyDynamicsInterface.cpp @@ -0,0 +1,25 @@ +#include + +namespace Barta::Dynamics::SoftBody { +void SoftBodyDynamicsInterface::emplace12Matrix( + const Utils::Matrix12Type& m12, + const std::array& nodeIndices, + StiffnessMatrixType& stiffnessMatrix, + unsigned int block3x3Count +) { + // std::cout << m12 << std::endl << std::endl; + for (unsigned int i = 0; i < 4; i++) { + if (nodeIndices[i] >= block3x3Count) { + continue; + } + + for (unsigned int j = 0; j < 4; j++) { + if (nodeIndices[j] >= block3x3Count) { + continue; + } + + stiffnessMatrix.block(3 * nodeIndices[i], 3 * nodeIndices[j], 3, 3) += m12.block<3, 3>(3 * i, 3 * j); + } + } +} +} diff --git a/lib/Dynamics/SoftBody/StVenantKirchhoffDynamics.cpp b/lib/Dynamics/SoftBody/StVenantKirchhoffDynamics.cpp new file mode 100644 index 0000000..281f4fb --- /dev/null +++ b/lib/Dynamics/SoftBody/StVenantKirchhoffDynamics.cpp @@ -0,0 +1,87 @@ +#include +#include "Utilities/MatrixUtilities.h" + +namespace Barta::Dynamics::SoftBody { + +// constexpr PrecisionType MU = 5300.f; +// constexpr PrecisionType LAMBDA = 99500.f; + +constexpr PrecisionType MU = 100.f; +constexpr PrecisionType LAMBDA = 1000.f; +// constexpr PrecisionType MU = 10.f; +// constexpr PrecisionType LAMBDA = 100.f; + +using Utils::Matrix3Type; +using Utils::Matrix9Type; + +StiffnessMatrixType StVenantKirchhoffDynamics::assembleTangentStiffnessMatrix( + const Objects::Soft::Mesh& mesh, + const NodalVectorType& nodesPositions +) { + StiffnessMatrixType tangentStiffnessMatrix = StiffnessMatrixType::Zero(nodesPositions.size(), nodesPositions.size()); + for (const auto& e: mesh.elements) { + Matrix3Type D_e_star_inv = e.getInverseReferenceShapeMatrix(); + Matrix3Type D_e = e.calculateShapeMatrix(mesh.nodes, nodesPositions); + Matrix3Type F_e = D_e * D_e_star_inv; + Matrix9Type H9 = Matrix9Type::Zero(); + for (int j = 0; j < 9; j++) { + Matrix3Type delta_D_e = Matrix3Type::Zero(); + delta_D_e(j % 3, j / 3) = 1.; + Matrix3Type delta_F_e = delta_D_e * D_e_star_inv; + + Matrix3Type term1 = MU * delta_F_e * (F_e.transpose() * F_e - 0.5 * Matrix3Type::Identity()); + Matrix3Type term2 = MU * F_e * delta_F_e.transpose() * F_e; + Matrix3Type term3 = MU * (F_e * F_e.transpose() - 0.5 * Matrix3Type::Identity()) * delta_F_e; + Matrix3Type term4 = 0.5 * LAMBDA * (F_e.transpose() * F_e - Matrix3Type::Identity()).trace() * delta_F_e; + Matrix3Type term5 = LAMBDA * (F_e.transpose() * delta_F_e).trace() * F_e; + + Matrix3Type dP = -(term1 + term2 + term3 + term4 + term5) * D_e_star_inv.transpose() / (6. * std::abs(D_e_star_inv.determinant())); + for (int i = 0; i < 9; i++) { + H9(i, j) = dP(i % 3, i / 3); + } + } + + SoftBodyDynamicsInterface::emplace12Matrix( + Utils::create12MatrixFrom9MatrixAndThirdNewtonLaw(H9), + e.getNodeIndices(), + tangentStiffnessMatrix, + mesh.nodes.size() - mesh.dirichletNodeCount + ); + } + + return tangentStiffnessMatrix; +} + +NodalVectorType StVenantKirchhoffDynamics::calculateNodalForces( + const Objects::Soft::Mesh& mesh, + const NodalVectorType& nodesPositions +) { + NodalVectorType forces = NodalVectorType::Zero(nodesPositions.size()); + for (const auto& e: mesh.elements) { + Matrix3Type D_e_star_inv = e.getInverseReferenceShapeMatrix(); + Matrix3Type D_e_star_inv_T = D_e_star_inv.transpose(); + Matrix3Type F_e = e.calculateShapeMatrix(mesh.nodes, nodesPositions) * D_e_star_inv; + Matrix3Type E_e = 0.5 * (F_e.transpose() * F_e - Matrix3Type::Identity()); + Matrix3Type P_e = F_e * (2. * MU * E_e + LAMBDA * E_e.trace() * Matrix3Type::Identity()); + Matrix3Type H = -(P_e * D_e_star_inv_T) / (6. * std::abs(D_e_star_inv.determinant())); + const auto& nodeIndices = e.getNodeIndices(); + Eigen::Vector3 force4 = Eigen::Vector3::Zero(); + for (int i = 0; i < 3; ++i) { + force4 += H.block<3, 1>(0, i); + if (3 * nodeIndices[i] >= nodesPositions.size()) { + continue; + } + + forces.block<3, 1>(3 * nodeIndices[i], 0) += H.block<3, 1>(0, i); + } + + if (3 * nodeIndices[3] >= nodesPositions.size()) { + continue; + } + + forces.block<3, 1>(3 * nodeIndices[3], 0) -= force4; + } + + return forces; +} +} diff --git a/lib/Dynamics/UpdateStrategy/CMakeLists.txt b/lib/Dynamics/UpdateStrategy/CMakeLists.txt new file mode 100644 index 0000000..3fc6e64 --- /dev/null +++ b/lib/Dynamics/UpdateStrategy/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${LIBRARY_NAME} PUBLIC + SoftBodyStrategy.cpp +) diff --git a/lib/Dynamics/UpdateStrategy/SoftBodyStrategy.cpp b/lib/Dynamics/UpdateStrategy/SoftBodyStrategy.cpp new file mode 100644 index 0000000..9c5a2c8 --- /dev/null +++ b/lib/Dynamics/UpdateStrategy/SoftBodyStrategy.cpp @@ -0,0 +1,118 @@ +#include +#include "Dynamics/Mass/ConstantPointMassDistributionCalculator.h" +#include "Dynamics/UpdateStrategy/ExplicitEulerStrategy.h" + +namespace Barta::Dynamics::UpdateStrategy { + +constexpr unsigned int MAX_ITERATIONS = 1000; + +SoftBodyStrategy::SoftBodyStrategy( + std::unique_ptr softBodyDynamics, + std::unique_ptr massCalculator +) noexcept: + softBodyDynamics(std::move(softBodyDynamics)), + massCalculator(std::move(massCalculator)) {} + +template<> +void Barta::Dynamics::UpdateStrategy::SoftBodyStrategy::prepare( + Objects::Soft::SoftObject& object, + float time +) { + auto& mesh = object.getMesh(); + const auto NODAL_VECTOR_SIZE = mesh.getNodalVectorSize(); + SoftBody::NodalVectorType initialPositions = SoftBody::NodalVectorType::Zero(NODAL_VECTOR_SIZE); + SoftBody::NodalVectorType initialVelocities = SoftBody::NodalVectorType::Zero(NODAL_VECTOR_SIZE); + unsigned int i = 0; + for (const auto& node: mesh.nodes) { + if (node.isZeroDirichlet) { + break; + } + + const auto& dynamicsData = node.dynamicsDTOCollection.dynamicsDTOs[DynamicsDTOIteration::CURRENT]; + + initialPositions[3 * i] = dynamicsData.massCenter.x(); + initialPositions[3 * i + 1] = dynamicsData.massCenter.y(); + initialPositions[3 * i + 2] = dynamicsData.massCenter.z(); + + initialVelocities[3 * i] = dynamicsData.velocity.x(); + initialVelocities[3 * i + 1] = dynamicsData.velocity.y(); + initialVelocities[3 * i + 2] = dynamicsData.velocity.z(); + + ++i; + } + + SoftBody::NodalVectorType positions = initialPositions; + SoftBody::StiffnessMatrixType massMatrixInverse = this->massCalculator->assembleInverseMassMatrix(mesh, positions); + auto iterCounter = 0; + while (true) { + ++iterCounter; + if (iterCounter >= MAX_ITERATIONS) { + throw std::runtime_error("Iteration count exceeded"); + } + + SoftBody::StiffnessMatrixType A = -time * massMatrixInverse * this->softBodyDynamics->assembleTangentStiffnessMatrix(mesh, positions) + + SoftBody::StiffnessMatrixType::Identity(NODAL_VECTOR_SIZE, NODAL_VECTOR_SIZE) / time; + SoftBody::NodalVectorType b = initialVelocities + (initialPositions - positions) / time + + time * massMatrixInverse * this->softBodyDynamics->calculateNodalForces(mesh, positions); + SoftBody::NodalVectorType delta_positions = A.colPivHouseholderQr().solve(b); + + bool precisionReached = true; + float maxEr = 0.f; + for (const auto& u_i: delta_positions) { + if (std::abs(u_i) > maxEr) { + maxEr = std::abs(u_i); + } + + if (std::abs(u_i) > 1e-4) { + precisionReached = false; + } + } + + positions += delta_positions; + if (precisionReached) { + break; + } + } + + SoftBody::NodalVectorType velocities = (positions - initialPositions) / time; + + ExplicitEulerStrategy().prepare(object, time); + i = 0; + for (auto& node: mesh.nodes) { + if (node.isZeroDirichlet) { + continue; + } + + auto& dynamicsData = node.dynamicsDTOCollection.dynamicsDTOs[DynamicsDTOIteration::NEXT]; + + dynamicsData.massCenter.x() = positions[3 * i]; + dynamicsData.massCenter.y() = positions[3 * i + 1]; + dynamicsData.massCenter.z() = positions[3 * i + 2]; + + dynamicsData.velocity.x() = velocities[3 * i]; + dynamicsData.velocity.y() = velocities[3 * i + 1]; + dynamicsData.velocity.z() = velocities[3 * i + 2]; + dynamicsData.velocity = dynamicsData.velocity.zeroised(); + + ++i; + } + + this->massCalculator->prepareMassDistribution(object); +} + +template<> +void Barta::Dynamics::UpdateStrategy::SoftBodyStrategy::update( + Objects::Soft::SoftObject& object, + bool doForward +) { + ExplicitEulerStrategy().update(object, doForward); + if (!doForward) { + return; + } + + for (auto& node: object.getMesh().nodes) { + node.dynamicsDTOCollection.forward(); + } +} + +} diff --git a/lib/Geometrics/Transformation.cpp b/lib/Geometrics/Transformation.cpp index 74aa542..062b377 100644 --- a/lib/Geometrics/Transformation.cpp +++ b/lib/Geometrics/Transformation.cpp @@ -51,7 +51,7 @@ Matrix Transformation::rotation( R(1, 0) = (1 - cos) * x * y + sin * z; R(1, 1) = cos + (1 - cos) * y * y; - R(2, 1) = (1 - cos) * y * z - sin * x; + R(1, 2) = (1 - cos) * y * z - sin * x; R(2, 0) = (1 - cos) * x * z - sin * y; R(2, 1) = (1 - cos) * y * z + sin * x; diff --git a/lib/Graphics/OpenGL_Bridge.cpp b/lib/Graphics/OpenGL_Bridge.cpp index 5ea9f94..ce39720 100644 --- a/lib/Graphics/OpenGL_Bridge.cpp +++ b/lib/Graphics/OpenGL_Bridge.cpp @@ -65,7 +65,7 @@ void OpenGL_Bridge::createWindow( this->attributeArray = std::make_unique(); this->PB_uniformBuffer = std::make_unique(UniformBinding::VIEW_PROJECTION_MATRIX); - this->projection = Transformation::perspective(0.1f, 2000.f, 1.f, std::tan(M_PI / 4.f)) * Transformation::Identity(); + this->projection = Transformation::perspective(0.1f, 2000.f, size.y / size.x, std::tan(M_PI / 4.f)) * Transformation::Identity(); glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); @@ -75,6 +75,7 @@ void OpenGL_Bridge::createWindow( void OpenGL_Bridge::drawObjects( std::list& objects ) { + auto xd = 0; for (const auto& object: objects) { for (auto graphicsData_ptr: object->getGraphicsData()) { if (graphicsData_ptr->resource.getResourceId() != 0) { @@ -121,6 +122,7 @@ void OpenGL_Bridge::drawObjects( this->vertexArray->addTrianglePrimitive( {initialIndex, static_cast(initialIndex + 1u), static_cast(initialIndex + 2)} ); + xd++; } else if (type == SpriteType::RECTANGLE_WITH_COLORS) { Point p1(data[0], data[1], data[2]); Point p2(data[0] + data[3], data[1], data[2]); diff --git a/lib/Graphics/OpenGL_Bridge/AttributeArrayGuard.cpp b/lib/Graphics/OpenGL_Bridge/AttributeArrayGuard.cpp index 542fef5..e3c8a56 100644 --- a/lib/Graphics/OpenGL_Bridge/AttributeArrayGuard.cpp +++ b/lib/Graphics/OpenGL_Bridge/AttributeArrayGuard.cpp @@ -24,7 +24,7 @@ AttributeArrayGuard::AttributeArrayGuard( AttributeArrayGuard&& other ): attributeArray(other.attributeArray) { - other.moved -= true; + other.moved = true; } void AttributeArrayGuard::defineAttributePointer() const { @@ -40,7 +40,7 @@ void AttributeArrayGuard::defineAttributePointer() const { reinterpret_cast(offset) ); - offset += sizeof(Point); + offset += 4 * sizeof(float); glEnableVertexAttribArray(static_cast(AttributeArray::AttributeEnum::COLOR)); glVertexAttribPointer( static_cast(AttributeArray::AttributeEnum::COLOR), diff --git a/lib/Graphics/OpenGL_Bridge/PV_BufferGuard.cpp b/lib/Graphics/OpenGL_Bridge/PV_BufferGuard.cpp index d959266..bb970e1 100644 --- a/lib/Graphics/OpenGL_Bridge/PV_BufferGuard.cpp +++ b/lib/Graphics/OpenGL_Bridge/PV_BufferGuard.cpp @@ -21,7 +21,7 @@ PV_BufferGuard::PV_BufferGuard( void PV_BufferGuard::bufferData( const Matrix& transformation ) const { - constexpr unsigned int BUFFER_SIZE = 16 * sizeof(PrecisionType); + constexpr unsigned int BUFFER_SIZE = 16 * sizeof(float); glBufferData(GL_UNIFORM_BUFFER, BUFFER_SIZE, nullptr, GL_STATIC_DRAW); auto error = glGetError(); @@ -29,7 +29,12 @@ void PV_BufferGuard::bufferData( throw std::runtime_error("Uniform buffer allocation error: " + error); } - glBufferSubData(GL_UNIFORM_BUFFER, 0, BUFFER_SIZE, &transformation); + std::vector data(16); + for (unsigned int i = 0; i < 16; ++i) { + data[i] = transformation(i % 4, i / 4); + } + + glBufferSubData(GL_UNIFORM_BUFFER, 0, BUFFER_SIZE, data.data()); error = glGetError(); if (error != GL_NO_ERROR) { throw std::runtime_error("Uniform buffer data loading error: " + error); diff --git a/lib/Graphics/SpriteBuilder/SpriteMerger.cpp b/lib/Graphics/SpriteBuilder/SpriteMerger.cpp index bd55625..8d52c7b 100644 --- a/lib/Graphics/SpriteBuilder/SpriteMerger.cpp +++ b/lib/Graphics/SpriteBuilder/SpriteMerger.cpp @@ -75,9 +75,9 @@ Barta::SpriteMerger* Barta::SpriteMerger::addTriangle( this->types.push_back(SpriteType::TRIANGLE); for (const auto& vector: {triangleSprite.triangle.p1, triangleSprite.triangle.p2, triangleSprite.triangle.p3}) { - this->data.push_back(vector.x()); - this->data.push_back(vector.y()); - this->data.push_back(vector.z()); + this->data.push_back(static_cast(vector.x())); + this->data.push_back(static_cast(vector.y())); + this->data.push_back(static_cast(vector.z())); } for (const auto& color: {triangleSprite.color1, triangleSprite.color2, triangleSprite.color3}) { diff --git a/lib/Objects/Soft/CMakeLists.txt b/lib/Objects/Soft/CMakeLists.txt index b26e36c..009b2a0 100644 --- a/lib/Objects/Soft/CMakeLists.txt +++ b/lib/Objects/Soft/CMakeLists.txt @@ -4,4 +4,5 @@ target_sources(${LIBRARY_NAME} PUBLIC SoftObject.cpp ) -add_subdirectory(MeshLoader) +add_subdirectory(MshParser) +add_subdirectory(MshLoader) diff --git a/lib/Objects/Soft/Mesh.cpp b/lib/Objects/Soft/Mesh.cpp index 0ff8d72..76ee1ba 100644 --- a/lib/Objects/Soft/Mesh.cpp +++ b/lib/Objects/Soft/Mesh.cpp @@ -6,6 +6,7 @@ Mesh::Mesh( std::vector elements ) noexcept: nodes(std::move(nodes)), - elements(std::move(elements)) {} + elements(std::move(elements)), + dirichletNodeCount(0) {} } diff --git a/lib/Objects/Soft/MeshLoader/MshDataLoader.cpp b/lib/Objects/Soft/MeshLoader/MshDataLoader.cpp deleted file mode 100644 index 6dbdc74..0000000 --- a/lib/Objects/Soft/MeshLoader/MshDataLoader.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include - -namespace Barta::Objects::Soft::MeshLoader { -MshData MshDataLoader::loadMshDataFromFile( - const std::filesystem::path& filePath -) const { - std::ifstream infile(filePath.string()); - if (!infile) { - throw std::runtime_error("Failed to open file: " + filePath.string()); - } - - std::string line; - unsigned int nextNodeIndex = 0; - MshData mshData; - while (std::getline(infile, line)) { - if (line == "$MeshFormat") { - std::string version; - size_t s1, s2; - infile >> version >> s1 >> s2; - - if (std::find(MshDataLoader::ALLOWED_VERSIONS.begin(), MshDataLoader::ALLOWED_VERSIONS.end(), version) - == MshDataLoader::ALLOWED_VERSIONS.end()) { - throw std::runtime_error("The msh file has an incompatible version: " + version); - } - - mshData.version = version; - - std::getline(infile, line); - } - - if (line == "$Nodes") { - size_t numEntityBlocks, totalNumNodes, minNodeTag, maxNodeTag; - infile >> numEntityBlocks >> totalNumNodes >> minNodeTag >> maxNodeTag; - std::getline(infile, line); - - for (size_t i = 0; i < numEntityBlocks; ++i) { - size_t entityDim, entityTag, parametric, numNodesInBlock; - infile >> entityDim >> entityTag >> parametric >> numNodesInBlock; - - std::vector nodeTags(numNodesInBlock); - for (size_t j = 0; j < numNodesInBlock; ++j) { - infile >> nodeTags[j]; - ++nextNodeIndex; - } - - for (size_t j = 0; j < numNodesInBlock; ++j) { - float x, y, z; - infile >> x >> y >> z; - mshData.nodes.emplace_back(nodeTags[j], x, y, z); - } - } - } - - else if (line == "$Elements") { - size_t numEntityBlocks, totalNumElements, minElemTag, maxElemTag; - infile >> numEntityBlocks >> totalNumElements >> minElemTag >> maxElemTag; - std::getline(infile, line); - - for (size_t i = 0; i < numEntityBlocks; ++i) { - size_t entityDim, entityTag, elementType, numElemsInBlock; - infile >> entityDim >> entityTag >> elementType >> numElemsInBlock; - std::getline(infile, line); - - if (elementType != 4) { - for (size_t j = 0; j < numElemsInBlock; ++j) { - std::getline(infile, line); - } - - continue; - } - - for (size_t j = 0; j < numElemsInBlock; ++j) { - std::array nodeTags = {}; - infile >> line >> nodeTags[0] >> nodeTags[1] >> nodeTags[2] >> nodeTags[3]; - mshData.elements.emplace_back(MshData::ElementType::TETRAHEDRON, std::move(nodeTags)); - } - } - } - } - - if (mshData.version == "") { - throw std::runtime_error("Could not extract msh version. Check if the file contains a correct .msh file structure."); - } - - return mshData; -} -} diff --git a/lib/Objects/Soft/MeshLoader/MshDataParser.cpp b/lib/Objects/Soft/MeshLoader/MshDataParser.cpp deleted file mode 100644 index e630485..0000000 --- a/lib/Objects/Soft/MeshLoader/MshDataParser.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include "Geometrics/BartaShapes/Triangle.h" -#include "Geometrics/ConvexFactor.h" - -namespace Barta::Objects::Soft::MeshLoader { -Mesh MshDataParser::parse( - const MshData& mshData -) const { - std::map nodeTagToIndex; // maps Gmsh node ID → index in vector - std::vector nodes; - std::vector tetrahedralElements; - for (size_t i = 0; i < mshData.nodes.size(); ++i) { - nodeTagToIndex[mshData.nodes[i].tag] = i; - - DynamicsDTO dynamics; - dynamics.massCenter = {mshData.nodes[i].x, mshData.nodes[i].y, mshData.nodes[i].z}; - nodes.emplace_back(std::to_string(mshData.nodes[i].tag), DynamicsDTOCollection{dynamics}, false); - } - - for (size_t i = 0; i < mshData.elements.size(); ++i) { - if (mshData.elements[i].type != MshData::ElementType::TETRAHEDRON) { - continue; - } - - auto nodeTags = mshData.elements[i].nodeIndices; - std::array positions; - for (size_t k = 0; k < 4; ++k) { - positions[k] = nodes[nodeTagToIndex[nodeTags[k]]].dynamicsDTOCollection[DynamicsDTOIteration::CURRENT].massCenter; - } - - Point tetrahedralCenter = ConvexFactor::baricentricCombination(positions); - auto faceIndices = TetrahedralElement::FACE_INDICES_CONTAINER[0]; - auto normal = Triangle{positions[faceIndices[0]], positions[faceIndices[1]], positions[faceIndices[2]]}.getNormal(); - - Point faceCenter = ConvexFactor::baricentricCombination( - std::initializer_list{positions[faceIndices[0]], positions[faceIndices[1]], positions[faceIndices[2]]} - ); - - if ((tetrahedralCenter - faceCenter).dot(normal) > 0.) { - std::swap(nodeTags[1], nodeTags[2]); - } - - tetrahedralElements.emplace_back( - nodes[nodeTagToIndex[nodeTags[0]]], - nodes[nodeTagToIndex[nodeTags[1]]], - nodes[nodeTagToIndex[nodeTags[2]]], - nodes[nodeTagToIndex[nodeTags[3]]] - ); - } - - return Mesh(std::move(nodes), std::move(tetrahedralElements)); -} -} diff --git a/lib/Objects/Soft/MshLoader/CMakeLists.txt b/lib/Objects/Soft/MshLoader/CMakeLists.txt new file mode 100644 index 0000000..b562dd0 --- /dev/null +++ b/lib/Objects/Soft/MshLoader/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources(${LIBRARY_NAME} PUBLIC + NodesPlugin.cpp + EntitiesPlugin.cpp + ElementsPlugin.cpp + DirichletConditionPlugin.cpp + MeshFormatPlugin.cpp + MshDataLoader.cpp +) diff --git a/lib/Objects/Soft/MshLoader/DirichletConditionPlugin.cpp b/lib/Objects/Soft/MshLoader/DirichletConditionPlugin.cpp new file mode 100644 index 0000000..9aac190 --- /dev/null +++ b/lib/Objects/Soft/MshLoader/DirichletConditionPlugin.cpp @@ -0,0 +1,22 @@ +#include + +namespace Barta::Objects::Soft::MshLoader { +void DirichletConditionPlugin::loadMshData( + std::istream& in, + MshData& mshData +) { + size_t n; + in >> n; + std::string dummy; + for (size_t i = 0; i < n; ++i) { + MshData::PhysicalGroup physicalGroup; + in >> physicalGroup.dim; + in >> physicalGroup.tag; + in >> physicalGroup.name; + + mshData.physicalGroups.push_back(std::move(physicalGroup)); + + std::getline(in, dummy); + } +} +} diff --git a/lib/Objects/Soft/MshLoader/ElementsPlugin.cpp b/lib/Objects/Soft/MshLoader/ElementsPlugin.cpp new file mode 100644 index 0000000..2dde64f --- /dev/null +++ b/lib/Objects/Soft/MshLoader/ElementsPlugin.cpp @@ -0,0 +1,41 @@ +#include + +namespace Barta::Objects::Soft::MshLoader { +void ElementsPlugin::loadMshData( + std::istream& in, + MshData& mshData +) { + std::string dummy; + size_t numEntityBlocks, totalNumElements, minElemTag, maxElemTag; + in >> numEntityBlocks >> totalNumElements >> minElemTag >> maxElemTag; + std::getline(in, dummy); + + for (size_t i = 0; i < numEntityBlocks; ++i) { + size_t entityDim, entityTag, elementType, numElemsInBlock; + in >> entityDim >> entityTag >> elementType >> numElemsInBlock; + std::getline(in, dummy); + + if (elementType == static_cast(MshData::ElementType::TRIANGLE)) { + for (size_t j = 0; j < numElemsInBlock; ++j) { + unsigned int elementTag; + std::array nodeTags = {}; + in >> elementTag >> nodeTags[0] >> nodeTags[1] >> nodeTags[2]; + + mshData.triangleElements.emplace_back(entityTag, nodeTags); + } + } else if (elementType == static_cast(MshData::ElementType::TETRAHEDRON)) { + for (size_t j = 0; j < numElemsInBlock; ++j) { + unsigned int elementTag; + std::array nodeTags = {}; + in >> elementTag >> nodeTags[0] >> nodeTags[1] >> nodeTags[2] >> nodeTags[3]; + + mshData.tetrahedralElements.emplace_back(nodeTags); + } + } else { + for (size_t j = 0; j < numElemsInBlock; ++j) { + std::getline(in, dummy); + } + } + } +} +} diff --git a/lib/Objects/Soft/MshLoader/EntitiesPlugin.cpp b/lib/Objects/Soft/MshLoader/EntitiesPlugin.cpp new file mode 100644 index 0000000..7a78515 --- /dev/null +++ b/lib/Objects/Soft/MshLoader/EntitiesPlugin.cpp @@ -0,0 +1,41 @@ +#include + +namespace Barta::Objects::Soft::MshLoader { +void EntitiesPlugin::loadMshData( + std::istream& in, + MshData& mshData +) { + std::string dummy; + size_t n0, n1, n2, n3; + in >> n0 >> n1 >> n2 >> n3; + for (unsigned int i = 0; i < n0; i++) { + std::getline(in, dummy); // I don't need it so far + } + + for (unsigned int i = 0; i < n1; i++) { + std::getline(in, dummy); // I don't need it so far + } + + for (unsigned int i = 0; i < n2; i++) { + MshData::Entity entity; + in >> entity.tag; + in >> entity.x_min >> entity.y_min >> entity.z_min; + in >> entity.x_max >> entity.y_max >> entity.z_max; + size_t numberOfGroups; + in >> numberOfGroups; + for (unsigned int j = 0; j < numberOfGroups; j++) { + unsigned int groupTag; + in >> groupTag; + entity.physicalGroupTags.push_back(groupTag); + } + + mshData.entities[MshData::EntityDimension::SURFACE].push_back(entity); + + std::getline(in, dummy); + } + + for (unsigned int i = 0; i < n3; i++) { + std::getline(in, dummy); // I don't need it so far + } +} +} diff --git a/lib/Objects/Soft/MshLoader/MeshFormatPlugin.cpp b/lib/Objects/Soft/MshLoader/MeshFormatPlugin.cpp new file mode 100644 index 0000000..bc2d941 --- /dev/null +++ b/lib/Objects/Soft/MshLoader/MeshFormatPlugin.cpp @@ -0,0 +1,21 @@ +#include + +namespace Barta::Objects::Soft::MshLoader { +void MeshFormatPlugin::loadMshData( + std::istream& in, + MshData& mshData +) { + std::string version; + size_t s1, s2; + in >> version >> s1 >> s2; + + if (std::ranges::find(MeshFormatPlugin::ALLOWED_VERSIONS, version) == MeshFormatPlugin::ALLOWED_VERSIONS.end()) { + throw std::runtime_error("The msh file has an incompatible version: " + version); + } + + mshData.version = version; + + std::string dummy; + std::getline(in, dummy); +} +} diff --git a/lib/Objects/Soft/MshLoader/MshDataLoader.cpp b/lib/Objects/Soft/MshLoader/MshDataLoader.cpp new file mode 100644 index 0000000..d60a3c2 --- /dev/null +++ b/lib/Objects/Soft/MshLoader/MshDataLoader.cpp @@ -0,0 +1,36 @@ +#include + +namespace Barta::Objects::Soft::MshLoader { +MshData MshDataLoader::loadMshDataFromFile( + const std::filesystem::path& filePath +) const { + std::ifstream infile(filePath.string()); + if (!infile) { + throw std::runtime_error("Failed to open file: " + filePath.string()); + } + + std::string line; + MshData mshData; + while (std::getline(infile, line)) { + for (const auto& plugin: this->plugins) { + if (line == plugin->getSectionKey()) { + plugin->loadMshData(infile, mshData); + + break; + } + } + } + + if (mshData.version == "") { + throw std::runtime_error("Could not extract msh version. Check if the file contains a correct .msh file structure."); + } + + return mshData; +} + +void MshDataLoader::registerPlugin( + std::unique_ptr plugin +) { + plugins.push_back(std::move(plugin)); +} +} diff --git a/lib/Objects/Soft/MshLoader/NodesPlugin.cpp b/lib/Objects/Soft/MshLoader/NodesPlugin.cpp new file mode 100644 index 0000000..eacb465 --- /dev/null +++ b/lib/Objects/Soft/MshLoader/NodesPlugin.cpp @@ -0,0 +1,29 @@ +#include + +namespace Barta::Objects::Soft::MshLoader { +void NodesPlugin::loadMshData( + std::istream& in, + MshData& mshData +) { + std::string dummy; + size_t numEntityBlocks, totalNumNodes, minNodeTag, maxNodeTag; + in >> numEntityBlocks >> totalNumNodes >> minNodeTag >> maxNodeTag; + std::getline(in, dummy); + + for (size_t i = 0; i < numEntityBlocks; ++i) { + size_t entityDim, entityTag, parametric, numNodesInBlock; + in >> entityDim >> entityTag >> parametric >> numNodesInBlock; + + std::vector nodeTags(numNodesInBlock); + for (size_t j = 0; j < numNodesInBlock; ++j) { + in >> nodeTags[j]; + } + + for (size_t j = 0; j < numNodesInBlock; ++j) { + float x, y, z; + in >> x >> y >> z; + mshData.nodes.emplace_back(nodeTags[j], x, y, z); + } + } +} +} diff --git a/lib/Objects/Soft/MeshLoader/CMakeLists.txt b/lib/Objects/Soft/MshParser/CMakeLists.txt similarity index 53% rename from lib/Objects/Soft/MeshLoader/CMakeLists.txt rename to lib/Objects/Soft/MshParser/CMakeLists.txt index ad68add..469b365 100644 --- a/lib/Objects/Soft/MeshLoader/CMakeLists.txt +++ b/lib/Objects/Soft/MshParser/CMakeLists.txt @@ -1,5 +1,6 @@ target_sources(${LIBRARY_NAME} PUBLIC - SurfaceFacesMshParserDecorator.cpp + DirichletConditionParserDecorator.cpp MshDataParser.cpp - MshDataLoader.cpp + SurfaceFacesMshParserDecorator.cpp + TetrahedralElementLoaderDecoratorTest.cpp ) diff --git a/lib/Objects/Soft/MshParser/DirichletConditionParserDecorator.cpp b/lib/Objects/Soft/MshParser/DirichletConditionParserDecorator.cpp new file mode 100644 index 0000000..aa52f99 --- /dev/null +++ b/lib/Objects/Soft/MshParser/DirichletConditionParserDecorator.cpp @@ -0,0 +1,75 @@ +#include + +namespace Barta::Objects::Soft::MshParser { +DirichletConditionParserDecorator::DirichletConditionParserDecorator( + MshDataParserInterface& decorated +) noexcept: + decorated(decorated) {} + +Mesh DirichletConditionParserDecorator::parse( + const MshData& mshData +) const { + auto mesh = this->decorated.parse(mshData); + + unsigned int dirichletConditionGroupTag = 0; + for (const auto& physicalGroup: mshData.physicalGroups) { + if (physicalGroup.name != "\"DirichletCondition\"") { + continue; + } + + dirichletConditionGroupTag = physicalGroup.tag; + + break; + } + + if (dirichletConditionGroupTag == 0) { + return mesh; + } + + std::vector entityTagsInDirichletGroup; + for (const auto& entity: mshData.entities.at(MshData::EntityDimension::SURFACE)) { + for (const auto& entityPhysicalGroupTag: entity.physicalGroupTags) { + if (entityPhysicalGroupTag != dirichletConditionGroupTag) { + continue; + } + + entityTagsInDirichletGroup.push_back(entity.tag); + } + } + + std::vector dirichletNodeTags; + for (const auto& element: mshData.triangleElements) { + if (std::ranges::find(entityTagsInDirichletGroup, element.entityTag) != entityTagsInDirichletGroup.end()) { + for (const auto& elementNodeTag: element.nodeTags) { + dirichletNodeTags.push_back(elementNodeTag); + } + } + } + + std::ranges::sort(dirichletNodeTags); + const auto toErase = std::ranges::unique(dirichletNodeTags); + dirichletNodeTags.erase(toErase.begin(), toErase.end()); + unsigned int dirichletNodeCount = 0; + std::vector freeNodes; + std::vector dirichletNodes; + for (auto& node: mesh.nodes) { + if (std::ranges::binary_search(dirichletNodeTags, node.tag) && !node.isZeroDirichlet) { + node.isZeroDirichlet = true; + + ++dirichletNodeCount; + } + + if (node.isZeroDirichlet) { + dirichletNodes.push_back(node); + } else { + freeNodes.push_back(node); + } + } + + mesh.dirichletNodeCount += dirichletNodeCount; + std::ranges::move(dirichletNodes, std::back_inserter(freeNodes)); + mesh.nodes = freeNodes; + + return mesh; +} +} diff --git a/lib/Objects/Soft/MshParser/MshDataParser.cpp b/lib/Objects/Soft/MshParser/MshDataParser.cpp new file mode 100644 index 0000000..9c4f6d6 --- /dev/null +++ b/lib/Objects/Soft/MshParser/MshDataParser.cpp @@ -0,0 +1,24 @@ +#include +#include "Geometrics/BartaShapes/Triangle.h" +#include +#include + +namespace Barta::Objects::Soft::MshParser { +Mesh MshDataParser::parse( + const MshParser::MshData& mshData +) const { + std::vector nodes; + NodalVectorType nodePositions(3 * mshData.nodes.size()); + for (size_t i = 0; i < mshData.nodes.size(); ++i) { + DynamicsDTO dynamics; + dynamics.massCenter = {mshData.nodes[i].x, mshData.nodes[i].y, mshData.nodes[i].z}; + nodePositions[3 * i] = mshData.nodes[i].x; + nodePositions[3 * i + 1] = mshData.nodes[i].y; + nodePositions[3 * i + 2] = mshData.nodes[i].z; + + nodes.emplace_back(DynamicsDTOCollection{dynamics}, mshData.nodes[i].tag, false); + } + + return {std::move(nodes), {}}; +} +} diff --git a/lib/Objects/Soft/MeshLoader/SurfaceFacesMshParserDecorator.cpp b/lib/Objects/Soft/MshParser/SurfaceFacesMshParserDecorator.cpp similarity index 58% rename from lib/Objects/Soft/MeshLoader/SurfaceFacesMshParserDecorator.cpp rename to lib/Objects/Soft/MshParser/SurfaceFacesMshParserDecorator.cpp index d43b78d..06dc604 100644 --- a/lib/Objects/Soft/MeshLoader/SurfaceFacesMshParserDecorator.cpp +++ b/lib/Objects/Soft/MshParser/SurfaceFacesMshParserDecorator.cpp @@ -1,6 +1,6 @@ -#include +#include -namespace Barta::Objects::Soft::MeshLoader { +namespace Barta::Objects::Soft::MshParser { SurfaceFacesMshParserDecorator::SurfaceFacesMshParserDecorator( MshDataParserInterface& decorated ) noexcept: @@ -11,13 +11,13 @@ Mesh SurfaceFacesMshParserDecorator::parse( ) const { auto mesh = this->decorated.parse(mshData); - std::map, std::pair> facesFound; + std::map, std::pair> facesFound; for (auto& elem: mesh.elements) { for (unsigned char i = 0; i < TetrahedralElement::FACE_INDICES_CONTAINER.size(); ++i) { - std::array faceIndexPointers; - faceIndexPointers[0] = &elem[TetrahedralElement::FACE_INDICES_CONTAINER[i][0]]; - faceIndexPointers[1] = &elem[TetrahedralElement::FACE_INDICES_CONTAINER[i][1]]; - faceIndexPointers[2] = &elem[TetrahedralElement::FACE_INDICES_CONTAINER[i][2]]; + std::array faceIndexPointers; + faceIndexPointers[0] = elem.getNodeIndices()[TetrahedralElement::FACE_INDICES_CONTAINER[i][0]]; + faceIndexPointers[1] = elem.getNodeIndices()[TetrahedralElement::FACE_INDICES_CONTAINER[i][1]]; + faceIndexPointers[2] = elem.getNodeIndices()[TetrahedralElement::FACE_INDICES_CONTAINER[i][2]]; std::sort(faceIndexPointers.begin(), faceIndexPointers.end()); if (facesFound.contains(faceIndexPointers)) { facesFound.erase(faceIndexPointers); diff --git a/lib/Objects/Soft/MshParser/TetrahedralElementLoaderDecoratorTest.cpp b/lib/Objects/Soft/MshParser/TetrahedralElementLoaderDecoratorTest.cpp new file mode 100644 index 0000000..30be378 --- /dev/null +++ b/lib/Objects/Soft/MshParser/TetrahedralElementLoaderDecoratorTest.cpp @@ -0,0 +1,56 @@ +#include +#include "Geometrics/BartaShapes/Triangle.h" +#include "Geometrics/ConvexFactor.h" + +namespace Barta::Objects::Soft::MshParser { +TetrahedralElementLoaderDecorator::TetrahedralElementLoaderDecorator( + MshDataParserInterface& decorated +) noexcept: + decorated(decorated) {} + +Mesh TetrahedralElementLoaderDecorator::parse( + const MshData& mshData +) const { + auto mesh = this->decorated.parse(mshData); + + std::map nodeTagToIndex; // maps Gmsh node ID → index in vector + std::vector tetrahedralElements; + for (size_t i = 0; i < mesh.nodes.size(); ++i) { + nodeTagToIndex[mesh.nodes[i].tag] = i; + } + + for (auto tetrahedralElement: mshData.tetrahedralElements) { + auto nodeTags = tetrahedralElement.nodeTags; + std::array positions; + for (size_t k = 0; k < 4; ++k) { + positions[k] = mesh.nodes[nodeTagToIndex[nodeTags[k]]].dynamicsDTOCollection[DynamicsDTOIteration::CURRENT].massCenter; + } + + Point tetrahedralCenter = ConvexFactor::baricentricCombination(positions); + auto faceIndices = TetrahedralElement::FACE_INDICES_CONTAINER[0]; + auto normal = Triangle{positions[faceIndices[0]], positions[faceIndices[1]], positions[faceIndices[2]]}.getNormal(); + + Point faceCenter = ConvexFactor::baricentricCombination( + std::initializer_list{positions[faceIndices[0]], positions[faceIndices[1]], positions[faceIndices[2]]} + ); + + if ((tetrahedralCenter - faceCenter).dot(normal) > 0.) { + std::swap(nodeTags[1], nodeTags[2]); + } + + // clang-format off + tetrahedralElements.emplace_back( + mesh.nodes, + nodeTagToIndex[nodeTags[0]], + nodeTagToIndex[nodeTags[1]], + nodeTagToIndex[nodeTags[2]], + nodeTagToIndex[nodeTags[3]] + ); + // clang-format on + } + + mesh.elements = tetrahedralElements; + + return mesh; +} +} diff --git a/lib/Objects/Soft/SoftObject.cpp b/lib/Objects/Soft/SoftObject.cpp index 45c1c83..53b5ca6 100644 --- a/lib/Objects/Soft/SoftObject.cpp +++ b/lib/Objects/Soft/SoftObject.cpp @@ -47,19 +47,30 @@ GraphicsDataAwareInterface::GraphicsDataList SoftObject::getGraphicsData() { colors[3] = {0, 255, 255}; for (const auto& element: this->mesh.elements) { for (unsigned char i = 0; i < TetrahedralElement::FACE_INDICES_CONTAINER.size(); i++) { - if (!element.getIsFaceSurface(i)) { + if (!element.getIsFaceSurface(i)) { // possible optimization - loop through surface faces only continue; } SpriteBuilder builder; // clang-format off - builder.vertex1 = element[TetrahedralElement::FACE_INDICES_CONTAINER[i][0]].dynamicsDTOCollection[DynamicsDTOIteration::CURRENT].massCenter; - builder.vertex2 = element[TetrahedralElement::FACE_INDICES_CONTAINER[i][1]].dynamicsDTOCollection[DynamicsDTOIteration::CURRENT].massCenter; - builder.vertex3 = element[TetrahedralElement::FACE_INDICES_CONTAINER[i][2]].dynamicsDTOCollection[DynamicsDTOIteration::CURRENT].massCenter; + builder.vertex1 = this->mesh.nodes[element.getNodeIndices()[TetrahedralElement::FACE_INDICES_CONTAINER[i][0]]].dynamicsDTOCollection[DynamicsDTOIteration::CURRENT].massCenter; + builder.vertex2 = this->mesh.nodes[element.getNodeIndices()[TetrahedralElement::FACE_INDICES_CONTAINER[i][1]]].dynamicsDTOCollection[DynamicsDTOIteration::CURRENT].massCenter; + builder.vertex3 = this->mesh.nodes[element.getNodeIndices()[TetrahedralElement::FACE_INDICES_CONTAINER[i][2]]].dynamicsDTOCollection[DynamicsDTOIteration::CURRENT].massCenter; // clang-format on - builder.color = colors[i]; - merger.addTriangle(builder.buildTriangleSprite()); + // coloring TODO remove, this is just for tests + for (auto& [vertex, color]: std::vector>{ + {this->mesh.nodes[element.getNodeIndices()[TetrahedralElement::FACE_INDICES_CONTAINER[i][0]]].restPosition, builder.color1}, + {this->mesh.nodes[element.getNodeIndices()[TetrahedralElement::FACE_INDICES_CONTAINER[i][1]]].restPosition, builder.color2}, + {this->mesh.nodes[element.getNodeIndices()[TetrahedralElement::FACE_INDICES_CONTAINER[i][2]]].restPosition, builder.color3} + }) { + color.r = 255. * (20 + vertex.x()) / 40.; + color.g = 255. * (20 + vertex.y()) / 40.; + color.b = 255. * vertex.z() / 60.; + color.a = 0.; + } + + merger.addTriangle(builder.buildTriangleWithColorsSprite()); } } @@ -75,4 +86,12 @@ Vector SoftObject::getForce( return Vector::Zero(); } +Mesh& SoftObject::getMesh() noexcept { + return this->mesh; +} + +const Mesh& SoftObject::getMesh() const noexcept { + return this->mesh; +} + } diff --git a/lib/Objects/Soft/TetrahedralElement.cpp b/lib/Objects/Soft/TetrahedralElement.cpp index 8e5bcaf..5e3b991 100644 --- a/lib/Objects/Soft/TetrahedralElement.cpp +++ b/lib/Objects/Soft/TetrahedralElement.cpp @@ -1,14 +1,17 @@ #include namespace Barta::Objects::Soft { + TetrahedralElement::TetrahedralElement( - Node& n1, - Node& n2, - Node& n3, - Node& n4 + const std::vector& nodes, + unsigned int n1, + unsigned int n2, + unsigned int n3, + unsigned int n4 ) noexcept: - nodes({&n1, &n2, &n3, &n4}), - isFaceASurface({false, false, false, false}) {} + nodeIndices({n1, n2, n3, n4}), + isFaceASurface({false, false, false, false}), + referenceShapeMatrix(this->calculateShapeMatrix(nodes).inverse()) {} void TetrahedralElement::setIsFaceSurface( unsigned char index, @@ -23,9 +26,46 @@ bool TetrahedralElement::getIsFaceSurface( return this->isFaceASurface[index]; } -const Node& TetrahedralElement::operator[]( - unsigned char index -) const noexcept { - return *nodes[index]; +const std::array& TetrahedralElement::getNodeIndices() const noexcept { + return this->nodeIndices; +} + +Matrix3Type TetrahedralElement::getInverseReferenceShapeMatrix() const noexcept { + return this->referenceShapeMatrix; +} + +Eigen::Vector3 TetrahedralElement::retrieveNodePosition( + const std::vector& nodes, + const NodalVectorType& nodesPositions, + unsigned int i +) const { + if (3 * this->nodeIndices[i] >= nodesPositions.size()) { + Eigen::Vector3 u; + auto u_Dirichlet = nodes[this->nodeIndices[i]].dynamicsDTOCollection[DynamicsDTOIteration::CURRENT].massCenter; + + u.x() = u_Dirichlet.x(); + u.y() = u_Dirichlet.y(); + u.z() = u_Dirichlet.z(); + + return u; + } + + return nodesPositions.block<3, 1>(3 * this->nodeIndices[i], 0); +} + +Matrix3Type TetrahedralElement::calculateShapeMatrix( + const std::vector& nodes, + const NodalVectorType& nodesPositions +) const { + Matrix3Type result; + auto u_3 = this->retrieveNodePosition(nodes, nodesPositions, 3); + + for (unsigned int i = 0; i < 3; i++) { + auto u_i = this->retrieveNodePosition(nodes, nodesPositions, i); + + result.block<3, 1>(0, i) = u_i - u_3; + } + + return result; } } diff --git a/lib/Utilities/CMakeLists.txt b/lib/Utilities/CMakeLists.txt index e5f0492..8e92ac4 100644 --- a/lib/Utilities/CMakeLists.txt +++ b/lib/Utilities/CMakeLists.txt @@ -1,3 +1,4 @@ target_sources(${LIBRARY_NAME} PUBLIC + MatrixUtilities.cpp QuadraticEquation.cpp ) diff --git a/lib/Utilities/MatrixUtilities.cpp b/lib/Utilities/MatrixUtilities.cpp new file mode 100644 index 0000000..661e1c5 --- /dev/null +++ b/lib/Utilities/MatrixUtilities.cpp @@ -0,0 +1,24 @@ +#include + +namespace Barta::Utils { +Matrix12Type create12MatrixFrom9MatrixAndThirdNewtonLaw( + const Matrix9Type& m9 +) { + Matrix12Type m12 = Matrix12Type::Zero(); + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + auto currBlock = m9.block<3, 3>(3 * i, 3 * j); + // 9x9 left top + m12.block<3, 3>(3 * i, 3 * j) += currBlock; + // 3x9 left bottom + m12.block<3, 3>(3 * 3, 3 * j) -= currBlock; + // 9x3 right top + m12.block<3, 3>(3 * i, 3 * 3) -= currBlock; + // 3x3 right bottom + m12.block<3, 3>(3 * 3, 3 * 3) += currBlock; + } + } + + return m12; +} +} diff --git a/scripts/Matrices/latex.txt b/scripts/Matrices/latex.txt new file mode 100644 index 0000000..e69de29 diff --git a/scripts/Matrices/matrix_from_latex_to_white.py b/scripts/Matrices/matrix_from_latex_to_white.py new file mode 100644 index 0000000..2ff6258 --- /dev/null +++ b/scripts/Matrices/matrix_from_latex_to_white.py @@ -0,0 +1,16 @@ +import sys + +def convert_matrix(input_str) : +#Split by '\\' to get rows, then split each row by '&' and strip whitespace + rows = input_str.strip().split('\\\\') + matrix = [] + for row in rows: + elements =[elem.strip() for elem in row.strip().split('&')] + matrix.append(' '.join(elements)) + + return '\n'.join(matrix) + +if __name__ == "__main__" : + input_str = sys.stdin.read() + result = convert_matrix(input_str) + print(result) \ No newline at end of file diff --git a/scripts/Matrices/matrix_from_white_to_latex.py b/scripts/Matrices/matrix_from_white_to_latex.py new file mode 100644 index 0000000..ba2bd9d --- /dev/null +++ b/scripts/Matrices/matrix_from_white_to_latex.py @@ -0,0 +1,29 @@ +import sys +from fractions import Fraction + +def format_number(x): + f = float(Fraction(x)) + if f == 0.0: + return "0" + # Format to 5 decimal places and strip unnecessary zeros + formatted = f"{f:.5f}".rstrip('0').rstrip('.') + return formatted + +def convert_to_latex(input_str): + lines = input_str.strip().splitlines() + formatted_lines = [] + + for i, line in enumerate(lines): + elements = line.strip().split() + decimal_elements = [format_number(e) for e in elements] + formatted_line = ' & '.join(decimal_elements) + if i < len(lines) - 1: + formatted_line += r'\\' + formatted_lines.append(formatted_line) + + return '\n'.join(formatted_lines) + +if __name__ == "__main__": + input_str = sys.stdin.read() + result = convert_to_latex(input_str) + print(result) diff --git a/scripts/Matrices/white.txt b/scripts/Matrices/white.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..bd8490b --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,17 @@ +enable_testing() + +add_subdirectory(Dynamics) +add_subdirectory(Objects) +add_subdirectory(Utilities) + +target_link_libraries( + ${TESTS_TARGET_NAME} + ${LIBRARY_NAME} +) +target_link_libraries( + ${TESTS_TARGET_NAME} + GTest::gtest_main + GTest::gmock +) + +include(GoogleTest) diff --git a/test/Dynamics/CMakeLists.txt b/test/Dynamics/CMakeLists.txt new file mode 100644 index 0000000..65efd6e --- /dev/null +++ b/test/Dynamics/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(SoftBody) \ No newline at end of file diff --git a/test/Dynamics/SoftBody/CMakeLists.txt b/test/Dynamics/SoftBody/CMakeLists.txt new file mode 100644 index 0000000..357f928 --- /dev/null +++ b/test/Dynamics/SoftBody/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${TESTS_TARGET_NAME} PUBLIC + SoftBodyDynamicsTest.cpp + StVenantKirchhoffDynamicsTest.cpp +) \ No newline at end of file diff --git a/test/Dynamics/SoftBody/SoftBodyDynamicsTest.cpp b/test/Dynamics/SoftBody/SoftBodyDynamicsTest.cpp new file mode 100644 index 0000000..3a19166 --- /dev/null +++ b/test/Dynamics/SoftBody/SoftBodyDynamicsTest.cpp @@ -0,0 +1,58 @@ +#include +#include +#include + +using namespace Barta::Dynamics::SoftBody; + +TEST( + SoftBodyDynamicsInterface, + Emplace12Matrix +) { + std::array nodeIndices = {4, 3, 1, 2}; + StiffnessMatrixType stiffnessMatrix = StiffnessMatrixType::Zero(12, 12); + Barta::Utils::Matrix12Type m12; + // clang-format off + m12 << 1, 1, 1, 10, 10, 10, 100, 100, 100, -111, -111, -111, + 1, 1, 1, 10, 10, 10, 100, 100, 100, -111, -111, -111, + 1, 1, 1, 10, 10, 10, 100, 100, 100, -111, -111, -111, + + 2, 2, 2, 20, 20, 20, 200, 200, 200, -222, -222, -222, + 2, 2, 2, 20, 20, 20, 200, 200, 200, -222, -222, -222, + 2, 2, 2, 20, 20, 20, 200, 200, 200, -222, -222, -222, + + 4, 4, 4, 40, 40, 40, 400, 400, 400, -444, -444, -444, + 4, 4, 4, 40, 40, 40, 400, 400, 400, -444, -444, -444, + 4, 4, 4, 40, 40, 40, 400, 400, 400, -444, -444, -444, + + -111, -111, -111, -222, -222, -222, -444, -444, -444, 777, 777, 777, + -111, -111, -111, -222, -222, -222, -444, -444, -444, 777, 777, 777, + -111, -111, -111, -222, -222, -222, -444, -444, -444, 777, 777, 777; + // clang-format on + + StiffnessMatrixType expected = StiffnessMatrixType::Zero(12, 12); + // clang-format off + expected << 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 400, 400, 400, -444, -444, -444, 40, 40, 40, + 0, 0, 0, 400, 400, 400, -444, -444, -444, 40, 40, 40, + 0, 0, 0, 400, 400, 400, -444, -444, -444, 40, 40, 40, + + 0, 0, 0, -444, -444, -444, 777, 777, 777, -222, -222, -222, + 0, 0, 0, -444, -444, -444, 777, 777, 777, -222, -222, -222, + 0, 0, 0, -444, -444, -444, 777, 777, 777, -222, -222, -222, + + 0, 0, 0, 200, 200, 200, -222, -222, -222, 20, 20, 20, + 0, 0, 0, 200, 200, 200, -222, -222, -222, 20, 20, 20, + 0, 0, 0, 200, 200, 200, -222, -222, -222, 20, 20, 20, + // clang-format on + + SoftBodyDynamicsInterface::emplace12Matrix(m12, nodeIndices, stiffnessMatrix, 4); + + for (int i = 0; i < 12; ++i) { + for (int j = 0; j < 12; ++j) { + EXPECT_FLOAT_EQ(stiffnessMatrix(i, j), expected(i, j)); + } + } +} diff --git a/test/Dynamics/SoftBody/StVenantKirchhoffDynamicsTest.cpp b/test/Dynamics/SoftBody/StVenantKirchhoffDynamicsTest.cpp new file mode 100644 index 0000000..d17ff00 --- /dev/null +++ b/test/Dynamics/SoftBody/StVenantKirchhoffDynamicsTest.cpp @@ -0,0 +1,5 @@ +#include +#include +#include + +using namespace Barta::Dynamics::SoftBody; diff --git a/test/Objects/CMakeLists.txt b/test/Objects/CMakeLists.txt new file mode 100644 index 0000000..054e367 --- /dev/null +++ b/test/Objects/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Soft) \ No newline at end of file diff --git a/test/Objects/Soft/CMakeLists.txt b/test/Objects/Soft/CMakeLists.txt new file mode 100644 index 0000000..6087cda --- /dev/null +++ b/test/Objects/Soft/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(MshLoader) \ No newline at end of file diff --git a/test/Objects/Soft/MshLoader/CMakeLists.txt b/test/Objects/Soft/MshLoader/CMakeLists.txt new file mode 100644 index 0000000..d1dce4b --- /dev/null +++ b/test/Objects/Soft/MshLoader/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources(${TESTS_TARGET_NAME} PUBLIC + NodesPluginTest.cpp + EntitiesPluginTest.cpp + ElementsPluginTest.cpp + DirichletConditionPluginTest.cpp + TetrahedralElementLoaderDecorator.cpp + DirichletConditionParserDecoratorTest.cpp +) \ No newline at end of file diff --git a/test/Objects/Soft/MshLoader/DirichletConditionParserDecoratorTest.cpp b/test/Objects/Soft/MshLoader/DirichletConditionParserDecoratorTest.cpp new file mode 100644 index 0000000..ae71133 --- /dev/null +++ b/test/Objects/Soft/MshLoader/DirichletConditionParserDecoratorTest.cpp @@ -0,0 +1,64 @@ +#include +#include "MshDataParserMock.h" + +using namespace Barta::Objects::Soft::MshParser; + +TEST( + DirichletConditionParserDecoratorTest, + MarksNodesWithDirichletCondition +) { + MshDataParserMock mockParser; + + MshData mshData; + + mshData.physicalGroups = { + {1, 2, "\"SomeOtherCondition\""}, + {2, 2, "\"DirichletCondition\""} + }; + + mshData.entities[MshData::EntityDimension::SURFACE] = { + {.tag = 101, .physicalGroupTags = {2}}, + {.tag = 102, .physicalGroupTags = {1}}, + }; + + mshData.triangleElements = { + {.entityTag = 101, .nodeTags = {10, 11, 12}}, + {.entityTag = 102, .nodeTags = {13, 14, 15}} + }; + + Barta::Objects::Soft::Mesh initialMesh; + Barta::Objects::Soft::Node node; + node.isZeroDirichlet = false; + + node.tag = 10; + initialMesh.nodes.push_back(node); + node.tag = 11; + initialMesh.nodes.push_back(node); + node.tag = 12; + initialMesh.nodes.push_back(node); + node.tag = 13; + initialMesh.nodes.push_back(node); + node.tag = 14; + initialMesh.nodes.push_back(node); + node.tag = 15; + initialMesh.nodes.push_back(node); + node.tag = 16; + initialMesh.nodes.push_back(node); + + EXPECT_CALL(mockParser, parse(testing::_)).WillOnce(testing::Return(initialMesh)); + + DirichletConditionParserDecorator decorator(mockParser); + + Barta::Objects::Soft::Mesh finalMesh = decorator.parse(mshData); + + bool dirichletStarted = false; + for (const auto& node: finalMesh.nodes) { + if (node.tag == 10 || node.tag == 11 || node.tag == 12) { + EXPECT_TRUE(node.isZeroDirichlet) << "Node " << node.tag << " should be marked as Dirichlet."; + dirichletStarted = true; + } else { + EXPECT_FALSE(node.isZeroDirichlet) << "Node " << node.tag << " should not be marked as Dirichlet."; + EXPECT_FALSE(dirichletStarted) << "Dirichlet node should be in the back of the vector."; + } + } +} diff --git a/test/Objects/Soft/MshLoader/DirichletConditionPluginTest.cpp b/test/Objects/Soft/MshLoader/DirichletConditionPluginTest.cpp new file mode 100644 index 0000000..844a9ef --- /dev/null +++ b/test/Objects/Soft/MshLoader/DirichletConditionPluginTest.cpp @@ -0,0 +1,50 @@ +#include +#include + +using namespace Barta::Objects::Soft::MshLoader; + +class DirichletConditionPluginTest: public ::testing::Test { +protected: + DirichletConditionPlugin plugin; + MshData mshData; +}; + +TEST_F( + DirichletConditionPluginTest, + LoadsSingleDirichletGroup +) { + std::istringstream input("1\n1 42 left_wall\n"); + + plugin.loadMshData(input, mshData); + + ASSERT_EQ(mshData.physicalGroups.size(), 1); + const auto& group = mshData.physicalGroups[0]; + + EXPECT_EQ(group.dim, 1u); + EXPECT_EQ(group.tag, 42u); + EXPECT_EQ(group.name, "left_wall"); +} + +TEST_F( + DirichletConditionPluginTest, + LoadsMultipleGroups +) { + std::istringstream input("2\n2 1 top\n1 2 bottom\n"); + + plugin.loadMshData(input, mshData); + + ASSERT_EQ(mshData.physicalGroups.size(), 2); + + EXPECT_EQ(mshData.physicalGroups[0].name, "top"); + EXPECT_EQ(mshData.physicalGroups[1].name, "bottom"); +} + +TEST_F( + DirichletConditionPluginTest, + HandlesEmptyInput +) { + std::istringstream input("0\n"); + + EXPECT_NO_THROW(plugin.loadMshData(input, mshData)); + EXPECT_TRUE(mshData.physicalGroups.empty()); +} diff --git a/test/Objects/Soft/MshLoader/ElementsPluginTest.cpp b/test/Objects/Soft/MshLoader/ElementsPluginTest.cpp new file mode 100644 index 0000000..fe57efc --- /dev/null +++ b/test/Objects/Soft/MshLoader/ElementsPluginTest.cpp @@ -0,0 +1,89 @@ +#include +#include + +using namespace Barta::Objects::Soft::MshLoader; + +class ElementsPluginTest: public ::testing::Test { +protected: + ElementsPlugin plugin; + MshData mshData; +}; + +// These values should match your enum in MshData +constexpr size_t TRIANGLE_TYPE = static_cast(MshData::ElementType::TRIANGLE); +constexpr size_t TETRAHEDRON_TYPE = static_cast(MshData::ElementType::TETRAHEDRON); + +TEST_F( + ElementsPluginTest, + ParsesSingleTriangleElement +) { + // clang-format off + std::istringstream input( + "1 1 101 101\n" // numBlocks, totalElements, minTag, maxTag + "2 5 " + std::to_string(TRIANGLE_TYPE) + " 1\n" + "101 1 2 3\n" + ); + // clang-format on + + plugin.loadMshData(input, mshData); + + ASSERT_EQ(mshData.triangleElements.size(), 1); + EXPECT_EQ(mshData.triangleElements[0].entityTag, 5u); // entityTag + EXPECT_EQ(mshData.triangleElements[0].nodeTags[0], 1u); + EXPECT_EQ(mshData.triangleElements[0].nodeTags[1], 2u); + EXPECT_EQ(mshData.triangleElements[0].nodeTags[2], 3u); +} + +TEST_F( + ElementsPluginTest, + ParsesSingleTetrahedronElement +) { + // clang-format off + std::istringstream input( + "1 1 201 201\n" + "3 7 " + std::to_string(TETRAHEDRON_TYPE) + " 1\n" + "201 4 5 6 7\n" + ); + // clang-format on + + plugin.loadMshData(input, mshData); + + ASSERT_EQ(mshData.tetrahedralElements.size(), 1); + EXPECT_EQ(mshData.tetrahedralElements[0].nodeTags[0], 4u); + EXPECT_EQ(mshData.tetrahedralElements[0].nodeTags[1], 5u); + EXPECT_EQ(mshData.tetrahedralElements[0].nodeTags[2], 6u); + EXPECT_EQ(mshData.tetrahedralElements[0].nodeTags[3], 7u); +} + +TEST_F( + ElementsPluginTest, + SkipsUnsupportedElementTypes +) { + std::istringstream input("1 1 1 1\n" "1 1 999 1\n" // Unknown element type 999 + "1 8 9 10 11\n"); + + plugin.loadMshData(input, mshData); + + EXPECT_TRUE(mshData.triangleElements.empty()); + EXPECT_TRUE(mshData.tetrahedralElements.empty()); +} + +TEST_F( + ElementsPluginTest, + HandlesMultipleBlocks +) { + std::ostringstream oss; + // clang-format off + oss << "2 2 1 2\n" + << "2 1 " << TRIANGLE_TYPE << " 1\n" + << "1 10 11 12\n" + << "3 2 " << TETRAHEDRON_TYPE << " 1\n" + << "2 20 21 22 23\n"; + // clang-format on + + std::istringstream input(oss.str()); + plugin.loadMshData(input, mshData); + + ASSERT_EQ(mshData.triangleElements.size(), 1); + ASSERT_EQ(mshData.tetrahedralElements.size(), 1); +} diff --git a/test/Objects/Soft/MshLoader/EntitiesPluginTest.cpp b/test/Objects/Soft/MshLoader/EntitiesPluginTest.cpp new file mode 100644 index 0000000..f249a0e --- /dev/null +++ b/test/Objects/Soft/MshLoader/EntitiesPluginTest.cpp @@ -0,0 +1,85 @@ +#include +#include + +using namespace Barta::Objects::Soft::MshLoader; + +class EntitiesPluginTest: public ::testing::Test { +protected: + EntitiesPlugin plugin; + MshData mshData; +}; + +TEST_F( + EntitiesPluginTest, + LoadsSingleSurfaceEntity +) { + // clang-format off + std::istringstream input( + "0 0 1 0\n" // n0 (points), n1 (curves), n2 (surfaces), n3 (volumes) + "10 0.0 2.0 4.0 1.0 3.0 5.0 2 7 8\n" // tag + bbox + 2 physical group tags + ); + // clang-format on + + plugin.loadMshData(input, mshData); + + const auto& surfaces = mshData.entities[MshData::EntityDimension::SURFACE]; + ASSERT_EQ(surfaces.size(), 1); + + const auto& entity = surfaces[0]; + EXPECT_EQ(entity.tag, 10u); + EXPECT_FLOAT_EQ(entity.x_min, 0.0f); + EXPECT_FLOAT_EQ(entity.x_max, 1.0f); + EXPECT_FLOAT_EQ(entity.y_min, 2.0f); + EXPECT_FLOAT_EQ(entity.y_max, 3.0f); + EXPECT_FLOAT_EQ(entity.z_min, 4.0f); + EXPECT_FLOAT_EQ(entity.z_max, 5.0f); + ASSERT_EQ(entity.physicalGroupTags.size(), 2); + EXPECT_EQ(entity.physicalGroupTags[0], 7u); + EXPECT_EQ(entity.physicalGroupTags[1], 8u); +} + +TEST_F( + EntitiesPluginTest, + HandlesMultipleSurfaceEntities +) { + // clang-format off + std::istringstream input( + "0 0 2 0\n" + "20 0 0 0 1 1 1 0\n" + "30 0 0 0 2 2 2 1 9\n" + ); + // clang-format on + + plugin.loadMshData(input, mshData); + + const auto& surfaces = mshData.entities[MshData::EntityDimension::SURFACE]; + ASSERT_EQ(surfaces.size(), 2); + + EXPECT_EQ(surfaces[0].tag, 20u); + EXPECT_TRUE(surfaces[0].physicalGroupTags.empty()); + + EXPECT_EQ(surfaces[1].tag, 30u); + ASSERT_EQ(surfaces[1].physicalGroupTags.size(), 1); + EXPECT_EQ(surfaces[1].physicalGroupTags[0], 9u); +} + +TEST_F( + EntitiesPluginTest, + SkipsNonSurfaceEntities +) { + // clang-format off + std::istringstream input( + "2 1 0 2\n" + "dummy_point\n" + "dummy_point\n" + "dummy_curve\n" + "dummy_volume_1\n" + "dummy_volume_2\n" + ); + // clang-format on + + plugin.loadMshData(input, mshData); + + // Only surfaces are stored + EXPECT_TRUE(mshData.entities[MshData::EntityDimension::SURFACE].empty()); +} diff --git a/test/Objects/Soft/MshLoader/MshDataParserMock.h b/test/Objects/Soft/MshLoader/MshDataParserMock.h new file mode 100644 index 0000000..9eac17d --- /dev/null +++ b/test/Objects/Soft/MshLoader/MshDataParserMock.h @@ -0,0 +1,11 @@ +#pragma once +#include "Objects/Soft/MshParser/MshDataParserInterface.h" +#include +#include + +using namespace Barta::Objects::Soft::MshParser; + +class MshDataParserMock: public MshDataParserInterface { +public: + MOCK_METHOD(Barta::Objects::Soft::Mesh, parse, (const MshData&), (const, override)); +}; diff --git a/test/Objects/Soft/MshLoader/NodesPluginTest.cpp b/test/Objects/Soft/MshLoader/NodesPluginTest.cpp new file mode 100644 index 0000000..95234da --- /dev/null +++ b/test/Objects/Soft/MshLoader/NodesPluginTest.cpp @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +using namespace Barta::Objects::Soft::MshLoader; + +TEST( + NodesPluginTest, + LoadsSingleBlockWithTwoNodes +) { + NodesPlugin plugin; + MshData mshData; + + // clang-format off + std::istringstream input( + "1 2 1 2\n" // numBlocks totalNodes minTag maxTag + "3 7 0 2\n" // entityDim entityTag parametric numNodes + "101 102\n" // node tags + "1.0 2.0 3.0\n" // coordinates for node 101 + "4.0 5.0 6.0\n" // coordinates for node 102 + ); + // clang-format on + + plugin.loadMshData(input, mshData); + + ASSERT_EQ(mshData.nodes.size(), 2); + + EXPECT_EQ(mshData.nodes[0].tag, 101); + EXPECT_FLOAT_EQ(mshData.nodes[0].x, 1.0f); + EXPECT_FLOAT_EQ(mshData.nodes[0].y, 2.0f); + EXPECT_FLOAT_EQ(mshData.nodes[0].z, 3.0f); + + EXPECT_EQ(mshData.nodes[1].tag, 102); + EXPECT_FLOAT_EQ(mshData.nodes[1].x, 4.0f); + EXPECT_FLOAT_EQ(mshData.nodes[1].y, 5.0f); + EXPECT_FLOAT_EQ(mshData.nodes[1].z, 6.0f); +} + +TEST( + NodesPluginTest, + HandlesMultipleEntityBlocks +) { + NodesPlugin plugin; + MshData mshData; + + // clang-format off + std::istringstream input( + "2 3 10 12\n" + "2 5 0 1\n" + "10\n" + "0.0 0.0 0.0\n" + "3 8 0 2\n" + "11 12\n" + "1.0 1.0 1.0\n" + "2.0 2.0 2.0\n" + ); + // clang-format on + + plugin.loadMshData(input, mshData); + + ASSERT_EQ(mshData.nodes.size(), 3); + + EXPECT_EQ(mshData.nodes[0].tag, 10); + EXPECT_FLOAT_EQ(mshData.nodes[1].x, 1.0f); + EXPECT_EQ(mshData.nodes[2].tag, 12); +} + +TEST( + NodesPluginTest, + EmptyInputDoesNotThrow +) { + NodesPlugin plugin; + MshData mshData; + + std::istringstream input("0 0 0 0\n"); + + EXPECT_NO_THROW(plugin.loadMshData(input, mshData)); + EXPECT_TRUE(mshData.nodes.empty()); +} diff --git a/test/Objects/Soft/MshLoader/TetrahedralElementLoaderDecorator.cpp b/test/Objects/Soft/MshLoader/TetrahedralElementLoaderDecorator.cpp new file mode 100644 index 0000000..60557d1 --- /dev/null +++ b/test/Objects/Soft/MshLoader/TetrahedralElementLoaderDecorator.cpp @@ -0,0 +1,55 @@ +#include +#include "MshDataParserMock.h" +#include + +using namespace Barta; +using namespace Barta::Objects::Soft; +using namespace Barta::Objects::Soft::MshParser; +using ::testing::Return; + +TEST( + TetrahedralElementLoaderDecoratorTest, + ParsesTetrahedralElementsCorrectly +) { + testing::NiceMock mockParser; + + // Prepare MshData with one tetrahedral element + MshData mshData; + mshData.tetrahedralElements.push_back({ + .nodeTags = {4, 3, 1, 2} + }); + + // Prepare corresponding mesh from the base parser + Mesh mesh; + Node node; + node.isZeroDirichlet = false; + + node.tag = 1; + node.dynamicsDTOCollection.dynamicsDTOs[DynamicsDTOIteration::CURRENT].massCenter = {0, 0, 0}; + mesh.nodes.push_back(node); + + node.tag = 1; + node.dynamicsDTOCollection.dynamicsDTOs[DynamicsDTOIteration::CURRENT].massCenter = {1, 0, 0}; + mesh.nodes.push_back(node); + + node.tag = 2; + node.dynamicsDTOCollection.dynamicsDTOs[DynamicsDTOIteration::CURRENT].massCenter = {0, 1, 0}; + mesh.nodes.push_back(node); + + node.tag = 3; + node.dynamicsDTOCollection.dynamicsDTOs[DynamicsDTOIteration::CURRENT].massCenter = {0, 0, 1}; + mesh.nodes.push_back(node); + + EXPECT_CALL(mockParser, parse(::testing::_)).WillOnce(testing::Return(mesh)); + + TetrahedralElementLoaderDecorator decorator(mockParser); + + Mesh result = decorator.parse(mshData); + + // Assert mesh has one tetrahedral element + ASSERT_EQ(result.elements.size(), 1); + + const auto& element = result.elements[0]; + EXPECT_EQ(element.getNodeIndices().size(), 4); + EXPECT_THAT(element.getNodeIndices(), ::testing::UnorderedElementsAre(3, 2, 0, 1)); +} diff --git a/test/Utilities/CMakeLists.txt b/test/Utilities/CMakeLists.txt new file mode 100644 index 0000000..632c0f1 --- /dev/null +++ b/test/Utilities/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${TESTS_TARGET_NAME} PUBLIC + MatrixUtilitiesTest.cpp +) \ No newline at end of file diff --git a/test/Utilities/MatrixUtilitiesTest.cpp b/test/Utilities/MatrixUtilitiesTest.cpp new file mode 100644 index 0000000..220a90c --- /dev/null +++ b/test/Utilities/MatrixUtilitiesTest.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +using namespace Barta::Utils; + +TEST( + MatrixUtilitiesTest, + Create12MatrixFrom9MatrixAndThirdNewtonLaw +) { + // clang-format off + Matrix9Type A; + A << 1, 1, 1, 10, 10, 10, 100, 100, 100, + 1, 1, 1, 10, 10, 10, 100, 100, 100, + 1, 1, 1, 10, 10, 10, 100, 100, 100, + + 2, 2, 2, 20, 20, 20, 200, 200, 200, + 2, 2, 2, 20, 20, 20, 200, 200, 200, + 2, 2, 2, 20, 20, 20, 200, 200, 200, + + 4, 4, 4, 40, 40, 40, 400, 400, 400, + 4, 4, 4, 40, 40, 40, 400, 400, 400, + 4, 4, 4, 40, 40, 40, 400, 400, 400; + + Matrix12Type expected; + expected << 1, 1, 1, 10, 10, 10, 100, 100, 100, -111, -111, -111, + 1, 1, 1, 10, 10, 10, 100, 100, 100, -111, -111, -111, + 1, 1, 1, 10, 10, 10, 100, 100, 100, -111, -111, -111, + + 2, 2, 2, 20, 20, 20, 200, 200, 200, -222, -222, -222, + 2, 2, 2, 20, 20, 20, 200, 200, 200, -222, -222, -222, + 2, 2, 2, 20, 20, 20, 200, 200, 200, -222, -222, -222, + + 4, 4, 4, 40, 40, 40, 400, 400, 400, -444, -444, -444, + 4, 4, 4, 40, 40, 40, 400, 400, 400, -444, -444, -444, + 4, 4, 4, 40, 40, 40, 400, 400, 400, -444, -444, -444, + + -7, -7, -7, -70, -70, -70, -700, -700, -700, 777, 777, 777, + -7, -7, -7, -70, -70, -70, -700, -700, -700, 777, 777, 777, + -7, -7, -7, -70, -70, -70, -700, -700, -700, 777, 777, 777; + // clang-format on + + auto result = create12MatrixFrom9MatrixAndThirdNewtonLaw(A); + + for (int i = 0; i < 12; ++i) { + for (int j = 0; j < 12; ++j) { + EXPECT_FLOAT_EQ(result(i, j), expected(i, j)); + } + } +} diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..e69de29 diff --git a/test/run/CMakeLists.txt b/test/run/CMakeLists.txt deleted file mode 100644 index 1884eb4..0000000 --- a/test/run/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ - -#add_executable(${RUN_TEST_NAME} -# main.cpp -#) -# -#target_link_libraries(${RUN_TEST_NAME} PUBLIC -# sfml-graphics -# ${LIBRARY_NAME} -#) -#target_include_directories(${RUN_TEST_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/include") -# -# -#add_test(NAME VectorOutput COMMAND ${RUN_TEST_NAME}) \ No newline at end of file diff --git a/test/run/main.cpp b/test/run/main.cpp deleted file mode 100644 index d8b7097..0000000 --- a/test/run/main.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// -// Created by barta on 01/03/2024. -// -#include - -int main() { - auto vec = Barta::Vector2f(1.f, 1.f); - - std::cout << vec << std::endl; - - return 0; -} \ No newline at end of file