diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fe2e9d894d764968fa9f1f9dd6c025c53b743c3..b5907534e02ed396f76b2d1f82b49d98ee1b0cb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ find_package(Tracy REQUIRED) find_package(OpenVDB REQUIRED) find_package(TinyAD REQUIRED) find_package(embree REQUIRED) +find_package(EnTT REQUIRED) # Set output directories for libraries, archives and binaries include(GNUInstallDirs) diff --git a/bindings/Mandos/python/BarycentricMapping.cpp b/bindings/Mandos/python/BarycentricMapping.cpp deleted file mode 100644 index f492c85b0c8c2b6551df2eb562c25c014a763006..0000000000000000000000000000000000000000 --- a/bindings/Mandos/python/BarycentricMapping.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -#include diff --git a/bindings/Mandos/python/BarycentricMapping.hpp b/bindings/Mandos/python/BarycentricMapping.hpp deleted file mode 100644 index 21cf75a342ac4049a2dc33b84b2569f569a6f9bc..0000000000000000000000000000000000000000 --- a/bindings/Mandos/python/BarycentricMapping.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef MANDOS_PY_BARYCENTRICMAPPING_HPP -#define MANDOS_PY_BARYCENTRICMAPPING_HPP - -#include - -#include - -namespace py = pybind11; - -namespace mandos::py -{ -// struct BarycentricMapping { -// RigidBodyPointMapping(RigidBodyCloud3D &rigidBodyCloud, mandos::core::Model &model); - -// void addParticle(core::Vec3 &localPos, int rigidBodyIndex); - -// core::RigidBodyPointMapping &mapping(); - -// py::Deformable3D m_deformable; -// py::RigidBodyCloud3D &m_rigidBodyCloud; -// unsigned int m_mapping_index; -// }; - -void wrapBarycentricMapping(::py::module_ &m); -} // namespace mandos::py - -#endif // MANDOS_PY_BARYCENTRICMAPPING_HPP \ No newline at end of file diff --git a/bindings/Mandos/python/Deformable3D.cpp b/bindings/Mandos/python/Deformable3D.cpp index bc2b2d44f45c35a6353ba6865f8e0134d4b0ad13..85445a7d1dea98269c49614327f4209280c37911 100644 --- a/bindings/Mandos/python/Deformable3D.cpp +++ b/bindings/Mandos/python/Deformable3D.cpp @@ -6,8 +6,13 @@ #include #include #include -#include +#include #include +#include +#include "Mandos/Core/DiffParameters.hpp" +#include "Mandos/Core/Energies/ConstantForce.hpp" +#include "Mandos/Core/Energies/LumpedMassInertia.hpp" +#include "Mandos/Core/Projections/FixedProjection.hpp" #include @@ -40,12 +45,14 @@ void mandos::py::wrapDeformable3D(nb::module_ &m) .def_prop_ro("constant_force", &Deformable3D::constantForce) .def_prop_rw("particle_mass", &Deformable3D::vertexMass, &Deformable3D::setVertexMass) .def("disable_gravity", - [](Deformable3D &self) { self.simObject().potential().disable(); }) + [](Deformable3D &self) { + self.registry().get(self.handle().entt).disable(); + }) .def("add_sphere_cloud", [](Deformable3D &def3d, mandos::core::Scalar radius) { - auto &scs = std::get>(def3d.simObject().colliders()); + auto &scs = def3d.registry().get>(def3d.handle().entt); auto &sc = scs.emplace_back(radius); - sc.update(def3d.simObject().mstate); + sc.update(def3d.handle().mstate()); return mandos::core::collisions::SimulationCollider{ def3d.handle(), static_cast(scs.size() - 1)}; @@ -53,9 +60,10 @@ void mandos::py::wrapDeformable3D(nb::module_ &m) .def( "add_collision_mesh", [](Deformable3D &def3d, mandos::core::SurfaceMesh &surfaceMesh, mandos::core::Scalar dhat) { - auto &cms = std::get>(def3d.simObject().colliders()); + std::cout << "def3d handle = " << entt::to_integral(def3d.handle().entt) << std::endl; + auto &cms = def3d.registry().get>(def3d.handle().entt); auto &cm = cms.emplace_back(surfaceMesh, dhat); - cm.update(def3d.simObject().mstate); + cm.update(def3d.handle().mstate()); return mandos::core::collisions::SimulationCollider{ def3d.handle(), static_cast(cms.size() - 1)}; @@ -69,9 +77,10 @@ void mandos::py::wrapDeformable3D(nb::module_ &m) const std::vector> &edges, const std::vector> &triangles, mandos::core::Scalar dhat) { - auto &cms = std::get>(def3d.simObject().colliders()); - auto &cm = cms.emplace_back(def3d.simObject().mstate.m_x, edges, triangles, dhat); - cm.update(def3d.simObject().mstate); + std::cout << "def3d handle = " << entt::to_integral(def3d.handle().entt) << std::endl; + auto &cms = def3d.registry().get>(def3d.handle().entt); + auto &cm = cms.emplace_back(def3d.handle().mstate().m_x, edges, triangles, dhat); + cm.update(def3d.handle().mstate()); return mandos::core::collisions::SimulationCollider{ def3d.handle(), static_cast(cms.size() - 1)}; @@ -112,86 +121,80 @@ void mandos::py::wrapDeformable3D(nb::module_ &m) }); } -mandos::core::SimulationObject &mandos::py::Deformable3D::simObject() -{ - return m_handle.simulationObject(); -} - -const mandos::core::SimulationObject &mandos::py::Deformable3D::simObject() const -{ - return m_handle.simulationObject(); -} - mandos::core::StableNeoHookean &mandos::py::Deformable3D::snh() { - return simObject().potential(); + return this->m_registry.get(m_handle.entt); } mandos::core::ARAP &mandos::py::Deformable3D::arap() { - return simObject().potential(); + return this->m_registry.get(m_handle.entt); } mandos::core::AirDrag &mandos::py::Deformable3D::airDrag() { - return simObject().dissipativePotential(); + return this->m_registry.get(m_handle.entt); } mandos::core::MassSpring &mandos::py::Deformable3D::massSpring() { - return simObject().potential(); + return this->m_registry.get(m_handle.entt); } mandos::core::ConstantForce &mandos::py::Deformable3D::constantForce() { - return simObject().potential(); + return this->m_registry.get(m_handle.entt); } const std::vector &mandos::py::Deformable3D::vertexMass() const { - return simObject().potential().vertexMass(); + return this->m_registry.get(m_handle.entt).vertexMass(); } -void mandos::py::Deformable3D::setVertexMass(std::vector vertexMass) +void mandos::py::Deformable3D::setVertexMass(const std::vector &vertexMass) { - simObject().potential().vertexMass() = vertexMass; - simObject().inertia().vertexMass() = std::move(vertexMass); + this->m_registry.get(m_handle.entt).vertexMass() = vertexMass; + this->m_registry.get(m_handle.entt).vertexMass() = vertexMass; } void mandos::py::Deformable3D::setV(const Eigen::Matrix &v) { + auto &mstate = m_handle.mstate(); Eigen::Matrix::MapType( - simObject().mstate.m_v.data()->data(), static_cast(simObject().mstate.m_v.size()), 3) = v; + mstate.m_v.data()->data(), static_cast(mstate.m_v.size()), 3) = v; } Eigen::Map> mandos::py::Deformable3D::v() const { - return {simObject().mstate.m_v.data()->data(), static_cast(simObject().mstate.m_v.size()), 3}; + const auto &mstate = m_handle.mstate(); + return {mstate.m_v.data()->data(), static_cast(mstate.m_v.size()), 3}; } Eigen::Matrix mandos::py::Deformable3D::f() const { + const auto &mstate = m_handle.mstate(); return -1 * Eigen::Matrix::ConstMapType( - simObject().mstate.m_grad.data()->data(), - static_cast(simObject().mstate.m_grad.size()), - 3); + mstate.m_grad.data()->data(), static_cast(mstate.m_grad.size()), 3); } void mandos::py::Deformable3D::setX(const Eigen::Matrix &x) { + auto &mstate = m_handle.mstate(); Eigen::Matrix::MapType( - simObject().mstate.m_x.data()->data(), static_cast(simObject().mstate.m_x.size()), 3) = x; + mstate.m_x.data()->data(), static_cast(mstate.m_x.size()), 3) = x; } void mandos::py::Deformable3D::resize(int nParticles) { - simObject().mstate.resize(nParticles); + auto &mstate = m_handle.mstate(); + mstate.resize(nParticles); } int mandos::py::Deformable3D::size() const { - return static_cast(simObject().mstate.m_x.size()); + const auto &mstate = m_handle.mstate(); + return static_cast(mstate.m_x.size()); } void mandos::py::Deformable3D::fixParticle(int index, bool x, bool y, bool z) @@ -209,21 +212,31 @@ void mandos::py::Deformable3D::fixParticle(int index, bool x, bool y, bool z) std::vector &mandos::py::Deformable3D::getFixedDofVector() { - return std::get(simObject().projections).indices(); + return m_registry.get(m_handle.entt).indices(); } Eigen::Map> mandos::py::Deformable3D::x() const { - return {simObject().mstate.m_x.data()->data(), static_cast(simObject().mstate.m_x.size()), 3}; + const auto &mstate = m_handle.mstate(); + return {mstate.m_x.data()->data(), static_cast(mstate.m_x.size()), 3}; } -mandos::py::Deformable3D::Deformable3D(mandos::core::SimulationObjectHandle handle) +mandos::py::Deformable3D::Deformable3D(mandos::core::SimulationObject handle, + entt::registry ®istry) : m_handle(handle) + , m_registry(registry) { } - -mandos::core::SimulationObjectHandle mandos::py::Deformable3D::handle() const +mandos::core::SimulationObject mandos::py::Deformable3D::handle() const { return m_handle; } +const entt::registry &mandos::py::Deformable3D::registry() const +{ + return m_registry; +} +entt::registry &mandos::py::Deformable3D::registry() +{ + return m_registry; +} diff --git a/bindings/Mandos/python/Deformable3D.hpp b/bindings/Mandos/python/Deformable3D.hpp index dcf3c86150b733fcf88c2ef96bd356c10a55e11d..676306c3e6f64cc3a5bed348cddc54af4aefb496 100644 --- a/bindings/Mandos/python/Deformable3D.hpp +++ b/bindings/Mandos/python/Deformable3D.hpp @@ -3,13 +3,17 @@ #include +#include +#include +#include + +#include +#include #include #include #include #include -#include -#include -#include +#include namespace nb = nanobind; @@ -17,13 +21,7 @@ namespace mandos::py { struct Deformable3D { - explicit Deformable3D(core::SimulationObjectHandle handle); - - core::SimulationObject &simObject(); - - const core::SimulationObject &simObject() const; - - core::SimulationObjectHandle handle() const; + explicit Deformable3D(core::SimulationObject handle, entt::registry ®istry); Eigen::Map> x() const; @@ -37,7 +35,7 @@ struct Deformable3D { void setV(const Eigen::Matrix &v); - void setVertexMass(std::vector vertexMass); + void setVertexMass(const std::vector &vertexMass); const std::vector &vertexMass() const; @@ -55,8 +53,14 @@ struct Deformable3D { mandos::core::ConstantForce &constantForce(); + entt::registry ®istry(); + const entt::registry ®istry() const; + + core::SimulationObject handle() const; + private: - core::SimulationObjectHandle m_handle; + core::SimulationObject m_handle; + entt::registry &m_registry; }; void wrapDeformable3D(nb::module_ &m); diff --git a/bindings/Mandos/python/Differentiable.cpp b/bindings/Mandos/python/Differentiable.cpp index 5335c52bfc3f2605cdce7e7edca68fb4dc5382d9..4a175245305f1aa111be61080158104cf34bd339 100644 --- a/bindings/Mandos/python/Differentiable.cpp +++ b/bindings/Mandos/python/Differentiable.cpp @@ -3,6 +3,9 @@ #include +#include +#include + #include #include @@ -21,7 +24,7 @@ void wrapDifferentiable(nb::module_ &m) .def("get_n_dof", &core::Trajectory::getNDof) .def("get_n_states", &core::Trajectory::getNStates) .def("get_n_steps", &core::Trajectory::getNSteps) - .def("append_state", [](core::Trajectory &trajectory, core::Model &model) { + .def("append_state", [](core::Trajectory &trajectory, core::Model &model) { // Get state mandos::core::Vec x(model.nDof()); mandos::core::Vec v(model.nDof()); @@ -64,17 +67,17 @@ void wrapDifferentiable(nb::module_ &m) .def(nb::init>()) .def_prop_ro("size", &mandos::core::DiffParameters::size); - nb::class_(m, "BackwardEngine") + nb::class_>(m, "BackwardEngine") .def(nb::init()) - .def("backward_step", &mandos::core::BackwardEngine::backwardStep) + .def("backward_step", &mandos::core::BackwardEngine::backwardStep) .def("backward_simulation", - &mandos::core::BackwardEngine::backwardSimulation, + &mandos::core::BackwardEngine::backwardSimulation, nb::arg("loss"), nb::arg("diff_parameters") = mandos::core::DiffParameters(), nb::arg("max_iterations") = 0); m.def("compute_loss_function_gradient_backpropagation", - &mandos::core::computeLossFunctionGradientBackpropagation, + &mandos::core::computeLossFunctionGradientBackpropagation, nb::arg("h"), nb::arg("model"), nb::arg("trajectory"), diff --git a/bindings/Mandos/python/Energies/PlaneField.cpp b/bindings/Mandos/python/Energies/PlaneField.cpp index 6678a6f19e6ed46d9c5b4eea44884975ae37543b..5428c5d7780a585a3b919bc0e8cd7be5c670dd59 100644 --- a/bindings/Mandos/python/Energies/PlaneField.cpp +++ b/bindings/Mandos/python/Energies/PlaneField.cpp @@ -41,10 +41,10 @@ void mandos::py::energies::wrapPlaneField(nb::module_ &m) .def("get_world_velocitioes", &core::PlaneField::getWorldVelocities) .def("compute_friction_forces", [](core::PlaneField &self, RigidBody3D &rb, core::Scalar h) { - return self.computeFrictionForces(rb.simObject().mstate, h); + return self.computeFrictionForces(rb.handle().mstate(), h); }) .def("compute_friction_rb_forces", [](core::PlaneField &self, RigidBody3D &rb, core::Scalar h) { - return self.computeFrictionRBForces(rb.simObject().mstate, h); + return self.computeFrictionRBForces(rb.handle().mstate(), h); }); } diff --git a/bindings/Mandos/python/IdentityMapping.cpp b/bindings/Mandos/python/IdentityMapping.cpp index c5be2d0848483160784cc0c4d8910bab40900b01..6613ab39d368cc32a53501845d4729f31198a9cd 100644 --- a/bindings/Mandos/python/IdentityMapping.cpp +++ b/bindings/Mandos/python/IdentityMapping.cpp @@ -11,18 +11,18 @@ namespace mandos::py { IdentityMapping::IdentityMapping(Deformable3D &from, Model &model) - : m_to(model.add()) + : m_to(model.add(), from.registry()) , m_from(from) { - auto &mappings = m_from.simObject().mappings(); + auto &mappings = m_from.registry().get>(m_from.handle().entt); m_mapping_index = static_cast(mappings.size()); mappings.emplace_back(m_from.handle(), m_to.handle()); } core::IdentityMapping &IdentityMapping::mapping() { - return m_from.simObject().mappings()[m_mapping_index]; + return m_from.registry().get>(m_from.handle().entt)[m_mapping_index]; } void IdentityMapping::addParticle(int from) @@ -30,13 +30,13 @@ void IdentityMapping::addParticle(int from) if (from >= m_from.size()) { throw std::invalid_argument("From particle in IdentityMapping is out of bounds for the Deformable3D"); } - auto &mapping = m_from.simObject().mappings()[m_mapping_index]; + auto &mapping = m_from.registry().get>(m_from.handle().entt)[m_mapping_index]; mapping.addMappedPoint(from, m_to.size()); m_to.resize(m_to.size() + 1); m_to.setX(Eigen::Matrix::Zero(m_to.size(), 3)); - mapping.apply(mapping.from()->mstate.m_x, mapping.to()->mstate.m_x); - mapping.applyJ(mapping.from()->mstate.m_v, mapping.to()->mstate.m_v); + mapping.apply(mapping.from().mstate().m_x, mapping.to().mstate().m_x); + mapping.applyJ(mapping.from().mstate().m_v, mapping.to().mstate().m_v); } void wrapIdentityMapping(nb::module_ &m) diff --git a/bindings/Mandos/python/Mandos.cpp b/bindings/Mandos/python/Mandos.cpp index b88d157aea59cb3b920e8f127246bfbf312b2fef..642c42ec62c95e39e21e20fdbc41000714064a89 100644 --- a/bindings/Mandos/python/Mandos.cpp +++ b/bindings/Mandos/python/Mandos.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -61,6 +63,6 @@ NB_MODULE(mandos_pylib, m) [](mandos::core::SimulationStepResult s) { return s == mandos::core::SimulationStepResult::Success; }); m.def("step", [](mandos::py::Model &model, mandos::core::StepParameters stepParameters) { - return mandos::core::step(model, stepParameters); + return mandos::core::step(model, stepParameters); }); } diff --git a/bindings/Mandos/python/Model.cpp b/bindings/Mandos/python/Model.cpp index 7c0ed93e875a724aa6cf396171d6a9127616c0b8..4c6ed7acebdd1bbc14106fbe12189eea440eb27c 100644 --- a/bindings/Mandos/python/Model.cpp +++ b/bindings/Mandos/python/Model.cpp @@ -7,33 +7,41 @@ #include #include +#include +#include +#include + #include #include #include #include -#include #include #include #include #include +#include #include -#include #include +#include +#include +#include + #include #include #include +#include "Mandos/Core/Collisions/CollisionDetection.hpp" #include void mandos::py::wrapModel(nb::module_ &m) { - const nb::class_ model_(m, "Model_"); - nb::class_(m, "Model") + const nb::class_> model_(m, "Model_"); + nb::class_>(m, "Model") .def(nb::init()) - .def("n_dof", &mandos::core::Model::nDof) + .def("n_dof", &mandos::core::Model::nDof) ///////////////// Simulation objects ////////////////////// @@ -51,7 +59,7 @@ void mandos::py::wrapModel(nb::module_ &m) if (name) { model.m_particle3DObjects[name.value()] = vd; } - return Deformable3D{vd}; + return Deformable3D{vd, model.registry()}; }, nb::arg("name") = std::optional{}) .def( @@ -68,7 +76,7 @@ void mandos::py::wrapModel(nb::module_ &m) if (name) { model.m_rigidBodyObjects[name.value()] = vd; } - return RigidBody3D{vd}; + return RigidBody3D{vd, model.registry()}; }, nb::arg("name") = std::optional{}) .def( @@ -86,7 +94,7 @@ void mandos::py::wrapModel(nb::module_ &m) if (name) { model.m_rigidBodyGlobalObjects[name.value()] = vd; } - return RigidBody3D{vd}; + return RigidBody3D{vd, model.registry()}; }, nb::arg("name") = std::optional{}) .def( @@ -104,7 +112,7 @@ void mandos::py::wrapModel(nb::module_ &m) if (name) { model.m_rigidBodyCloudObjects[name.value()] = vd; } - return RigidBodyCloud3D{vd}; + return RigidBodyCloud3D{vd, model.registry()}; }, nb::arg("name") = std::optional{}) .def( @@ -122,20 +130,20 @@ void mandos::py::wrapModel(nb::module_ &m) if (name) { model.m_rigidBodyGlobalCloudObjects[name.value()] = vd; } - return RigidBodyCloud3D{vd}; + return RigidBodyCloud3D{vd, model.registry()}; }, nb::arg("name") = std::optional{}) .def("get_deformable3d", [](Model &model, const std::string &name) -> std::optional { if (auto it = model.m_particle3DObjects.find(name); it != std::end(model.m_particle3DObjects)) { - return Deformable3D{it->second}; + return Deformable3D{it->second, model.registry()}; } return {}; }) .def("get_rigidbody", [](Model &model, const std::string &name) -> std::optional> { if (auto it = model.m_rigidBodyObjects.find(name); it != std::end(model.m_rigidBodyObjects)) { - return RigidBody3D{it->second}; + return RigidBody3D{it->second, model.registry()}; } return {}; }) @@ -143,7 +151,7 @@ void mandos::py::wrapModel(nb::module_ &m) [](Model &model, const std::string &name) -> std::optional { if (auto it = model.m_rigidBodyCloudObjects.find(name); it != std::end(model.m_rigidBodyCloudObjects)) { - return RigidBodyCloud3D{it->second}; + return RigidBodyCloud3D{it->second, model.registry()}; } return {}; }) @@ -151,7 +159,7 @@ void mandos::py::wrapModel(nb::module_ &m) [](Model &model, const std::string &name) -> std::optional { if (auto it = model.m_rigidBodyGlobalCloudObjects.find(name); it != std::end(model.m_rigidBodyGlobalCloudObjects)) { - return RigidBodyCloud3D{it->second}; + return RigidBodyCloud3D{it->second, model.registry()}; } return {}; }) @@ -159,7 +167,7 @@ void mandos::py::wrapModel(nb::module_ &m) [](Model &model, const std::string &name) -> std::optional> { if (auto it = model.m_rigidBodyGlobalObjects.find(name); it != std::end(model.m_rigidBodyGlobalObjects)) { - return RigidBody3D{it->second}; + return RigidBody3D{it->second, model.registry()}; } return {}; }) @@ -167,7 +175,7 @@ void mandos::py::wrapModel(nb::module_ &m) [](Model &model, const std::string &name) -> std::optional { if (auto it = model.m_collisionStateObjects.find(name); it != std::end(model.m_collisionStateObjects)) { - return Deformable3D{it->second}; + return Deformable3D{it->second, model.registry()}; } return {}; }) @@ -238,8 +246,10 @@ void mandos::py::wrapModel(nb::module_ &m) .def( "add_collision_pair", [](Model &model, - mandos::core::collisions::SimulationCollider c0, - mandos::core::collisions::SimulationCollider c1, + mandos::core::collisions::SimulationCollider + c0, + mandos::core::collisions::SimulationCollider c1, std::optional name = {}, mandos::core::Scalar stiffness = {}, mandos::core::Scalar coulombConst = {}, @@ -252,11 +262,21 @@ void mandos::py::wrapModel(nb::module_ &m) } } - auto vd{model.addCollisionPair(c0, c1, stiffness, coulombConst, vMax, threshold)}; + using SimulationCollider0 = decltype(c0); + using SimulationCollider1 = decltype(c1); + auto vd{mandos::core::addCollisionPair(model, // + c0, + c1, + stiffness, + coulombConst, + vMax, + threshold)}; if (name) { model.m_collisionStateObjects[name.value()] = vd; } - return Deformable3D{vd}; + return Deformable3D{vd, model.registry()}; }, nb::arg(), nb::arg(), @@ -269,8 +289,10 @@ void mandos::py::wrapModel(nb::module_ &m) .def( "add_collision_pair", [](Model &model, - mandos::core::collisions::SimulationCollider c0, - mandos::core::collisions::SimulationCollider c1, + mandos::core::collisions::SimulationCollider c0, + mandos::core::collisions::SimulationCollider c1, std::optional name = {}, mandos::core::Scalar stiffness = {}, mandos::core::Scalar coulombConst = {}, @@ -283,11 +305,21 @@ void mandos::py::wrapModel(nb::module_ &m) } } - auto vd{model.addCollisionPair(c0, c1, stiffness, coulombConst, vMax, threshold)}; + using SimulationCollider0 = decltype(c0); + using SimulationCollider1 = decltype(c1); + auto vd{mandos::core::addCollisionPair(model, // + c0, + c1, + stiffness, + coulombConst, + vMax, + threshold)}; if (name) { model.m_collisionStateObjects[name.value()] = vd; } - return Deformable3D{vd}; + return Deformable3D{vd, model.registry()}; }, nb::arg(), nb::arg(), @@ -299,19 +331,17 @@ void mandos::py::wrapModel(nb::module_ &m) ///////////////// Visitors ////////////////////// - .def_prop_ro( - "energy", - [](const mandos::core::Model &model, const mandos::core::Scalar h) { return model.computeEnergy(h); }) + .def_prop_ro("energy", [](const Model &model, const mandos::core::Scalar h) { return model.computeEnergy(h); }) .def_prop_rw( "state", - [](mandos::core::Model &model) { + [](Model &model) { mandos::core::Vec x(model.nDof()); mandos::core::Vec v(model.nDof()); model.state(x, v); return std::pair(x, v); }, - &mandos::core::Model::setState) - .def("set_state", &mandos::core::Model::setState) + &Model::setState) + .def("set_state", &Model::setState) .def("apply", [](Model &model) { auto nDof = model.nDof(); diff --git a/bindings/Mandos/python/Model.hpp b/bindings/Mandos/python/Model.hpp index a28d94fd6e5fb11f9008f1b3273efc964c773e5e..fbe715fe877a7f6869800134d37a7462e21f6d63 100644 --- a/bindings/Mandos/python/Model.hpp +++ b/bindings/Mandos/python/Model.hpp @@ -3,24 +3,24 @@ #include +#include #include #include #include #include -#include +#include namespace nb = nanobind; namespace mandos::py { -struct Model : public mandos::core::Model { - std::unordered_map> m_particle3DObjects; - std::unordered_map> m_rigidBodyObjects; - std::unordered_map> m_rigidBodyGlobalObjects; - std::unordered_map> m_rigidBodyCloudObjects; - std::unordered_map> - m_rigidBodyGlobalCloudObjects; - std::unordered_map> m_collisionStateObjects; +struct Model : public mandos::core::Model { + std::unordered_map> m_particle3DObjects; + std::unordered_map> m_rigidBodyObjects; + std::unordered_map> m_rigidBodyGlobalObjects; + std::unordered_map> m_rigidBodyCloudObjects; + std::unordered_map> m_rigidBodyGlobalCloudObjects; + std::unordered_map> m_collisionStateObjects; }; void wrapModel(nb::module_ &m); diff --git a/bindings/Mandos/python/RigidBody.cpp b/bindings/Mandos/python/RigidBody.cpp index 9a77dc07f53f83f697ff6c611120365f891310fb..7eb4d9a19de4a9e7c0afb9792fadda5943f06665 100644 --- a/bindings/Mandos/python/RigidBody.cpp +++ b/bindings/Mandos/python/RigidBody.cpp @@ -5,7 +5,9 @@ #include "Mandos/Core/Energies/CosseratRodAlignment.hpp" #include "Mandos/Core/Energies/MassSpring.hpp" #include "Mandos/Core/Energies/PlaneField.hpp" +#include "Mandos/Core/Energies/RigidBodyInertia.hpp" #include "Mandos/Core/Mesh.hpp" +#include "Mandos/Core/Projections/FixedProjection.hpp" #include "Mandos/Core/linear_algebra.hpp" #include #include @@ -37,9 +39,13 @@ auto wrapRigidBodyCloud(nb::module_ &m, const char *name) .def_prop_rw("mass", &RBCloud::mass, &RBCloud::setMass) .def_prop_rw("inertiaTensor", &RBCloud::inertiaTensor, &RBCloud::setInertiaTensor) .def("enable_gravity", - [](RBCloud &self) { self.simObject().template potential().enable(); }) + [](RBCloud &self) { + self.registry().template get(self.handle().entt).enable(); + }) .def("disable_gravity", - [](RBCloud &self) { self.simObject().template potential().disable(); }) + [](RBCloud &self) { + self.registry().template get(self.handle().entt).disable(); + }) .def_prop_ro("mass_spring", &RBCloud::massSpring) .def_prop_ro("cosserat_bending_rod", &RBCloud::cosseratBendingRod) .def_prop_ro("cosserat_rod_alignment", &RBCloud::cosseratRodAlignment) @@ -47,7 +53,7 @@ auto wrapRigidBodyCloud(nb::module_ &m, const char *name) .def("get_transform", [](const RBCloud &self, std::size_t index) { mandos::core::Mat4 transform = mandos::core::Mat4::Zero(); - mandos::core::Vec6 x = self.simObject().mstate.m_x[index].template segment<6>(0); + mandos::core::Vec6 x = self.handle().mstate().m_x[index].template segment<6>(0); transform.block<3, 3>(0, 0) = mandos::core::rotationExpMap(x.segment<3>(3)); // Rotation transform.block<3, 1>(0, 3) = x.segment<3>(0); // Position transform(3, 3) = 1.0; @@ -70,9 +76,10 @@ auto wrapRigidBodyCloud(nb::module_ &m, const char *name) mandos::core::Scalar contactOffset, int nbVoxels, bool flip = false) { - auto &sdfs = std::get>(self.simObject().colliders()); + auto &sdfs = + self.registry().template get>(self.handle().entt); auto &sdf = sdfs.emplace_back(mesh, contactOffset, rbIndex, nbVoxels, flip); - sdf.update(self.simObject().mstate); + sdf.update(self.handle().mstate()); return mandos::core::collisions::SimulationCollider( self.handle(), static_cast(sdfs.size() - 1)); }, @@ -90,15 +97,15 @@ namespace mandos::py void wrapRigidBody(nb::module_ &m) { nb::class_>( - m, "SimulationCollider") - .def_prop_ro( - "surface_mesh", - [](const mandos::core::collisions::SimulationCollider &self) { - return self.collider().mesh(); - }) - .def("distance", - [](const mandos::core::collisions::SimulationCollider &collider, - const mandos::core::Vec3 &v) { return collider.collider().vdb().distance(v); }); + m, "SimulationCollider"); + // .def_prop_ro( + // "surface_mesh", + // [](const mandos::core::collisions::SimulationCollider &self) { + // return self.collider().mesh(self.registry()); + // }) + // .def("distance", + // [](const mandos::core::collisions::SimulationCollider &collider, + // const mandos::core::Vec3 &v) { return collider.collider(self.registry()).vdb().distance(v); }); nb::class_>(m, "RigidBody3D") .def_prop_rw("x", &RigidBody3D::x, &RigidBody3D::setX) @@ -111,7 +118,7 @@ void wrapRigidBody(nb::module_ &m) &RigidBody3D::setInertiaTensor) .def_prop_ro("plane_field", [](RigidBody3D &rb) -> mandos::core::PlaneField & { - return rb.simObject().dissipativePotential(); + return rb.registry().get(rb.handle().entt); }) .def( "add_sdf", @@ -120,9 +127,9 @@ void wrapRigidBody(nb::module_ &m) mandos::core::Scalar contactOffset, int nbVoxels, bool flip = false) { - auto &sdfs = std::get>(rb.simObject().colliders()); + auto &sdfs = rb.registry().get>(rb.handle().entt); auto &sdf = sdfs.emplace_back(mesh, contactOffset, 0, nbVoxels, flip); - sdf.update(rb.simObject().mstate); + sdf.update(rb.handle().mstate()); return mandos::core::collisions::SimulationCollider( rb.handle(), static_cast(sdfs.size() - 1)); }, @@ -132,17 +139,21 @@ void wrapRigidBody(nb::module_ &m) nb::arg("flip") = false) .def("fix", [](RigidBody3D &self) { - std::get(self.simObject().projections).indices().emplace_back(0); - std::get(self.simObject().projections).indices().emplace_back(1); - std::get(self.simObject().projections).indices().emplace_back(2); - std::get(self.simObject().projections).indices().emplace_back(3); - std::get(self.simObject().projections).indices().emplace_back(4); - std::get(self.simObject().projections).indices().emplace_back(5); + self.registry().get(self.handle().entt).indices().emplace_back(0); + self.registry().get(self.handle().entt).indices().emplace_back(1); + self.registry().get(self.handle().entt).indices().emplace_back(2); + self.registry().get(self.handle().entt).indices().emplace_back(3); + self.registry().get(self.handle().entt).indices().emplace_back(4); + self.registry().get(self.handle().entt).indices().emplace_back(5); }) .def("enable_gravity", - [](RigidBody3D &self) { self.simObject().potential().enable(); }) + [](RigidBody3D &self) { + self.registry().get(self.handle().entt).enable(); + }) .def("disable_gravity", - [](RigidBody3D &self) { self.simObject().potential().disable(); }) + [](RigidBody3D &self) { + self.registry().get(self.handle().entt).disable(); + }) .def("get_transform", [](const RigidBody3D &self) { mandos::core::Mat4 transform = mandos::core::Mat4::Zero(); transform.block<3, 3>(0, 0) = mandos::core::rotationExpMap(self.x().segment<3>(3)); // Rotation @@ -163,11 +174,11 @@ void wrapRigidBody(nb::module_ &m) &RigidBody3D::setInertiaTensor) .def("enable_gravity", [](RigidBody3D &self) { - self.simObject().potential().enable(); + self.registry().get(self.handle().entt).enable(); }) .def("disable_gravity", [](RigidBody3D &self) { - self.simObject().potential().disable(); + self.registry().get(self.handle().entt).disable(); }) .def("get_transform", [](const RigidBody3D &self) { mandos::core::Mat4 transform = mandos::core::Mat4::Zero(); @@ -195,31 +206,21 @@ void wrapRigidBody(nb::module_ &m) } template -RigidBody3D::RigidBody3D(core::SimulationObjectHandle handle) +RigidBody3D::RigidBody3D(core::SimulationObject handle, entt::registry ®istry) : m_handle(handle) + , m_registry(registry) { - if (simObject().mstate.m_x.size() != 1) { - simObject().mstate.resize(1); + if (handle.mstate().m_x.size() != 1) { + handle.mstate().resize(1); - simObject().template inertia().mass().resize(1); - simObject().template inertia().inertiaTensor().resize(1); - simObject().template potential().vertexMass().resize(1); + registry.get(handle.entt).mass().resize(1); + registry.get(handle.entt).inertiaTensor().resize(1); + registry.get(handle.entt).vertexMass().resize(1); } } template -mandos::core::SimulationObject &RigidBody3D::simObject() -{ - return m_handle.simulationObject(); -} -template -const mandos::core::SimulationObject &RigidBody3D::simObject() const -{ - return m_handle.simulationObject(); -} - -template -mandos::core::SimulationObjectHandle RigidBody3D::handle() +mandos::core::SimulationObject RigidBody3D::handle() const { return this->m_handle; } @@ -227,158 +228,155 @@ mandos::core::SimulationObjectHandle RigidBody3D::handle() template Eigen::Map RigidBody3D::x() const { - return {simObject().mstate.m_x[0].data(), 6}; + return {m_handle.mstate().m_x[0].data(), 6}; } template void RigidBody3D::setX(const mandos::core::Vec6 &x) { - simObject().mstate.m_x[0] = x; + m_handle.mstate().m_x[0] = x; } template Eigen::Map RigidBody3D::v() const { - return {simObject().mstate.m_v[0].data(), 6}; + return {m_handle.mstate().m_v[0].data(), 6}; } template void RigidBody3D::setV(const mandos::core::Vec6 &v) { - simObject().mstate.m_v[0] = v; + m_handle.mstate().m_v[0] = v; } template Eigen::Map RigidBody3D::grad() const { - return {simObject().mstate.m_grad[0].data(), 6}; + return {m_handle.mstate().m_grad[0].data(), 6}; } template Eigen::Map RigidBody3D::hessian() const { - return {simObject().mstate.m_hessian.toDense().data(), 6, 6}; + return {m_handle.mstate().m_hessian.toDense().data(), 6, 6}; } template void RigidBody3D::setMass(core::Scalar mass) { - simObject().template inertia().mass()[0] = mass; - simObject().template potential().vertexMass()[0] = mass; + m_registry.get(m_handle.entt).mass()[0] = mass; + m_registry.get(m_handle.entt).vertexMass()[0] = mass; } template mandos::core::Scalar RigidBody3D::mass() { - return simObject().template inertia().mass()[0]; + return m_registry.get(m_handle.entt).mass()[0]; } template void RigidBody3D::setInertiaTensor(const core::Mat3 &inertiaTensor) { - simObject().template inertia().inertiaTensor()[0] = inertiaTensor; + m_registry.get(m_handle.entt).inertiaTensor()[0] = inertiaTensor; } template const mandos::core::Mat3 &RigidBody3D::inertiaTensor() const { - return simObject().template inertia().inertiaTensor()[0]; + return m_registry.get(m_handle.entt).inertiaTensor()[0]; } template -RigidBodyCloud3D::RigidBodyCloud3D(core::SimulationObjectHandle handle) +RigidBodyCloud3D::RigidBodyCloud3D(core::SimulationObject handle, entt::registry ®istry) : m_handle(handle) + , m_registry(registry) { } -template -mandos::core::SimulationObject &RigidBodyCloud3D::simObject() -{ - return m_handle.simulationObject(); -} -template -const mandos::core::SimulationObject &RigidBodyCloud3D::simObject() const -{ - return m_handle.simulationObject(); -} + template void RigidBodyCloud3D::resize(int n) { - simObject().mstate.resize(n); + m_handle.mstate().resize(n); } template int RigidBodyCloud3D::size() const { - return static_cast(simObject().mstate.m_x.size()); + return static_cast(m_handle.mstate().m_x.size()); } template Eigen::Map> RigidBodyCloud3D::x() const { - return {simObject().mstate.m_x.data()->data(), static_cast(simObject().mstate.m_x.size()), 6}; + const auto &mstate = m_handle.mstate(); + return {mstate.m_x.data()->data(), static_cast(mstate.m_x.size()), 6}; } template void RigidBodyCloud3D::setX(const Eigen::Matrix &x) { + auto &mstate = m_handle.mstate(); Eigen::Matrix::MapType( - simObject().mstate.m_x.data()->data(), static_cast(simObject().mstate.m_x.size()), 6) = x; + mstate.m_x.data()->data(), static_cast(mstate.m_x.size()), 6) = x; } template Eigen::Map> RigidBodyCloud3D::v() const { - return {simObject().mstate.m_v.data()->data(), // - static_cast(simObject().mstate.m_v.size()), + const auto &mstate = m_handle.mstate(); + return {mstate.m_v.data()->data(), // + static_cast(mstate.m_v.size()), 6}; } template void RigidBodyCloud3D::setV(const Eigen::Matrix &v) { + auto &mstate = m_handle.mstate(); Eigen::Matrix::MapType( - simObject().mstate.m_v.data()->data(), static_cast(simObject().mstate.m_v.size()), 6) = v; + mstate.m_v.data()->data(), static_cast(mstate.m_v.size()), 6) = v; } template void RigidBodyCloud3D::setMass(const std::vector &mass) { - simObject().template inertia().mass() = mass; - simObject().template potential().vertexMass() = mass; + m_registry.get(m_handle.entt).mass() = mass; + m_registry.get(m_handle.entt).vertexMass() = mass; } template const std::vector &RigidBodyCloud3D::mass() { - return simObject().template inertia().mass(); + return m_registry.get(m_handle.entt).mass(); } template void RigidBodyCloud3D::setInertiaTensor(const std::vector &inertiaTensor) { - simObject().template inertia().inertiaTensor() = inertiaTensor; + m_registry.get(m_handle.entt).inertiaTensor() = inertiaTensor; } template const std::vector &RigidBodyCloud3D::inertiaTensor() const { - return simObject().template inertia().inertiaTensor(); + return m_registry.get(m_handle.entt).inertiaTensor(); } template Eigen::Map> RigidBodyCloud3D::grad() const { - return {simObject().mstate.m_grad.data()->data(), // - static_cast(simObject().mstate.m_grad.size()), + const auto &mstate = m_handle.mstate(); + return {mstate.m_grad.data()->data(), // + static_cast(mstate.m_grad.size()), 6}; } template const mandos::core::SparseMat &RigidBodyCloud3D::hessian() const { - return simObject().mstate.m_hessian; + return m_handle.mstate().m_hessian; } template mandos::core::MassSpring &RigidBodyCloud3D::massSpring() { - return simObject().template potential(); + return m_registry.get(m_handle.entt); } template mandos::core::CosseratBendingRod &RigidBodyCloud3D::cosseratBendingRod() { - return simObject().template potential(); + return m_registry.get(m_handle.entt); } template mandos::core::CosseratRodAlignment &RigidBodyCloud3D::cosseratRodAlignment() { - return simObject().template potential(); + return m_registry.get(m_handle.entt); } template <> @@ -391,7 +389,7 @@ mandos::core::AirDrag &RigidBodyCloud3D::airDrag() template <> mandos::core::AirDrag &RigidBodyCloud3D::airDrag() { - return simObject().template dissipativePotential(); + return m_registry.get(m_handle.entt); } template @@ -424,11 +422,11 @@ void RigidBodyCloud3D::clearFixing() template std::vector &RigidBodyCloud3D::getFixedDofVector() { - return std::get(simObject().projections).indices(); + return m_registry.get(m_handle.entt).indices(); } template -mandos::core::SimulationObjectHandle RigidBodyCloud3D::handle() const +mandos::core::SimulationObject RigidBodyCloud3D::handle() const { return m_handle; } diff --git a/bindings/Mandos/python/RigidBody.hpp b/bindings/Mandos/python/RigidBody.hpp index 34115bfddba78653c699d93f7e0932fb29e72f74..7926cf4461d6a3907a1ecd11945a1b62e1c764db 100644 --- a/bindings/Mandos/python/RigidBody.hpp +++ b/bindings/Mandos/python/RigidBody.hpp @@ -3,15 +3,18 @@ #include +#include +#include + #include #include #include -#include -#include - -#include +#include #include #include +#include + +#include namespace nb = nanobind; @@ -20,12 +23,13 @@ namespace mandos::py template struct RigidBody3D { - explicit RigidBody3D(core::SimulationObjectHandle handle); + explicit RigidBody3D(core::SimulationObject handle, entt::registry ®istry); mandos::core::SimulationObject &simObject(); - const mandos::core::SimulationObject &simObject() const; - mandos::core::SimulationObjectHandle handle(); + mandos::core::SimulationObject handle() const; + const entt::registry ®istry() const; + entt::registry ®istry(); Eigen::Map x() const; @@ -48,19 +52,32 @@ struct RigidBody3D { const mandos::core::Mat3 &inertiaTensor() const; private: - core::SimulationObjectHandle m_handle; + core::SimulationObject m_handle; + entt::registry &m_registry; }; +template +inline entt::registry &RigidBody3D::registry() +{ + return m_registry; +} + +template +inline const entt::registry &RigidBody3D::registry() const +{ + return m_registry; +} + extern template struct RigidBody3D; extern template struct RigidBody3D; template struct RigidBodyCloud3D { - explicit RigidBodyCloud3D(core::SimulationObjectHandle handle); + RigidBodyCloud3D(core::SimulationObject handle, entt::registry ®istry); - mandos::core::SimulationObject &simObject(); - const mandos::core::SimulationObject &simObject() const; - mandos::core::SimulationObjectHandle handle() const; + mandos::core::SimulationObject handle() const; + const entt::registry ®istry() const; + entt::registry ®istry(); void resize(int n); @@ -99,8 +116,20 @@ struct RigidBodyCloud3D { std::vector &getFixedDofVector(); private: - core::SimulationObjectHandle m_handle; + core::SimulationObject m_handle; + entt::registry &m_registry; }; +template +inline const entt::registry &RigidBodyCloud3D::registry() const +{ + return m_registry; +} + +template +inline entt::registry &RigidBodyCloud3D::registry() +{ + return m_registry; +} template <> mandos::core::AirDrag &RigidBodyCloud3D::airDrag(); diff --git a/bindings/Mandos/python/RigidBodyPointMapping.cpp b/bindings/Mandos/python/RigidBodyPointMapping.cpp index a1ecd9b74e8b104c760ad21dd818607b38dcd48f..6d370e2ec5c6fd0036b690b764eb57185ceda0b6 100644 --- a/bindings/Mandos/python/RigidBodyPointMapping.cpp +++ b/bindings/Mandos/python/RigidBodyPointMapping.cpp @@ -9,18 +9,20 @@ namespace mandos::py { RigidBodyPointMapping::RigidBodyPointMapping(RigidBodyCloud3DLocal &rigidBodyCloud, Model &model) - : m_deformable(model.add()) + : m_deformable(model.add(), model.registry()) , m_rigidBodyCloud(rigidBodyCloud) { - auto &mappings = m_rigidBodyCloud.simObject().mappings(); + auto &mappings = + m_rigidBodyCloud.registry().get>(m_rigidBodyCloud.handle().entt); m_mapping_index = static_cast(mappings.size()); mappings.emplace_back(m_rigidBodyCloud.handle(), m_deformable.handle()); } core::RigidBodyPointMapping &RigidBodyPointMapping::mapping() { - return m_rigidBodyCloud.simObject().mappings()[m_mapping_index]; + return m_rigidBodyCloud.registry().get>( + m_rigidBodyCloud.handle().entt)[m_mapping_index]; } void RigidBodyPointMapping::addParticle(const core::Vec3 &localPos, int rigidBodyIndex) @@ -34,23 +36,25 @@ void RigidBodyPointMapping::addParticle(const core::Vec3 &localPos, int rigidBod m_deformable.setX(Eigen::Matrix::Zero(m.size(), 3)); m_deformable.setV(Eigen::Matrix::Zero(m.size(), 3)); - mapping().apply(mapping().from()->mstate.m_x, mapping().to()->mstate.m_x); - mapping().applyJ(mapping().from()->mstate.m_v, mapping().to()->mstate.m_v); + mapping().apply(mapping().from().mstate().m_x, mapping().to().mstate().m_x); + mapping().applyJ(mapping().from().mstate().m_v, mapping().to().mstate().m_v); } RigidBodyGlobalPointMapping::RigidBodyGlobalPointMapping(RigidBodyCloud3DGlobal &rigidBodyCloud, Model &model) - : m_deformable(model.add()) + : m_deformable(model.add(), model.registry()) , m_rigidBodyCloud(rigidBodyCloud) { - auto &mappings = m_rigidBodyCloud.simObject().mappings(); + auto &mappings = + m_rigidBodyCloud.registry().get>(m_rigidBodyCloud.handle().entt); m_mapping_index = static_cast(mappings.size()); mappings.emplace_back(m_rigidBodyCloud.handle(), m_deformable.handle()); } core::RigidBodyGlobalPointMapping &RigidBodyGlobalPointMapping::mapping() { - return m_rigidBodyCloud.simObject().mappings()[m_mapping_index]; + return m_rigidBodyCloud.registry().get>( + m_rigidBodyCloud.handle().entt)[m_mapping_index]; } void RigidBodyGlobalPointMapping::addParticle(const core::Vec3 &localPos, int rigidBodyIndex) @@ -64,8 +68,8 @@ void RigidBodyGlobalPointMapping::addParticle(const core::Vec3 &localPos, int ri m_deformable.setX(Eigen::Matrix::Zero(m.size(), 3)); m_deformable.setV(Eigen::Matrix::Zero(m.size(), 3)); - mapping().apply(mapping().from()->mstate.m_x, mapping().to()->mstate.m_x); - mapping().applyJ(mapping().from()->mstate.m_v, mapping().to()->mstate.m_v); + mapping().apply(mapping().from().mstate().m_x, mapping().to().mstate().m_x); + mapping().applyJ(mapping().from().mstate().m_v, mapping().to().mstate().m_v); } void wrapRigidBodyPointMapping(nb::module_ &m) diff --git a/bindings/Mandos/python/RigidBodyPointMapping.hpp b/bindings/Mandos/python/RigidBodyPointMapping.hpp index fc85ef747d37c60c9fdfc2cf14899dc580082c8a..9da307140bc94aaad0d37631b1e715d0ebf16c2d 100644 --- a/bindings/Mandos/python/RigidBodyPointMapping.hpp +++ b/bindings/Mandos/python/RigidBodyPointMapping.hpp @@ -7,6 +7,9 @@ #include #include +#include +#include + namespace nb = nanobind; namespace mandos::py diff --git a/conanfile.py b/conanfile.py index b227ca9395235c466de797c3738c90d11f55103b..4f2fca955b31d725087f83b6a454cb58501f4c20 100644 --- a/conanfile.py +++ b/conanfile.py @@ -52,6 +52,7 @@ class Mandos(ConanFile): self.requires("tinyad/cci.20240718") # Auto diff capabilities self.requires("onetbb/2021.12.0", force=True) # Multithreading used by some dependencies and Mandos itself self.requires("boost/1.84.0", override=True) + self.requires("entt/3.15.0") def configure(self): if self.options.shared: diff --git a/examples/python/CollisionMeshes/Untitled-1.ipynb b/examples/python/CollisionMeshes/Untitled-1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..9497d5082d7c33d09812a8652a4cc70bdd5a5ec3 --- /dev/null +++ b/examples/python/CollisionMeshes/Untitled-1.ipynb @@ -0,0 +1,124 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 13, + "id": "ca2c71cd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: pytriangle in /workspaces/mandos/.venv/lib/python3.12/site-packages (2.2)\n", + "Requirement already satisfied: numpy in /workspaces/mandos/.venv/lib/python3.12/site-packages (2.2.6)\n", + "Collecting meshio\n", + " Downloading meshio-5.3.5-py3-none-any.whl.metadata (11 kB)\n", + "Requirement already satisfied: numpy>=1.20.0 in /workspaces/mandos/.venv/lib/python3.12/site-packages (from meshio) (2.2.6)\n", + "Collecting rich (from meshio)\n", + " Downloading rich-14.0.0-py3-none-any.whl.metadata (18 kB)\n", + "Collecting markdown-it-py>=2.2.0 (from rich->meshio)\n", + " Downloading markdown_it_py-3.0.0-py3-none-any.whl.metadata (6.9 kB)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /workspaces/mandos/.venv/lib/python3.12/site-packages (from rich->meshio) (2.19.1)\n", + "Collecting mdurl~=0.1 (from markdown-it-py>=2.2.0->rich->meshio)\n", + " Downloading mdurl-0.1.2-py3-none-any.whl.metadata (1.6 kB)\n", + "Downloading meshio-5.3.5-py3-none-any.whl (166 kB)\n", + "Downloading rich-14.0.0-py3-none-any.whl (243 kB)\n", + "Downloading markdown_it_py-3.0.0-py3-none-any.whl (87 kB)\n", + "Downloading mdurl-0.1.2-py3-none-any.whl (10.0 kB)\n", + "Installing collected packages: mdurl, markdown-it-py, rich, meshio\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m4/4\u001b[0m [meshio]2m3/4\u001b[0m [meshio]\n", + "\u001b[1A\u001b[2KSuccessfully installed markdown-it-py-3.0.0 mdurl-0.1.2 meshio-5.3.5 rich-14.0.0\n" + ] + } + ], + "source": [ + "!pip install pytriangle\n", + "!pip install numpy\n", + "!pip install meshio" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "6db8a16a", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "boundary = [(0,0), (0,0.1), (2,0.1), (2,0)]\n", + "marker = [1,1,1,1]\n", + "\n", + "import triangle\n", + "\n", + "t = triangle.Triangle()\n", + "t.set_points(boundary, markers=marker)\n", + "\n", + "t.set_segments([(0,1), (1,2), (2,3), (3,0)])\n", + "\n", + "\n", + "t.triangulate(area=0.0001)\n", + "t.refine(area_ratio=0.5)\n", + "points = t.get_points()\n", + "triangles = t.get_triangles()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "b886b073", + "metadata": {}, + "outputs": [], + "source": [ + "triangles\n", + "\n", + "import numpy as np\n", + "import meshio\n", + "F = [t for t, _, _ in triangles]\n", + "\n", + "points = np.array([[p[0], p[1], 0] for p, _ in points])\n", + "\n", + "mesh = meshio.Mesh(points, [(\"triangle\", F)])" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "c59d79f5", + "metadata": {}, + "outputs": [], + "source": [ + "meshio.write(\"scarf.obj\", mesh)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31bfc116", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/python/CollisionMeshes/collisions.py b/examples/python/CollisionMeshes/collisions.py index b171960f892b3b302594bce16cd49772514975ab..3625e4293a97e19a988932cfd308c93abeecd3a3 100644 --- a/examples/python/CollisionMeshes/collisions.py +++ b/examples/python/CollisionMeshes/collisions.py @@ -1,7 +1,7 @@ import sys import numpy as np import meshio -# import polyscope as ps +import polyscope as ps import pymandos @@ -85,8 +85,8 @@ def create_model(): print("Finished") - # ps.register_volume_mesh("Beam0", beam.x, tets=tetras) - # ps.register_volume_mesh("Beam1", beam2.x, tets=tetras) + ps.register_volume_mesh("Beam0", beam.x, tets=tetras) + ps.register_volume_mesh("Beam1", beam2.x, tets=tetras) return model @@ -95,41 +95,27 @@ frame = 0 def simulate_callback(model): global frame step_parameters = pymandos.StepParameters() - step_parameters.h = 0.002 - step_parameters.newton_iterations =50 + step_parameters.h = 0.001 + step_parameters.newton_iterations = 50 step_parameters.cg_iterations = 20 step_parameters.cg_error = 1e-4 step_parameters.grad_norm = 1e-2 step_parameters.line_search_iterations = 5 + step_parameters.accept_failed_solution = True print(pymandos.step(model, step_parameters)) - beam_volume = meshio.read("beam.msh") beam0 = model.get_deformable3d("beam0") beam1 = model.get_deformable3d("beam1") - beam_volume.points = beam0.x - meshio.write(f"beam_0_{frame:04d}.vtu", beam_volume) - - beam_volume.points = beam1.x - meshio.write(f"beam_1_{frame:04d}.vtu", beam_volume) - - frame = frame + 1 - - # ps.get_volume_mesh("Beam0").update_vertex_positions(beam0.x) - # ps.get_volume_mesh("Beam1").update_vertex_positions(beam1.x) - + ps.get_volume_mesh("Beam0").update_vertex_positions(beam0.x) + ps.get_volume_mesh("Beam1").update_vertex_positions(beam1.x) -import time if __name__ == "__main__": - # ps.init() + ps.init() model = create_model() - import time - time.sleep(10) - for i in range(1000): - simulate_callback(model) - # ps.set_user_callback(lambda: simulate_callback(model)) + ps.set_user_callback(lambda: simulate_callback(model)) - # ps.show() + ps.show() diff --git a/examples/python/CollisionMeshes/scarf.obj b/examples/python/CollisionMeshes/scarf.obj new file mode 100644 index 0000000000000000000000000000000000000000..8ac597fe3b7670af917673af72133481dc64d332 --- /dev/null +++ b/examples/python/CollisionMeshes/scarf.obj @@ -0,0 +1,4825 @@ +# Created by meshio v5.3.5, 2025-05-29T19:48:19.036864 +v 0.0 0.0 0.0 +v 0.0 0.1 0.0 +v 2.0 0.1 0.0 +v 2.0 0.0 0.0 +v 1.0 0.1 0.0 +v 1.0 0.0 0.0 +v 0.5 0.0 0.0 +v 0.5 0.1 0.0 +v 1.5 0.0 0.0 +v 1.5 0.1 0.0 +v 0.75 0.1 0.0 +v 0.75 0.0 0.0 +v 0.25 0.0 0.0 +v 0.25 0.1 0.0 +v 1.75 0.1 0.0 +v 1.75 0.0 0.0 +v 1.25 0.0 0.0 +v 1.25 0.1 0.0 +v 0.625 0.0 0.0 +v 0.625 0.1 0.0 +v 0.875 0.0 0.0 +v 0.875 0.1 0.0 +v 0.375 0.1 0.0 +v 0.375 0.0 0.0 +v 0.125 0.1 0.0 +v 0.125 0.0 0.0 +v 1.625 0.0 0.0 +v 1.625 0.1 0.0 +v 1.875 0.0 0.0 +v 1.875 0.1 0.0 +v 1.375 0.1 0.0 +v 1.375 0.0 0.0 +v 1.125 0.1 0.0 +v 1.125 0.0 0.0 +v 0.5625 0.1 0.0 +v 0.51125 0.049999999999999996 0.0 +v 0.4375 0.1 0.0 +v 0.5625 0.0 0.0 +v 0.53125 0.1 0.0 +v 0.5062423780487805 0.07513891006097562 0.0 +v 0.515625 0.1 0.0 +v 0.5031942403796241 0.08758779751577161 0.0 +v 0.46875 0.1 0.0 +v 0.48008796888886424 0.07533256040041669 0.0 +v 0.4931896425915531 0.07854053422837316 0.0 +v 0.4914143850273857 0.057432622121286366 0.0 +v 0.484375 0.1 0.0 +v 0.48409072427676264 0.08734315712243264 0.0 +v 0.49358783565330216 0.09173273514804889 0.0 +v 0.46966160975268356 0.08547963387114929 0.0 +v 0.49729929544433027 0.06756628733897854 0.0 +v 0.4866695346107232 0.06696369387557119 0.0 +v 0.4375 0.0 0.0 +v 0.48021542417796265 0.05780404657066695 0.0 +v 0.5027885475044 0.05760292017216817 0.0 +v 0.4696319737435124 0.06649181130129592 0.0 +v 0.46875 0.0 0.0 +v 0.5162386910917226 0.0640619468581337 0.0 +v 0.4854709358208888 0.04724719984725821 0.0 +v 0.4991860024278284 0.040892741842806106 0.0 +v 0.4760037308148078 0.09316660063569177 0.0 +v 0.5281179353369229 0.08748323398124644 0.0 +v 0.5199702637192635 0.07747849971027714 0.0 +v 0.5070982132424097 0.06586222513078138 0.0 +v 0.5415661519530851 0.047160761346799786 0.0 +v 0.49896526448570017 0.020433875398977747 0.0 +v 0.453125 0.1 0.0 +v 0.5403871535032522 0.06917138421355812 0.0 +v 0.5288411878872159 0.06411995193521443 0.0 +v 0.5314500512138897 0.07644959597760762 0.0 +v 0.546875 0.1 0.0 +v 0.5427426745447035 0.08587988645526637 0.0 +v 0.5383847871511271 0.05802723956696161 0.0 +v 0.5261753411117919 0.04942329741293502 0.0 +v 0.5614348543159929 0.059637595239841856 0.0 +v 0.549706340632706 0.06174496030953559 0.0 +v 0.5585681166719961 0.07737393301611 0.0 +v 0.5501270338177968 0.07183326524098108 0.0 +v 0.4950093507626265 0.030707181182281388 0.0 +v 0.46195627516522597 0.042127502025918376 0.0 +v 0.5099094411037309 0.0305464188405138 0.0 +v 0.485438267812199 0.021123371158917853 0.0 +v 0.4609375 0.09222072070228766 0.0 +v 0.4453125 0.08434156649874636 0.0 +v 0.4577590386151833 0.07909147025592173 0.0 +v 0.5390625 0.09462163903765836 0.0 +v 0.5546875 0.0900489006503605 0.0 +v 0.59375 0.1 0.0 +v 0.5189244747431528 0.05519324323130599 0.0 +v 0.48199699555567616 0.034223473779293495 0.0 +v 0.47306669459841927 0.047658598594694374 0.0 +v 0.46227108002197126 0.05541955174970856 0.0 +v 0.5183273480107303 0.0397393153865666 0.0 +v 0.5996204711183275 0.06782806049628863 0.0 +v 0.5321464034283832 0.03656225457661423 0.0 +v 0.53125 0.0 0.0 +v 0.5794501019646967 0.08224733303779276 0.0 +v 0.46708932873558 0.0759897142496698 0.0 +v 0.5750213687848265 0.07093343918589458 0.0 +v 0.5684091947718508 0.08731845126057819 0.0 +v 0.49000807844265404 0.03906178403643205 0.0 +v 0.46599889267967937 0.023018878400032212 0.0 +v 0.484375 0.0 0.0 +v 0.4572640632378863 0.06673115095896272 0.0 +v 0.43738737587673965 0.0511688815710314 0.0 +v 0.44929212499053667 0.05643845287747731 0.0 +v 0.4427481623579717 0.06974048326095258 0.0 +v 0.448235157060179 0.04274421204092658 0.0 +v 0.5527816198879668 0.02770737078742557 0.0 +v 0.5681831326078921 0.07770054591023459 0.0 +v 0.578125 0.1 0.0 +v 0.5877133070369845 0.07248902455830832 0.0 +v 0.5844450906230892 0.04660001131733646 0.0 +v 0.5992333705732584 0.0843789929838885 0.0 +v 0.5893504631314586 0.05913155512674173 0.0 +v 0.5894471392961268 0.08233495051401732 0.0 +v 0.4760689344580857 0.025664182516975224 0.0 +v 0.4709405656117588 0.03554852561316666 0.0 +v 0.4741907794551182 0.012324095049241167 0.0 +v 0.40625 0.1 0.0 +v 0.42619390192748136 0.025559679695426517 0.0 +v 0.45456290614062567 0.030581422410181282 0.0 +v 0.5439372695735564 0.03556805886666305 0.0 +v 0.5365479555953565 0.018348172410992975 0.0 +v 0.558677690551346 0.043247154070472735 0.0 +v 0.5431076229704492 0.025728696307440162 0.0 +v 0.546875 0.0 0.0 +v 0.49166365351260805 0.010221563664261458 0.0 +v 0.515625 0.0 0.0 +v 0.5757568962430553 0.05722679176283067 0.0 +v 0.6116916542376654 0.043160418405913004 0.0 +v 0.43293884913988684 0.07898898342754966 0.0 +v 0.43503647644451504 0.03694556410504854 0.0 +v 0.5265613154997594 0.015273514948912277 0.0 +v 0.5094130856939735 0.02010522096655301 0.0 +v 0.5038799572611791 0.010439610505089509 0.0 +v 0.5221231834862999 0.024733403063431577 0.0 +v 0.5672165429202901 0.05023788978944935 0.0 +v 0.5729078199618464 0.03457599500675873 0.0 +v 0.6025391274661596 0.053968958049037896 0.0 +v 0.4357927582547345 0.08937003027134537 0.0 +v 0.421875 0.1 0.0 +v 0.4280102056333478 0.06393515891989773 0.0 +v 0.44078697048058024 0.02335295149849867 0.0 +v 0.453125 0.0 0.0 +v 0.531666923514317 0.026807512089648364 0.0 +v 0.5975063074200773 0.04042784492918161 0.0 +v 0.6285002745049093 0.06667312161459703 0.0 +v 0.41233932219806935 0.045446136421392044 0.0 +v 0.4155093628263417 0.028834674258435618 0.0 +v 0.4445349750973565 0.032951292269155885 0.0 +v 0.5938082922992124 0.049508114373293964 0.0 +v 0.5861873774339046 0.033381131562493435 0.0 +v 0.6092410589360612 0.07408015634200713 0.0 +v 0.6139144379202869 0.06360141876095414 0.0 +v 0.4223639329415652 0.037660762504482456 0.0 +v 0.42434818186829104 0.050562359521270535 0.0 +v 0.4048550613797997 0.025462608737462575 0.0 +v 0.4128501958546933 0.06089878832855206 0.0 +v 0.40625 0.0 0.0 +v 0.421875 0.0 0.0 +v 0.6197834061313482 0.07189883020167452 0.0 +v 0.6066122160888839 0.03134301639768938 0.0 +v 0.6164321571098055 0.0156897315789592 0.0 +v 0.628380636748453 0.028986702780540087 0.0 +v 0.6178584569118346 0.033509419502308964 0.0 +v 0.6302444121785523 0.047824185431235025 0.0 +v 0.6216611584408634 0.04273500623973844 0.0 +v 0.6206462568612812 0.054229424172287086 0.0 +v 0.61768901518428 0.08184394714617972 0.0 +v 0.41807101603593466 0.0741959255565657 0.0 +v 0.3820214184941954 0.05418323478910051 0.0 +v 0.43171598796805905 0.0127219095257886 0.0 +v 0.59375 0.0 0.0 +v 0.5654807226592403 0.023417760793861254 0.0 +v 0.6875 0.1 0.0 +v 0.6256906864821608 0.08917187689033768 0.0 +v 0.609375 0.1 0.0 +v 0.6157862910613243 0.09195425449578645 0.0 +v 0.6294993436373149 0.07822268764130559 0.0 +v 0.65625 0.1 0.0 +v 0.640625 0.1 0.0 +v 0.6518508263110664 0.0704712289748825 0.0 +v 0.6383421545498198 0.08743565675048573 0.0 +v 0.6396956320385613 0.0715226866112967 0.0 +v 0.6388508438875278 0.05812572211345871 0.0 +v 0.42676759889892124 0.07267572482106102 0.0 +v 0.40142094209461404 0.08217809706958511 0.0 +v 0.39828436599564837 0.05364557228900234 0.0 +v 0.42326178560282574 0.016691917949779395 0.0 +v 0.5624760763693291 0.03346861478754977 0.0 +v 0.5570236803597979 0.01932348024578122 0.0 +v 0.5660570626447716 0.011445820939872349 0.0 +v 0.578125 0.0 0.0 +v 0.5807639861448985 0.01815366819603286 0.0 +v 0.640625 0.0 0.0 +v 0.41142019201135005 0.08167926518161349 0.0 +v 0.4058682732774596 0.0708577700189285 0.0 +v 0.3883569318005221 0.07051198006123069 0.0 +v 0.4075253232798757 0.05334006086825316 0.0 +v 0.5789536906049925 0.027377945707434598 0.0 +v 0.6440876758920633 0.04500470885338027 0.0 +v 0.6354119290946297 0.037801970664105296 0.0 +v 0.6712652691140147 0.0603896601108852 0.0 +v 0.6875 0.0 0.0 +v 0.6495345527854713 0.054784218705566635 0.0 +v 0.666160044654749 0.039117723120357335 0.0 +v 0.6558677235520938 0.04485021961976581 0.0 +v 0.6551767834311111 0.03150384034265637 0.0 +v 0.6400599268606483 0.026882796981939978 0.0 +v 0.6610338072028634 0.05512950231054881 0.0 +v 0.65625 0.0 0.0 +v 0.6618835554438333 0.06605728658665226 0.0 +v 0.677334238524184 0.08103438756577709 0.0 +v 0.6715811549950735 0.07151121545457235 0.0 +v 0.6613255785758062 0.07839800947307352 0.0 +v 0.6925371657681079 0.06535073750112837 0.0 +v 0.6497244962254964 0.08263869603016381 0.0 +v 0.6828704022909965 0.0711905666137978 0.0 +v 0.6586897639229349 0.08917597281076593 0.0 +v 0.6946687036390862 0.09092586133391693 0.0 +v 0.671875 0.1 0.0 +v 0.6874627753907366 0.08025199612246108 0.0 +v 0.6831316514876894 0.09013419723071286 0.0 +v 0.6730264703125381 0.0174839036210315 0.0 +v 0.6246801362342022 0.02029506559444532 0.0 +v 0.623454737852001 0.009340391858184048 0.0 +v 0.609375 0.0 0.0 +v 0.666261071537321 0.027243200447601163 0.0 +v 0.655479924388746 0.0157439668233966 0.0 +v 0.6854840383490325 0.033344438027903565 0.0 +v 0.6749328014187983 0.03325466948699325 0.0 +v 0.6800853376401209 0.047766424924241185 0.0 +v 0.6456339613383572 0.018356467797087335 0.0 +v 0.6346306561637609 0.013636118763943501 0.0 +v 0.6998757505976123 0.04695326919803647 0.0 +v 0.682792028091722 0.0590506187816177 0.0 +v 0.6901432844489022 0.05132058696919434 0.0 +v 0.6328125 0.004701274341684996 0.0 +v 0.5166347694985376 0.08851488897495045 0.0 +v 0.4453125 0.09411973798501495 0.0 +v 0.5859375 0.09254640114962334 0.0 +v 0.5514899892196815 0.05118558697735241 0.0 +v 0.5749352683420338 0.04417774267391901 0.0 +v 0.4227717037377192 0.08507079785310132 0.0 +v 0.43761458942159576 0.06116280510614208 0.0 +v 0.4609375 0.010740125861273275 0.0 +v 0.45196011826628596 0.018800540912256924 0.0 +v 0.44345313485153803 0.010382395184096364 0.0 +v 0.5994774945211213 0.01596024968642781 0.0 +v 0.6015625 0.09396948136443455 0.0 +v 0.390625 0.1 0.0 +v 0.3895857610316203 0.03676010107853914 0.0 +v 0.4140625 0.009052639280392537 0.0 +v 0.41273151656262946 0.019093917168833647 0.0 +v 0.4008662876960086 0.012474574141156288 0.0 +v 0.39094397821329385 0.022628344910466145 0.0 +v 0.390625 0.0 0.0 +v 0.39662411090120875 0.030305416017116622 0.0 +v 0.4058082775662649 0.0364988809251198 0.0 +v 0.34987962569020437 0.030819948509662232 0.0 +v 0.3895402942423636 0.01133171112438315 0.0 +v 0.37570238710335624 0.018786691238862594 0.0 +v 0.38127583536650866 0.028830277945644203 0.0 +v 0.3769868828190854 0.0416438495583723 0.0 +v 0.3125 0.1 0.0 +v 0.3713377299363334 0.032628383936999954 0.0 +v 0.36213657979395814 0.04467151989278119 0.0 +v 0.3712102659900036 0.05124351868144483 0.0 +v 0.3726759763350873 0.06720269801900124 0.0 +v 0.3720269993426479 0.08771869362677398 0.0 +v 0.3125 0.0 0.0 +v 0.3540842801726101 0.04883160679996831 0.0 +v 0.5757776267004358 0.009609883917283964 0.0 +v 0.5869185581882194 0.008834738668695998 0.0 +v 0.3903653024840441 0.06033928265370976 0.0 +v 0.6458085748009342 0.03511366776162824 0.0 +v 0.6484375 0.09302943837087325 0.0 +v 0.6697672538886908 0.08912476147976019 0.0 +v 0.671875 0.0 0.0 +v 0.6696354588689737 0.04905687405665954 0.0 +v 0.6484375 0.007508688217192573 0.0 +v 0.5546875 0.008223660210018068 0.0 +v 0.5469375401689291 0.01565055320746113 0.0 +v 0.5390625 0.007683141454664075 0.0 +v 0.4140625 0.092314829008389 0.0 +v 0.6015625 0.006204215646403162 0.0 +v 0.6080427171653902 0.02133356156133464 0.0 +v 0.5958630064998577 0.026982665263262658 0.0 +v 0.3867427252065174 0.08546737069757548 0.0 +v 0.3929041988113171 0.0785675980875258 0.0 +v 0.39566102444775236 0.09086979279395549 0.0 +v 0.3828125 0.09430217254843198 0.0 +v 0.34375 0.1 0.0 +v 0.3787887446126205 0.0770440498366674 0.0 +v 0.38993304226466224 0.047264490840089636 0.0 +v 0.36043103686701716 0.03383197603806238 0.0 +v 0.34375 0.0 0.0 +v 0.36710784714392686 0.023685595261315986 0.0 +v 0.3794856633124792 0.00923876820064689 0.0 +v 0.359375 0.0 0.0 +v 0.3671875 0.00950151030217428 0.0 +v 0.3515625 0.014386353225853164 0.0 +v 0.3608174973867587 0.016558004728130807 0.0 +v 0.3650782807250498 0.05985358360422986 0.0 +v 0.35590368462200084 0.07694040687040442 0.0 +v 0.359375 0.1 0.0 +v 0.35074008492441516 0.06316134978721685 0.0 +v 0.35935826006943244 0.0677887913466666 0.0 +v 0.330854098409176 0.07847051504083861 0.0 +v 0.34314429510851185 0.07386485475676943 0.0 +v 0.3467633471209632 0.08685557154766284 0.0 +v 0.3294893221206125 0.05612759709413622 0.0 +v 0.3390362875373265 0.0629026434823371 0.0 +v 0.32867397657342057 0.06739054239141261 0.0 +v 0.341040629591915 0.049964239762151925 0.0 +v 0.3078421189148987 0.0772439376822152 0.0 +v 0.3230706661892616 0.030191455709775543 0.0 +v 0.3468355866868079 0.04102714909711618 0.0 +v 0.33352423153564315 0.03874316495758183 0.0 +v 0.31950309559370793 0.0749494961083884 0.0 +v 0.328125 0.1 0.0 +v 0.31009136120390424 0.05789584956829881 0.0 +v 0.3201642477909445 0.061113522467229724 0.0 +v 0.31893200669422517 0.04759567646764007 0.0 +v 0.3120657352916396 0.06793015615294122 0.0 +v 0.2954166454847352 0.06599467618992588 0.0 +v 0.35834725647656906 0.08836363636285921 0.0 +v 0.3515625 0.09487339019974601 0.0 +v 0.31590612700950965 0.08744807230753895 0.0 +v 0.28125 0.1 0.0 +v 0.296875 0.1 0.0 +v 0.33662079518642424 0.02429289464940787 0.0 +v 0.29413261234123367 0.03525439216953423 0.0 +v 0.32407797093597224 0.03962517842225873 0.0 +v 0.31023290662657776 0.0363328732015511 0.0 +v 0.3359375 0.09129137452399573 0.0 +v 0.3257916484615761 0.09004814639251692 0.0 +v 0.3033362963553555 0.08909318203595107 0.0 +v 0.2890625 0.08800453142669294 0.0 +v 0.3407057358023839 0.032725178976253384 0.0 +v 0.328125 0.0 0.0 +v 0.34593012453486627 0.02211253716680723 0.0 +v 0.33857527827072453 0.01167392766041644 0.0 +v 0.265625 0.08128180467487178 0.0 +v 0.3274574673609864 0.021755875075735304 0.0 +v 0.31642132499266346 0.021375125980302598 0.0 +v 0.2763115291042765 0.08824180862277449 0.0 +v 0.265625 0.1 0.0 +v 0.27950377544540683 0.07165621270612571 0.0 +v 0.2671392824005369 0.0906409023374359 0.0 +v 0.2545080916460054 0.08788254985556745 0.0 +v 0.28255153780228176 0.08084282398644946 0.0 +v 0.29355529356237586 0.07737944644778584 0.0 +v 0.1875 0.1 0.0 +v 0.24700685669080358 0.06258718075498991 0.0 +v 0.25395372723374143 0.0742870299475875 0.0 +v 0.234375 0.1 0.0 +v 0.21875 0.1 0.0 +v 0.2653525893260991 0.05960657063568342 0.0 +v 0.26417860861009856 0.07046065385190434 0.0 +v 0.23312807481093717 0.07874011047545214 0.0 +v 0.24279027114368862 0.07300311113647427 0.0 +v 0.24321456412031028 0.08472268748014858 0.0 +v 0.22945054549944802 0.06154146636010008 0.0 +v 0.23363662260197088 0.08937679516753201 0.0 +v 0.21909515541664673 0.08474153413845668 0.0 +v 0.2220001233090763 0.07212706458451175 0.0 +v 0.1955896919747533 0.07268676986661715 0.0 +v 0.2088656304574824 0.07574406647899333 0.0 +v 0.1875 0.0 0.0 +v 0.21905093677343906 0.06213664903515718 0.0 +v 0.2230731618631444 0.04126325097577782 0.0 +v 0.2246315889965242 0.0924999082415913 0.0 +v 0.171875 0.1 0.0 +v 0.20423969859386287 0.06547839071404848 0.0 +v 0.20882220467132745 0.05129494312532607 0.0 +v 0.22369955432033442 0.052208187272981904 0.0 +v 0.18990624774557233 0.06267050463182504 0.0 +v 0.2405382207986868 0.04575409646942343 0.0 +v 0.15625 0.1 0.0 +v 0.1834721062044542 0.0729419617048859 0.0 +v 0.18980496269044414 0.08582806250987249 0.0 +v 0.1663669205271791 0.05507617223442991 0.0 +v 0.17717698524268702 0.06184770289006044 0.0 +v 0.16575187938938246 0.06807237682458596 0.0 +v 0.18516954404938565 0.03707403652701558 0.0 +v 0.1403461831623539 0.06035740494232419 0.0 +v 0.18430359679251246 0.050470787392846994 0.0 +v 0.15402387384209523 0.06100469718674381 0.0 +v 0.20977552563002425 0.08502345601045483 0.0 +v 0.19966592534911343 0.08234013088920465 0.0 +v 0.19868385453951332 0.05585136160026026 0.0 +v 0.1984480459293918 0.04176919224652817 0.0 +v 0.203125 0.1 0.0 +v 0.17282649736430178 0.043002561479329474 0.0 +v 0.15009407461450422 0.08079023064278357 0.0 +v 0.19308731120036932 0.04890201777833872 0.0 +v 0.1690267734701117 0.031843250956210475 0.0 +v 0.2107054550118817 0.03883202138843143 0.0 +v 0.20225768940979721 0.030622695152373987 0.0 +v 0.19139135503311042 0.027697219414492893 0.0 +v 0.21995018085476525 0.024476925393326524 0.0 +v 0.171875 0.0 0.0 +v 0.18508257663821825 0.014461608951278897 0.0 +v 0.21875 0.0 0.0 +v 0.203125 0.0 0.0 +v 0.1795967660503045 0.026623732790886926 0.0 +v 0.2365905333303017 0.02830215825273051 0.0 +v 0.15684577625135632 0.042217411838199055 0.0 +v 0.2181783560606058 0.033490228686447736 0.0 +v 0.15625 0.0 0.0 +v 0.1953125 0.09399723491199577 0.0 +v 0.17993387769374883 0.08268000643123057 0.0 +v 0.17130563771465537 0.07686864094293797 0.0 +v 0.1611800856905478 0.08142695865279348 0.0 +v 0.15513992652534125 0.08976448936393189 0.0 +v 0.16580143862869084 0.09113159191393241 0.0 +v 0.140625 0.1 0.0 +v 0.15562852290596005 0.07160644705476774 0.0 +v 0.14427570031492695 0.07102437648442392 0.0 +v 0.133051212740027 0.08432797096690703 0.0 +v 0.12988397034581664 0.07026875960989344 0.0 +v 0.19263729277254116 0.01898199014740326 0.0 +v 0.19378896450157576 0.008484126815057743 0.0 +v 0.2075852816940609 0.01530976050497554 0.0 +v 0.1572373227235456 0.05188179466691846 0.0 +v 0.14449567208837955 0.04755789168889098 0.0 +v 0.112957922614365 0.063500737356789 0.0 +v 0.16761295246269467 0.01566778582788441 0.0 +v 0.2101427653134675 0.024782786862725118 0.0 +v 0.20122523361465788 0.021680248115762833 0.0 +v 0.21944645656445874 0.01223373756101375 0.0 +v 0.234375 0.0 0.0 +v 0.2302167183128738 0.017922570545304863 0.0 +v 0.22852466363363086 0.008086316291979917 0.0 +v 0.24105736696834887 0.010994075010809212 0.0 +v 0.11709686805976428 0.04574781231256307 0.0 +v 0.1405196668713983 0.021412867005354674 0.0 +v 0.12169370931926075 0.0795002138379887 0.0 +v 0.0625 0.1 0.0 +v 0.1240927253077394 0.08962980980206883 0.0 +v 0.09375 0.1 0.0 +v 0.109375 0.1 0.0 +v 0.12020360000006348 0.06992919246654564 0.0 +v 0.10975946619241798 0.07645674439971882 0.0 +v 0.12520298962275278 0.05699662922236484 0.0 +v 0.08462558489811597 0.07642507959271712 0.0 +v 0.09718852446759778 0.07961675990779848 0.0 +v 0.09646339944334488 0.08997608465166616 0.0 +v 0.078125 0.1 0.0 +v 0.0950423801457701 0.06174364923377318 0.0 +v 0.10146332465065346 0.0700380496558303 0.0 +v 0.1796875 0.006126905717717078 0.0 +v 0.1765455411280125 0.017929188118626947 0.0 +v 0.140625 0.0 0.0 +v 0.14594639939276507 0.03396243441825479 0.0 +v 0.13095109453903178 0.044309305507823284 0.0 +v 0.08842246090645399 0.08505433484801567 0.0 +v 0.07409978089652373 0.08620637902434332 0.0 +v 0.28125 0.0 0.0 +v 0.1549331448527089 0.022628243109221926 0.0 +v 0.13396351134184775 0.05331988464881106 0.0 +v 0.1221720750312219 0.027192891554884257 0.0 +v 0.08173033174664512 0.09146377628801759 0.0 +v 0.0703125 0.09479569867068881 0.0 +v 0.03125 0.1 0.0 +v 0.16400912367316958 0.02413229975332026 0.0 +v 0.13370952282006007 0.0318058439074279 0.0 +v 0.12323770193436367 0.03745592014111123 0.0 +v 0.10949246091089643 0.03369627363128733 0.0 +v 0.0679221330545495 0.06900438088726765 0.0 +v 0.265625 0.021843477175158152 0.0 +v 0.13738805501660165 0.010690769555942016 0.0 +v 0.15896802142406558 0.00998311785679002 0.0 +v 0.0625 0.0 0.0 +v 0.07468774473734469 0.07628495776989817 0.0 +v 0.07982055620140853 0.06473137332789201 0.0 +v 0.06541712486076458 0.04332663998850588 0.0 +v 0.06991419746868624 0.05584898451228182 0.0 +v 0.2548250268183914 0.01305872749682294 0.0 +v 0.1295713895783464 0.018670124664607775 0.0 +v 0.10884866245557098 0.042527428285214025 0.0 +v 0.14810468816659159 0.008100120566344432 0.0 +v 0.10136021600819649 0.04725256730723811 0.0 +v 0.046338071483598836 0.048880581935529764 0.0 +v 0.08470044842688555 0.04932354183006241 0.0 +v 0.265625 0.0 0.0 +v 0.2517343420547171 0.02788953556173944 0.0 +v 0.09375 0.0 0.0 +v 0.14815433813684403 0.016945589044251017 0.0 +v 0.109375 0.0 0.0 +v 0.09256098652284567 0.036733273382153195 0.0 +v 0.1097836698012762 0.05230568807972659 0.0 +v 0.05606896741415646 0.05375246158655622 0.0 +v 0.08647452799628925 0.05836210296156353 0.0 +v 0.07484604732989691 0.047009164308198785 0.0 +v 0.07636206820199457 0.029215059036117708 0.0 +v 0.2655360290130371 0.010921738587579076 0.0 +v 0.2808972683432682 0.01625783409237923 0.0 +v 0.2578125 0.004534140237857039 0.0 +v 0.24396843795646816 0.020975795200579336 0.0 +v 0.26558255587705104 0.04072570026832306 0.0 +v 0.2443700328359486 0.035714868383755705 0.0 +v 0.2549573364802448 0.03830053934080219 0.0 +v 0.2621498453094978 0.03127682485090609 0.0 +v 0.2578595910896871 0.05007347122445686 0.0 +v 0.2767801593161327 0.03130971138356782 0.0 +v 0.11213275327366992 0.02323165580448895 0.0 +v 0.09929456730927232 0.02555788702097247 0.0 +v 0.08763052120858598 0.026146178855942948 0.0 +v 0.078125 0.0 0.0 +v 0.09284343839142636 0.013577037858642888 0.0 +v 0.07995731426615049 0.020193798376027694 0.0 +v 0.0646645099738345 0.01932618955091924 0.0 +v 0.053687140374899425 0.06783537634732771 0.0 +v 0.27456463680617776 0.02261469324338323 0.0 +v 0.2871017999200648 0.026043957675473146 0.0 +v 0.296875 0.0 0.0 +v 0.2904947709529109 0.017032829970084373 0.0 +v 0.3008864597550207 0.024432101908617283 0.0 +v 0.30744699968974554 0.016770456326148805 0.0 +v 0.2819289853430797 0.04879890967920061 0.0 +v 0.06453506500246042 0.031342274338577676 0.0 +v 0.04951532639960629 0.038472383333267454 0.0 +v 0.052418399284123 0.025203006218962477 0.0 +v 0.06134112814951575 0.06188701121941632 0.0 +v 0.06027895270855585 0.07482114373129267 0.0 +v 0.027333897000199725 0.03739022852809547 0.0 +v 0.3095144122205894 0.027276582579808764 0.0 +v 0.27552400383953235 0.041182031219680164 0.0 +v 0.1015625 0.007340438988768074 0.0 +v 0.11271547012671809 0.011382973605768133 0.0 +v 0.10436269073168179 0.016938935415897142 0.0 +v 0.11999641122867435 0.017679719541174414 0.0 +v 0.0859375 0.006297131510256047 0.0 +v 0.07282714154350035 0.010660737115026932 0.0 +v 0.05633933577498104 0.04459102956735056 0.0 +v 0.0 0.05 0.0 +v 0.006653916793072313 0.06250000000000001 0.0 +v 0.03828787360371445 0.040734090164803956 0.0 +v 0.02619499690132399 0.06073479255484265 0.0 +v 0.03125 0.0 0.0 +v 0.029714201668029107 0.04920641881644501 0.0 +v 0.014893968635716903 0.05098365707702054 0.0 +v 0.04475934834171946 0.030479615427673773 0.0 +v 0.046875 0.0 0.0 +v 0.0 0.07500000000000001 0.0 +v 0.0627578483901483 0.009755427322260935 0.0 +v 0.03961041196152601 0.014808983814766058 0.0 +v 0.0 0.025 0.0 +v 0.01634405105617975 0.0607272935261734 0.0 +v 0.05339448251346534 0.08207016203239653 0.0 +v 0.013370142686066457 0.07184195014757638 0.0 +v 0.0 0.037500000000000006 0.0 +v 0.013642069251766689 0.03125 0.0 +v 0.016290759361093393 0.011881250965693112 0.0 +v 0.017532816961886157 0.040909710384400035 0.0 +v 0.007882487347023348 0.043750000000000004 0.0 +v 0.009718162737249717 0.021801348427759985 0.0 +v 0.0 0.0125 0.0 +v 0.02250216784687884 0.022031411785078163 0.0 +v 0.015625 0.0 0.0 +v 0.02817137520542748 0.011586509029407065 0.0 +v 0.6796875 0.008265346582244404 0.0 +v 0.7059778096617061 0.026085943543422266 0.0 +v 0.6853087213459015 0.01934285871592095 0.0 +v 0.71875 0.0 0.0 +v 0.6944964326808193 0.026229704512042696 0.0 +v 0.703125 0.0 0.0 +v 0.700357455908867 0.03576828018693475 0.0 +v 0.7223243042179879 0.04204707950757906 0.0 +v 0.7105117470417126 0.0418084636414147 0.0 +v 0.7159600184141681 0.0646011860014532 0.0 +v 0.7098531695416469 0.054013413594770784 0.0 +v 0.721920628216063 0.0541081566063482 0.0 +v 0.7042273869917802 0.0643133202679284 0.0 +v 0.7459855170086347 0.04887629804701786 0.0 +v 0.7222101230643739 0.08191793390204855 0.0 +v 0.6991474288093238 0.0734542118240659 0.0 +v 0.7380185514538393 0.07019213962353699 0.0 +v 0.7332919893962401 0.04845145435893481 0.0 +v 0.7277580413441459 0.0643636133867945 0.0 +v 0.7403461838820251 0.023777500120330808 0.0 +v 0.7393086232867954 0.05852753504368306 0.0 +v 0.7220144818253823 0.07220225254294815 0.0 +v 0.7336682494892445 0.03521366426764583 0.0 +v 0.7526415092734473 0.025689169564928848 0.0 +v 0.7494659452496436 0.06555454715289513 0.0 +v 0.756811127306342 0.055319476014967034 0.0 +v 0.7482939109184424 0.01315582239296434 0.0 +v 0.7819884409155462 0.04940257073918188 0.0 +v 0.7548191121004495 0.07333093018380257 0.0 +v 0.7131168139451997 0.032542244072165195 0.0 +v 0.7570229054880578 0.04264765183236627 0.0 +v 0.7686521402813625 0.04917968747798569 0.0 +v 0.7461763272630239 0.03632077001445939 0.0 +v 0.8125 0.1 0.0 +v 0.8125 0.0 0.0 +v 0.71875 0.1 0.0 +v 0.7108250913343862 0.07728737980022748 0.0 +v 0.703125 0.1 0.0 +v 0.7026326538280939 0.08254100583227851 0.0 +v 0.7126411215792952 0.08913367858526505 0.0 +v 0.7210134660041005 0.027038419498485247 0.0 +v 0.7142922403148156 0.013987122700975359 0.0 +v 0.734375 0.0 0.0 +v 0.78125 0.0 0.0 +v 0.7719135477290372 0.02975527285044803 0.0 +v 0.7749934302263024 0.06884893782434091 0.0 +v 0.7056597506432093 0.016961079846204485 0.0 +v 0.7245958016845673 0.016937254834155067 0.0 +v 0.7451640650515486 0.07847774742755897 0.0 +v 0.7754699132768076 0.040338401974650356 0.0 +v 0.7657725880222055 0.03770799416078354 0.0 +v 0.794630635524164 0.028010506328268587 0.0 +v 0.7834925555116161 0.03175335814081521 0.0 +v 0.7802444317678631 0.01602688586653523 0.0 +v 0.792948233767619 0.04144789582951963 0.0 +v 0.7670389763453386 0.060556594069914337 0.0 +v 0.7772830555954198 0.05869127038546461 0.0 +v 0.7980136020435057 0.06335660724857524 0.0 +v 0.7882378279167187 0.05840448415054142 0.0 +v 0.78125 0.1 0.0 +v 0.7648099056788025 0.07065617424139999 0.0 +v 0.765625 0.1 0.0 +v 0.7618680229818082 0.07966387273792118 0.0 +v 0.787776813599669 0.0714395828411094 0.0 +v 0.7742060388600216 0.08126553903268885 0.0 +v 0.7516176635792895 0.0873129456313266 0.0 +v 0.8044292090417453 0.08200547929956747 0.0 +v 0.734375 0.1 0.0 +v 0.7881356150098566 0.08621950668149087 0.0 +v 0.7979541443654501 0.07380505032479526 0.0 +v 0.8128932780158921 0.06866567191449012 0.0 +v 0.7617515652686883 0.09020049066707267 0.0 +v 0.7109375 0.005214036185291291 0.0 +v 0.7359968293643033 0.012238768948850887 0.0 +v 0.765625 0.0 0.0 +v 0.7828742207907896 0.07895291660401532 0.0 +v 0.796875 0.1 0.0 +v 0.7788735289486023 0.09020206993291296 0.0 +v 0.8203755434609753 0.038057862212518986 0.0 +v 0.7633243013972645 0.014962769903727245 0.0 +v 0.7890625 0.09529313092651814 0.0 +v 0.7976787851766228 0.0898288103392259 0.0 +v 0.8062335333174501 0.03628723982307878 0.0 +v 0.7565924367632229 0.007543465000742356 0.0 +v 0.7722135004549634 0.008671322439088894 0.0 +v 0.7628213388404702 0.025144732876914308 0.0 +v 0.8103067101584286 0.018305939356421108 0.0 +v 0.8114366845741637 0.05209115125497065 0.0 +v 0.804502221166133 0.02644307371771113 0.0 +v 0.796875 0.0 0.0 +v 0.8154604218077511 0.02812108840392684 0.0 +v 0.7891117252489315 0.020008847604321263 0.0 +v 0.84375 0.0 0.0 +v 0.7889398520008845 0.008527470092708943 0.0 +v 0.8005552272126575 0.01138031638020527 0.0 +v 0.8375780624230387 0.023364821639455916 0.0 +v 0.8271202308641676 0.02853767908100622 0.0 +v 0.828125 0.0 0.0 +v 0.8425837350518032 0.04664225196944073 0.0 +v 0.8325643390670744 0.03954389126016966 0.0 +v 0.8282220003422137 0.05629355028465059 0.0 +v 0.8255125765930061 0.04665343003198502 0.0 +v 0.8125804180268209 0.04295612044013912 0.0 +v 0.8024474610877311 0.04632655847125819 0.0 +v 0.6649850369473967 0.009233634333836506 0.0 +v 0.3515625 0.005071890026541505 0.0 +v 0.30142827884839957 0.047057029911478095 0.0 +v 0.30004784279182406 0.05704181962076973 0.0 +v 0.28910678174668303 0.057056407386425735 0.0 +v 0.29142462295375815 0.045084490638906335 0.0 +v 0.3223139173101989 0.010709877763730929 0.0 +v 0.3130789219735496 0.009320903297638756 0.0 +v 0.2421875 0.09432439501184844 0.0 +v 0.23858002415755658 0.0561660237513299 0.0 +v 0.2109375 0.09450434298553209 0.0 +v 0.23315757102603635 0.03825116517939752 0.0 +v 0.1796875 0.09310023638313704 0.0 +v 0.14295299352303378 0.08920885731734791 0.0 +v 0.1328125 0.09410943148629679 0.0 +v 0.2109375 0.00602853904802729 0.0 +v 0.08183069952860415 0.03864260863904289 0.0 +v 0.2751581954811265 0.008000575074083008 0.0 +v 0.24804839645267027 0.05252750737316307 0.0 +v 0.050944018512501334 0.011182392106236066 0.0 +v 0.0390625 0.005353880282301172 0.0 +v 0.0435804312410303 0.08397504885812783 0.0 +v 0.03397792856542885 0.025340877055778036 0.0 +v 0.6953125 0.009837482996325725 0.0 +v 0.7265625 0.006781008775631795 0.0 +v 0.7334717991617626 0.0805814845920679 0.0 +v 0.7289413809382221 0.09052246770447202 0.0 +v 0.740395882041284 0.08973982511934768 0.0 +v 0.8614036144524055 0.07881869684796197 0.0 +v 0.7714941879696798 0.0201088550516815 0.0 +v 0.8073109939005837 0.06080498719136301 0.0 +v 0.8156865559822037 0.009666152905087187 0.0 +v 0.8216248053362757 0.01896776935298881 0.0 +v 0.818418173872696 0.05982887175274349 0.0 +v 0.8279126250466715 0.07079681241085121 0.0 +v 0.8195802798421417 0.07552907276684317 0.0 +v 0.8437437003543024 0.06387958108645118 0.0 +v 0.8342282657525724 0.06367660327590713 0.0 +v 0.8298728011598565 0.08394993411094753 0.0 +v 0.8387665869743518 0.07406320185311188 0.0 +v 0.84375 0.1 0.0 +v 0.8664084748420215 0.054994644402054274 0.0 +v 0.8506859919434709 0.07358059699990871 0.0 +v 0.8561987636895806 0.0623009711304855 0.0 +v 0.859375 0.1 0.0 +v 0.8489297253346536 0.054872899988887 0.0 +v 0.8577626647593317 0.04150076607634465 0.0 +v 0.8677991409025655 0.06772451650935599 0.0 +v 0.9375 0.1 0.0 +v 0.897478204761677 0.07808622129869566 0.0 +v 0.8788926887828927 0.060071712326632484 0.0 +v 0.871156393819531 0.07705041483195653 0.0 +v 0.8803673214813994 0.07099548537244227 0.0 +v 0.8683439609155451 0.08931809925232058 0.0 +v 0.8897462936891692 0.06416797089011748 0.0 +v 0.901242880076356 0.06261601241050999 0.0 +v 0.9375 0.0 0.0 +v 0.8814693489368964 0.020248677702916582 0.0 +v 0.8452302376840465 0.08626844433584131 0.0 +v 0.8940573490582134 0.05274523207138438 0.0 +v 0.9067912475599633 0.051026245139726305 0.0 +v 0.9171796251289418 0.04346392415946995 0.0 +v 0.90625 0.0 0.0 +v 0.9269476199186474 0.06779867033194495 0.0 +v 0.9189887511218907 0.05686555412350404 0.0 +v 0.9049821215670142 0.037624615175691926 0.0 +v 0.8177549118699311 0.0882606788118513 0.0 +v 0.828125 0.1 0.0 +v 0.90625 0.1 0.0 +v 0.8515625 0.09389661248712554 0.0 +v 0.8589624602958903 0.08797592140721586 0.0 +v 0.8364704948971494 0.09226972073301998 0.0 +v 0.8792172700236087 0.041386075010821796 0.0 +v 0.9445995460742296 0.04658535190230201 0.0 +v 0.9132483501599313 0.06940776137332477 0.0 +v 0.8972443497479405 0.02349873731467479 0.0 +v 0.8919853769857846 0.03872229614811864 0.0 +v 0.8754432558518989 0.05066615576519152 0.0 +v 0.9330931265940385 0.05496156241880455 0.0 +v 0.8684313930407902 0.030486249932929973 0.0 +v 0.890625 0.1 0.0 +v 0.883749995027166 0.03118034084926374 0.0 +v 0.9313996591369157 0.04054393971994514 0.0 +v 0.9778409826614589 0.07172824116929939 0.0 +v 0.921875 0.1 0.0 +v 0.9041337074362655 0.07151266655591616 0.0 +v 0.9112613735303678 0.08159029911289768 0.0 +v 0.8684887514630968 0.041215963738966556 0.0 +v 0.8531381900229399 0.026347315235438526 0.0 +v 0.8437597253588446 0.03436192043099687 0.0 +v 0.9256297110445596 0.04852993435207696 0.0 +v 0.9201149133598988 0.02167341773456566 0.0 +v 0.96875 0.0 0.0 +v 0.9419085631100115 0.06707134765568924 0.0 +v 0.9020484071576246 0.08896933588939206 0.0 +v 0.883718292062045 0.08612650016029388 0.0 +v 0.8596403718960882 0.032645368707603026 0.0 +v 0.9224575730479359 0.033081937582163434 0.0 +v 0.9121350919557091 0.029256802506696336 0.0 +v 0.9082478626753814 0.017176176864001188 0.0 +v 0.9338687012815716 0.024793957972664607 0.0 +v 0.8822788055124725 0.008832259672057847 0.0 +v 0.921875 0.0 0.0 +v 0.9175422643256942 0.010556317571386191 0.0 +v 0.9432540545921206 0.05682834977899564 0.0 +v 0.9641927497660687 0.054368929137165144 0.0 +v 0.953412636570807 0.05295288369275499 0.0 +v 0.958899928702575 0.03607538417226507 0.0 +v 0.9575841823508908 0.06293722208311947 0.0 +v 0.9688233914289431 0.06477313821489632 0.0 +v 0.96875 0.1 0.0 +v 0.987092979195243 0.05040922861550337 0.0 +v 0.9624396411378892 0.08237334569266325 0.0 +v 0.9797731693079673 0.05989967813090432 0.0 +v 0.9618409341741436 0.07219836443180169 0.0 +v 0.9497187522691759 0.07801675171411321 0.0 +v 1.002210093664268 0.06963682453247658 0.0 +v 0.989761272525554 0.06760331920381422 0.0 +v 0.9977935242101191 0.057552733920600785 0.0 +v 0.9934309415317786 0.0842598247258349 0.0 +v 1.006576801180038 0.0328097668651568 0.0 +v 0.9863679405929395 0.07708341262571264 0.0 +v 0.984375 0.1 0.0 +v 0.9990587096284306 0.044071419742565586 0.0 +v 1.0625 0.1 0.0 +v 1.0111926444723425 0.05950468225702668 0.0 +v 0.8877836862933841 0.07728959632311265 0.0 +v 0.8924342989238201 0.0904433194981492 0.0 +v 0.8828125 0.09523338370490114 0.0 +v 0.9388677838325676 0.012863221483083513 0.0 +v 0.9279570143561322 0.014002425133255437 0.0 +v 0.9491931843139207 0.024202352077767827 0.0 +v 0.953125 0.0 0.0 +v 0.9453125 0.005673605066335064 0.0 +v 0.9565871312819769 0.012982993198960544 0.0 +v 0.8903775962204483 0.016919249821146974 0.0 +v 0.890625 0.0 0.0 +v 0.8675837781028124 0.013527245751897298 0.0 +v 0.9507970286015062 0.06898812162006575 0.0 +v 0.953125 0.1 0.0 +v 0.9350498570546152 0.08022465412691043 0.0 +v 0.9755384386060533 0.08486080532987608 0.0 +v 0.9817935415593222 0.025939430241506266 0.0 +v 1.0132469477409054 0.04540294808871733 0.0 +v 0.9296875 0.004928693873115035 0.0 +v 0.9474738367363631 0.03551236583446071 0.0 +v 0.9609375 0.004869780881774371 0.0 +v 0.9723319122396039 0.014448011547804392 0.0 +v 0.872148964248337 0.02179976078158253 0.0 +v 0.859375 0.0 0.0 +v 0.9453125 0.08995500796185575 0.0 +v 0.9555854815907389 0.09002577376912012 0.0 +v 0.9230570176156176 0.07918998105120886 0.0 +v 0.9281724221170895 0.08991911714318047 0.0 +v 0.9174986366486896 0.09025872326833023 0.0 +v 0.9676943066367435 0.09043505485446468 0.0 +v 0.9934125099113634 0.032161808346584476 0.0 +v 1.0055202359755124 0.05147784100465902 0.0 +v 1.0625 0.0 0.0 +v 1.0321693404701107 0.01815632766838493 0.0 +v 0.984375 0.0 0.0 +v 0.9686322824663304 0.027135051843679155 0.0 +v 0.8671875 0.004513419063886878 0.0 +v 0.9824904602997735 0.03859724841083604 0.0 +v 0.9749428768898456 0.048340824130947035 0.0 +v 0.965489216633767 0.044081374778342994 0.0 +v 0.9947620392848222 0.01568268336045914 0.0 +v 1.0141722476702302 0.020725777425479368 0.0 +v 1.03125 0.1 0.0 +v 1.040120322398963 0.05651828991568656 0.0 +v 0.9765625 0.005731160778974774 0.0 +v 0.9835769630865592 0.01452057151615819 0.0 +v 0.9588280908923953 0.026229746033618155 0.0 +v 1.000390752494821 0.02443845824852124 0.0 +v 1.0284999967381172 0.0465692320000333 0.0 +v 1.0215741686672606 0.03292926765234999 0.0 +v 1.029518237176748 0.05714064251249571 0.0 +v 1.030171030807129 0.06774089695708325 0.0 +v 0.9640106042271667 0.018540089205705573 0.0 +v 1.03125 0.0 0.0 +v 1.0525940936684763 0.07759831550422036 0.0 +v 1.016500376385115 0.07325796669072059 0.0 +v 1.0429676048004355 0.06906404673757881 0.0 +v 1.0610495558941082 0.058364338565892016 0.0 +v 1.037664314893592 0.08474243472427109 0.0 +v 1.0537290604890832 0.06662171473389733 0.0 +v 1.0702275143868272 0.07387461173803753 0.0 +v 1.03584666311596 0.07539147968351347 0.0 +v 1.0252945840025725 0.08229474401922056 0.0 +v 1.0300909321558462 0.09053564639085684 0.0 +v 1.015625 0.1 0.0 +v 1.0119393672402692 0.08649401014612228 0.0 +v 1.0237191286535763 0.02328173024677498 0.0 +v 1.015625 0.0 0.0 +v 1.0339485359540046 0.030618274489627575 0.0 +v 1.0219562800782647 0.01084673444800365 0.0 +v 1.0546533695728477 0.021304258332516333 0.0 +v 1.0429571945960674 0.022974124365169296 0.0 +v 1.046875 0.0 0.0 +v 1.0514385733894889 0.040583446915590175 0.0 +v 1.0439912665172264 0.03332322537551927 0.0 +v 1.0402426394130295 0.044618166469577496 0.0 +v 1.019739665796894 0.053549289864105745 0.0 +v 1.0203612341767516 0.09145165615589676 0.0 +v 1.0078125 0.09487606460543442 0.0 +v 1.0077816189359783 0.07765743040699863 0.0 +v 1.0336617734273486 0.008979319646133865 0.0 +v 1.0473435760615524 0.011901023092105268 0.0 +v 1.0586285634364068 0.010671236735605182 0.0 +v 1.09375 0.0 0.0 +v 1.077678584650793 0.02385273772488574 0.0 +v 1.0664879959381683 0.01966909536371901 0.0 +v 1.078125 0.0 0.0 +v 1.0668375687653362 0.035792400850538864 0.0 +v 1.075774853644636 0.011886562191826489 0.0 +v 1.0911334190504418 0.015577649375091384 0.0 +v 1.020639142078149 0.06381766733302484 0.0 +v 0.1081845956221281 0.08820579344634379 0.0 +v 0.29887117281316283 0.010459116296000395 0.0 +v 0.2890625 0.006784942403238577 0.0 +v 0.06249092636508787 0.08621731834137585 0.0 +v 0.046875 0.1 0.0 +v 0.015625 0.1 0.0 +v 0.0546875 0.0931137994335976 0.0 +v 0.8359375 0.010433873337045253 0.0 +v 0.8498169269340914 0.012684484172760734 0.0 +v 0.8258749038125844 0.009826581911044303 0.0 +v 0.846132665845835 0.020815176152248965 0.0 +v 0.8584770044996624 0.01781442205237597 0.0 +v 0.8576960810100371 0.05106014251838095 0.0 +v 0.8994212661036978 0.009498570451963437 0.0 +v 0.9921875 0.09401963113576649 0.0 +v 0.9765625 0.09441156665082055 0.0 +v 0.9380236648846266 0.03351383013029719 0.0 +v 0.9724644383256814 0.036610445454584625 0.0 +v 0.9921875 0.006106724508206457 0.0 +v 1.046875 0.1 0.0 +v 1.0636037232491304 0.08612092749413228 0.0 +v 1.0546875 0.0923952939105901 0.0 +v 1.09375 0.1 0.0 +v 1.04738379381266 0.08588201582159433 0.0 +v 1.078125 0.1 0.0 +v 1.0703125 0.09363786078675362 0.0 +v 1.0849076640492845 0.0836932916402718 0.0 +v 1.0063787871889645 0.010846564182647596 0.0 +v 1.0570942706453648 0.03161890472212649 0.0 +v 1.087576007449102 0.05313832336635975 0.0 +v 1.0617273164963938 0.04651006850293678 0.0 +v 1.075503663236607 0.04650158420432056 0.0 +v 1.078340304474174 0.035345131116922356 0.0 +v 1.0900580965325215 0.028905185818535774 0.0 +v 1.0755549959375847 0.060706480977817896 0.0 +v 1.0891048542757134 0.041051232859887446 0.0 +v 1.113612640704821 0.03686421572194525 0.0 +v 1.1008296875540964 0.035860987999494956 0.0 +v 1.108527145807345 0.019722010467023363 0.0 +v 1.1058795649522706 0.05345702523566553 0.0 +v 1.107777822657607 0.029269755713197218 0.0 +v 1.1205815470240958 0.02547133690032164 0.0 +v 1.0968171222847765 0.04816696027096477 0.0 +v 1.1066441393694615 0.04371495099221801 0.0 +v 1.1192897604778262 0.04960844033096898 0.0 +v 1.088950409201985 0.06865234527455111 0.0 +v 1.0674082586751765 0.06507226927977458 0.0 +v 1.0692563342660433 0.053626701519832634 0.0 +v 1.0985827112234094 0.022885839085464422 0.0 +v 1.119527521789197 0.012169599839691727 0.0 +v 1.1193540934263586 0.07512046481557426 0.0 +v 1.0804743904780485 0.07443792167691216 0.0 +v 1.0966083684106571 0.060156028760817604 0.0 +v 1.1085003757687233 0.06684915417133364 0.0 +v 1.109375 0.0 0.0 +v 1.1875 0.0 0.0 +v 1.120498825955134 0.06236148482043663 0.0 +v 1.145126413524448 0.05359280550224192 0.0 +v 1.131704633608484 0.054865271128298324 0.0 +v 1.133703549362586 0.0699770518951089 0.0 +v 1.1036060316560108 0.08452842155601452 0.0 +v 1.1015625 0.00932084572785613 0.0 +v 1.1532991543535596 0.04379674495560624 0.0 +v 1.131459195160984 0.03815661364239178 0.0 +v 1.1875 0.1 0.0 +v 1.1278988125320482 0.046565044565694956 0.0 +v 1.15625 0.0 0.0 +v 1.109375 0.1 0.0 +v 1.1095952015136332 0.07666935768643095 0.0 +v 1.0985675060956572 0.07447708376711072 0.0 +v 1.1381908340985105 0.0459649941196188 0.0 +v 1.1460161853782516 0.03241284666544032 0.0 +v 1.12274504753616 0.03462257124487643 0.0 +v 1.1391118299840275 0.06157357580155793 0.0 +v 1.15625 0.1 0.0 +v 1.1841237436807244 0.061865890686556686 0.0 +v 1.1502815109491227 0.06253250673657593 0.0 +v 1.1653913165974084 0.04786320621345835 0.0 +v 1.160987857740462 0.05844393530343575 0.0 +v 1.1629785984182721 0.07971901890680824 0.0 +v 1.1729881895537548 0.057231526265123976 0.0 +v 1.1590221129918237 0.06935855297313484 0.0 +v 1.1877251372203164 0.037516820979515086 0.0 +v 1.1737691361423963 0.07105061143425405 0.0 +v 1.1466160128546765 0.08003192140519806 0.0 +v 1.1828477591662983 0.049236293679380934 0.0 +v 1.17486752069076 0.03904043661405849 0.0 +v 1.2084736287409141 0.05302653463079433 0.0 +v 1.1609356306901613 0.03357712049803944 0.0 +v 1.1308276627073865 0.08454196888507591 0.0 +v 1.1951817695619062 0.054369428348678646 0.0 +v 1.1802686118204297 0.02960584248212071 0.0 +v 1.1164074049666488 0.0880723651332154 0.0 +v 1.195975350590547 0.022270969723310906 0.0 +v 1.1913507630823037 0.0804425519161322 0.0 +v 1.2006338303956579 0.04188115997337808 0.0 +v 1.1364150229055656 0.017524068685173467 0.0 +v 1.1546982019482834 0.017332046997962713 0.0 +v 1.137585796302232 0.07830999795574302 0.0 +v 1.1403919096476611 0.0881333757024932 0.0 +v 1.140625 0.1 0.0 +v 1.1513064138363542 0.0900770377555785 0.0 +v 1.124789212523911 0.0910930061803814 0.0 +v 1.171875 0.1 0.0 +v 1.184298503772413 0.07249202414088703 0.0 +v 1.1956678620715706 0.06699053698288218 0.0 +v 1.1966215692602709 0.03247587333182884 0.0 +v 1.21875 0.0 0.0 +v 1.2073377483824723 0.03346284156108608 0.0 +v 1.126882922560536 0.01827938915643637 0.0 +v 1.217132047558487 0.04270899987352488 0.0 +v 1.1324073900102962 0.027472910776924812 0.0 +v 1.1539484290088131 0.02693992106810755 0.0 +v 1.1644099855335983 0.022923120937768528 0.0 +v 1.171875 0.0 0.0 +v 1.186721144266795 0.0896888252509377 0.0 +v 1.21875 0.1 0.0 +v 1.203125 0.1 0.0 +v 1.1981890383988278 0.08964864776341902 0.0 +v 1.205039947760389 0.07741702305758874 0.0 +v 1.2109375 0.08992215677218401 0.0 +v 1.234375 0.07982015118811377 0.0 +v 1.2239593722856037 0.08789450694849368 0.0 +v 1.1776056506160661 0.08220394403241485 0.0 +v 1.1772630229157177 0.09191432609207452 0.0 +v 1.1662156743197234 0.09039077829746696 0.0 +v 1.2276815877327598 0.02172350340855936 0.0 +v 1.1651113920042873 0.010474469265086127 0.0 +v 1.1796875 0.012465656627986082 0.0 +v 1.2079366277015684 0.06538787606257425 0.0 +v 1.2273037925348664 0.060036888973104045 0.0 +v 1.2190557372673367 0.07442875549838616 0.0 +v 1.2167543723648044 0.05957860136515449 0.0 +v 1.234375 0.1 0.0 +v 1.219266416278626 0.030637552255026217 0.0 +v 1.2102459015823182 0.019146681116567824 0.0 +v 1.2383658676221343 0.040238965764174794 0.0 +v 1.2273793261876662 0.03829642075040986 0.0 +v 1.231120027209347 0.04917978252191719 0.0 +v 1.252432508858827 0.05904539541065604 0.0 +v 1.2398206500216966 0.05833727054182625 0.0 +v 1.2466888276548937 0.04867756926521045 0.0 +v 1.2454248376546335 0.07118950770506387 0.0 +v 1.3125 0.0 0.0 +v 1.2647207829450917 0.04546291695279528 0.0 +v 1.250342170198154 0.036750540066779164 0.0 +v 1.2562544211336883 0.05015320424346496 0.0 +v 1.3125 0.1 0.0 +v 1.2408500489511634 0.0264650844958893 0.0 +v 1.2597643424422207 0.03742168204207622 0.0 +v 1.3125458741177995 0.06370912591311305 0.0 +v 1.1915378101064558 0.011211544753394363 0.0 +v 1.203125 0.0 0.0 +v 1.2338593689009072 0.08991007559405689 0.0 +v 1.2477552781700332 0.0855620685882933 0.0 +v 1.2216718393490393 0.05124966476916108 0.0 +v 1.2345175843666247 0.06861381116847433 0.0 +v 1.232105711715437 0.03009340686732644 0.0 +v 1.234375 0.0 0.0 +v 1.263913397364324 0.05871250896746391 0.0 +v 1.242687041172971 0.09374351083441117 0.0 +v 1.28125 0.1 0.0 +v 1.2295230174150513 0.010397948252533469 0.0 +v 1.219596548632084 0.01615331795115285 0.0 +v 1.2119772976107839 0.008453779283290707 0.0 +v 1.2020959075381985 0.01234040819210695 0.0 +v 1.2421875 0.009976549494092818 0.0 +v 1.296875 0.1 0.0 +v 1.2729288186206835 0.051596063658878116 0.0 +v 1.258514215522356 0.0706487186346917 0.0 +v 1.2750115061946654 0.06350328830268397 0.0 +v 1.2781522858079652 0.031635895766074874 0.0 +v 1.2561363488182469 0.021880559250793706 0.0 +v 1.2875431137422664 0.06131139018182478 0.0 +v 1.307240712723405 0.07599882961219062 0.0 +v 1.2844712470958957 0.0806678442501113 0.0 +v 1.2956001035578824 0.077085500235769 0.0 +v 1.2535293051085752 0.01081095665927863 0.0 +v 1.299360180502086 0.06601542994862698 0.0 +v 1.2826862497302451 0.07046258596417188 0.0 +v 1.2706562127870105 0.0778254902911438 0.0 +v 1.3230527764663427 0.05541535973182517 0.0 +v 1.3019911454181172 0.04221124842095857 0.0 +v 1.28125 0.0 0.0 +v 1.2973974897981153 0.05375101802840955 0.0 +v 1.265625 0.0 0.0 +v 1.2855318254039576 0.04848742239966278 0.0 +v 1.3094776200032503 0.0518755889684598 0.0 +v 1.266830021068256 0.013520253681854723 0.0 +v 1.2772632318500847 0.04206679558736888 0.0 +v 1.321088031026794 0.03514972586148722 0.0 +v 1.3126508055271888 0.04168561664072556 0.0 +v 1.3064859091318302 0.025013529245974993 0.0 +v 1.2365264955939401 0.017815873279517905 0.0 +v 1.2676319369192084 0.06908251668391732 0.0 +v 1.265625 0.1 0.0 +v 1.2595592676477243 0.08274023534976063 0.0 +v 1.2690180128221762 0.08911182140481257 0.0 +v 1.2496116253651746 0.027902019913647637 0.0 +v 1.293201528877396 0.08871166564452453 0.0 +v 1.34375 0.1 0.0 +v 1.2734375 0.0061175213534327465 0.0 +v 1.2879686386826033 0.016627482065201617 0.0 +v 1.2676580925090353 0.025598734719770387 0.0 +v 1.2768366434266338 0.01890184966643902 0.0 +v 1.3212745431388828 0.07476698202246312 0.0 +v 1.332705363195212 0.06605985495020944 0.0 +v 1.337052424654762 0.05241907262268957 0.0 +v 1.3229939899895704 0.06516747046531657 0.0 +v 1.347657930693621 0.04280184186224667 0.0 +v 1.3152613750970075 0.08681810090413342 0.0 +v 1.3045320104262645 0.09126636004156384 0.0 +v 1.3369235188623823 0.08345432176463277 0.0 +v 1.3280798623101653 0.0446999280912625 0.0 +v 1.3366420968584842 0.031096893317000846 0.0 +v 1.360225247150866 0.06731687112298258 0.0 +v 1.3310027223179013 0.07568142958923697 0.0 +v 1.3428308601357257 0.07281310789242261 0.0 +v 1.328125 0.1 0.0 +v 1.359375 0.1 0.0 +v 1.350643242855273 0.0567502114521686 0.0 +v 1.342309036300089 0.06262804126834284 0.0 +v 1.375068477263617 0.044228927178456334 0.0 +v 1.3645405216724649 0.05377583403842353 0.0 +v 1.356099226183043 0.04828883795033508 0.0 +v 1.3620741330376545 0.029860311583006525 0.0 +v 1.4375 0.1 0.0 +v 1.3740524596534163 0.06426522731711681 0.0 +v 1.3745604684585166 0.054247077247786574 0.0 +v 1.393429502720082 0.05018194885098794 0.0 +v 1.3758000215844866 0.08209883789917827 0.0 +v 1.3787614062779883 0.07280621474029399 0.0 +v 1.385635799489626 0.05983064978113206 0.0 +v 1.40625 0.1 0.0 +v 1.4033332141426136 0.06615365412664095 0.0 +v 1.3955896932085678 0.05989885313781246 0.0 +v 1.4375 0.0 0.0 +v 1.3925235799713525 0.07161542703078702 0.0 +v 1.4084077124476178 0.05195067469593242 0.0 +v 1.3697891674204246 0.015472162516131392 0.0 +v 1.433971305099847 0.06909214755728678 0.0 +v 1.4025565893462582 0.037195290191820385 0.0 +v 1.4190235595456484 0.06375155618007106 0.0 +v 1.4014988602212612 0.04615252271850542 0.0 +v 1.410529928354285 0.06071691705423276 0.0 +v 1.0859375 0.00669631128415372 0.0 +v 0.028909661571941643 0.07682694708383998 0.0 +v 0.0390625 0.0932550307444241 0.0 +v 0.028226229607197968 0.08860067606912568 0.0 +v 0.008935305174771837 0.08159241150367046 0.0 +v 0.019629985251092715 0.08219498803276439 0.0 +v 0.0 0.08750000000000001 0.0 +v 0.009312825398504216 0.09187459325186974 0.0 +v 1.0390625 0.09430731377039411 0.0 +v 1.0859375 0.09368560046510674 0.0 +v 1.0742533802201921 0.08373910702426562 0.0 +v 1.1015625 0.09410174580479005 0.0 +v 1.140625 0.0 0.0 +v 1.185741248969956 0.0208403572974119 0.0 +v 1.1744638727673526 0.021222699511815893 0.0 +v 1.2903281779375533 0.03634545715505079 0.0 +v 1.2870454786039247 0.026738115388633372 0.0 +v 1.2996168236285266 0.03240446901373842 0.0 +v 1.405086479715412 0.08305141574236802 0.0 +v 1.2578125 0.09304986129276505 0.0 +v 1.2794047450347004 0.0897580816048058 0.0 +v 1.296875 0.0 0.0 +v 1.3376225830410438 0.04121025130766491 0.0 +v 1.344745643938023 0.08990812593580759 0.0 +v 1.3532094931066316 0.07538637757642878 0.0 +v 1.3646512806436544 0.07824948918229052 0.0 +v 1.3667394086697875 0.09027125183458691 0.0 +v 1.3557505342075507 0.0896046671630746 0.0 +v 1.34375 0.0 0.0 +v 1.3877282010670928 0.03647443674595112 0.0 +v 1.3847153734445208 0.04576696207459854 0.0 +v 1.3755222171697066 0.03075852014868087 0.0 +v 1.40625 0.0 0.0 +v 1.3960691924769448 0.017770087045360847 0.0 +v 1.3691919072758667 0.024414411543454983 0.0 +v 1.3596245658809984 0.0192843307060913 0.0 +v 1.3793565088152926 0.020602243353495062 0.0 +v 1.3492021036014805 0.027270010489009858 0.0 +v 1.359375 0.0 0.0 +v 1.3542201333838984 0.0356115788730257 0.0 +v 1.3639413129444566 0.040648546629988694 0.0 +v 1.33866075531243 0.015197520453207422 0.0 +v 1.3492736277928075 0.0165691127672838 0.0 +v 1.3955357097863361 0.02874414636614275 0.0 +v 1.4099117382281463 0.023943012437020576 0.0 +v 1.406870073031385 0.012156680006233562 0.0 +v 1.421875 0.0 0.0 +v 1.4213600128304085 0.014702945713185554 0.0 +v 1.4206376128853806 0.03856321199515364 0.0 +v 1.415222529239833 0.0071274816061732554 0.0 +v 1.4296875 0.0076341334360390265 0.0 +v 1.4411270549161013 0.011526382405272782 0.0 +v 1.4342777720307263 0.021481297908334872 0.0 +v 1.46875 0.0 0.0 +v 1.453125 0.0 0.0 +v 1.4573983869200964 0.030055265459978785 0.0 +v 1.446921462364986 0.022846824562151945 0.0 +v 1.45346037996371 0.012356692167014464 0.0 +v 1.440965046522056 0.042721955064213994 0.0 +v 1.4395934060958326 0.03148077544899925 0.0 +v 1.448918560445747 0.03604720009215611 0.0 +v 1.465013620557179 0.049828716926474004 0.0 +v 1.4523995532915395 0.04827109359004783 0.0 +v 1.4313017078893193 0.03819679541723284 0.0 +v 1.4300988769632639 0.05334586408775799 0.0 +v 1.459762617717188 0.04049787400409849 0.0 +v 1.479241384928426 0.030598897984144446 0.0 +v 1.471719542709581 0.039911965860077975 0.0 +v 1.456105204497743 0.07011662411487567 0.0 +v 1.468340746016101 0.02948892924909942 0.0 +v 1.4753393465524256 0.014838752915455493 0.0 +v 1.4939065305731731 0.05013752756289666 0.0 +v 1.457522477496247 0.05863915294528898 0.0 +v 1.479434726159915 0.0523548607756356 0.0 +v 1.4453457110267176 0.06296176901123939 0.0 +v 1.4715473564540273 0.06619722776620482 0.0 +v 1.436478278827652 0.06012630826814244 0.0 +v 1.4432403269600043 0.05258902396058958 0.0 +v 1.4119117493566862 0.033720269026097524 0.0 +v 1.4215383108027249 0.02665789678967093 0.0 +v 1.4192381881129923 0.052883133263367195 0.0 +v 1.4851658634688467 0.04142508872498604 0.0 +v 1.503208347927 0.024517457745960078 0.0 +v 1.4981207473017346 0.03716894395520621 0.0 +v 1.4712171248083459 0.056840770047969974 0.0 +v 1.4890879994928208 0.0670235802349903 0.0 +v 1.4910804700125944 0.026989111426390704 0.0 +v 1.482886149084634 0.021333371111802522 0.0 +v 1.484375 0.0 0.0 +v 1.4952524296997778 0.016469745478308352 0.0 +v 1.515625 0.0 0.0 +v 1.5625 0.0 0.0 +v 1.48762508287043 0.0574756047162402 0.0 +v 1.4987658924693885 0.06065469803990284 0.0 +v 1.5107357846996687 0.04874292244209105 0.0 +v 1.5227645768365015 0.0563628115670013 0.0 +v 1.4988256920726368 0.07128307340110708 0.0 +v 1.6171875 0.010039047544668186 0.0 +v 1.46875 0.1 0.0 +v 1.5020028827425627 0.045599475394654754 0.0 +v 1.5101194821130863 0.03675280225462521 0.0 +v 1.5341502947149763 0.04152849696281416 0.0 +v 1.52153144480388 0.0421771168030084 0.0 +v 1.547300489747861 0.02398580936460837 0.0 +v 1.65625 0.0 0.0 +v 1.527394812636869 0.03317479931585326 0.0 +v 1.5385087466158884 0.031095530792576268 0.0 +v 1.53125 0.0 0.0 +v 1.5300590151258435 0.016672999722567788 0.0 +v 1.6015625 0.00444560090551674 0.0 +v 1.5625 0.1 0.0 +v 1.6182215320116422 0.029094381630261724 0.0 +v 1.4858926246537014 0.010824553497226087 0.0 +v 1.51668790580078 0.02497307893857528 0.0 +v 1.5081891272589611 0.0116370485792258 0.0 +v 1.519871794013854 0.010907849228980481 0.0 +v 0.022656517385967447 0.06960676839252762 0.0 +v 0.03929138181769385 0.06680055131998155 0.0 +v 0.04705079347921817 0.05950631993930314 0.0 +v 0.03703442629629488 0.05662541654528531 0.0 +v 0.045990940119611426 0.07425025506625889 0.0 +v 1.1484375 0.008036012301592869 0.0 +v 1.1328125 0.007390863473873622 0.0 +v 1.30084742205878 0.012826839397370768 0.0 +v 1.3046875 0.00460904076240417 0.0 +v 1.3154588767874122 0.013941172298917136 0.0 +v 1.328125 0.0 0.0 +v 1.3174846223038639 0.024754820018697957 0.0 +v 1.3269069109878033 0.017393153347880122 0.0 +v 1.3233722717696426 0.00840638342872536 0.0 +v 1.2890625 0.006514351821604914 0.0 +v 1.390625 0.1 0.0 +v 1.4125236714082194 0.0737399152237308 0.0 +v 1.421875 0.1 0.0 +v 1.4148372518459955 0.08321362702001747 0.0 +v 1.4242198170479048 0.07590294926063877 0.0 +v 1.4373796108701185 0.08435838596205089 0.0 +v 1.4450718909394944 0.07504104802421672 0.0 +v 1.484375 0.1 0.0 +v 1.453125 0.1 0.0 +v 1.446774911050458 0.08428103563532238 0.0 +v 1.390625 0.0 0.0 +v 1.3766199902900185 0.009159145826127896 0.0 +v 1.3976288504908494 0.007573249481975239 0.0 +v 1.3873396309612676 0.011217158760719485 0.0 +v 1.3671875 0.0059823999990004945 0.0 +v 1.3515625 0.006600814440038473 0.0 +v 1.3595389781330058 0.010434716044588573 0.0 +v 1.4638605243468399 0.018351908362900106 0.0 +v 1.4627656237513558 0.008232892809115563 0.0 +v 1.5087156888203581 0.058683004674679684 0.0 +v 1.4765625 0.005413175120158698 0.0 +v 1.5503919544264955 0.011934290554757782 0.0 +v 1.5396083739285527 0.01813998561073788 0.0 +v 1.546875 0.0 0.0 +v 1.572690361637936 0.0240765665893834 0.0 +v 1.5600068935254783 0.02082299379985256 0.0 +v 1.5607694319402954 0.010353546515884599 0.0 +v 1.59375 0.0 0.0 +v 1.578125 0.0 0.0 +v 1.5635110993593146 0.03351137341998494 0.0 +v 1.5861182088380068 0.04875338246522857 0.0 +v 1.5768381171120125 0.03729470190031352 0.0 +v 1.5966775723204718 0.02380938850050426 0.0 +v 1.6875 0.0 0.0 +v 1.5893508744863634 0.034366884088710116 0.0 +v 1.5846768291680564 0.02330214587898542 0.0 +v 1.61296566710601 0.04577202707275543 0.0 +v 1.5779681849573062 0.012616248344848485 0.0 +v 1.5911485358446202 0.012404552291525785 0.0 +v 1.5997171483884283 0.04305335563760595 0.0 +v 1.5688581677483762 0.015366267558156817 0.0 +v 1.6047787827695374 0.012839291917670317 0.0 +v 1.609375 0.0 0.0 +v 1.5505559163660423 0.03700211648586114 0.0 +v 1.6089431304450825 0.031734082452701685 0.0 +v 1.6272216536748259 0.03256635025198338 0.0 +v 1.6131197825051504 0.02090726320951452 0.0 +v 1.6261307683205333 0.021993018197 0.0 +v 1.5019751450514416 0.08553675779827888 0.0 +v 1.601724116926533 0.06691348459981035 0.0 +v 1.5639905383988129 0.05718682115348071 0.0 +v 1.5992623843989913 0.03326315199392599 0.0 +v 1.6364679631357801 0.026269437034223735 0.0 +v 1.6406686411909317 0.013470542074093683 0.0 +v 1.6443033607149544 0.04771187771225498 0.0 +v 1.6270917156907991 0.009687208306237382 0.0 +v 1.6376762542676668 0.03798071659834857 0.0 +v 1.6602430789877822 0.029734451798707467 0.0 +v 1.6283424995369793 0.0514593552584303 0.0 +v 1.6500016419014845 0.03670906592230899 0.0 +v 1.648921740265461 0.024116844906038534 0.0 +v 1.5843552873082727 0.0843398761719421 0.0 +v 1.6607313283432457 0.014533540307616517 0.0 +v 1.6569898030915222 0.06465759643427874 0.0 +v 1.668463440231245 0.04019135058774605 0.0 +v 1.7095822990968053 0.02073256872701035 0.0 +v 1.65625 0.1 0.0 +v 1.5171288501800126 0.06593166276022681 0.0 +v 1.53125 0.1 0.0 +v 1.5092221748660732 0.0728652491341253 0.0 +v 1.524837545942684 0.08269718863427858 0.0 +v 1.596859725149917 0.05530817611262952 0.0 +v 1.6088094823033716 0.057121522635295706 0.0 +v 1.5813619294101011 0.0686262276355754 0.0 +v 1.6242806057452803 0.0757757028554547 0.0 +v 1.586955777523467 0.05945943856664827 0.0 +v 1.575434015059368 0.05871867646373662 0.0 +v 1.6141433546684052 0.06844048974324046 0.0 +v 1.5681237246847624 0.06981976922603161 0.0 +v 1.5710879716487671 0.04767584301994418 0.0 +v 1.6198881679012396 0.058817111134158606 0.0 +v 1.59375 0.1 0.0 +v 1.5589193400244479 0.04599884212940856 0.0 +v 1.6323765244680657 0.0646306446703118 0.0 +v 1.609375 0.1 0.0 +v 1.5467428138630148 0.05826142710366575 0.0 +v 1.6069786602749354 0.07544468201317146 0.0 +v 1.5923965794902037 0.07869925079554871 0.0 +v 1.5482241075697387 0.047555496950408366 0.0 +v 1.640625 0.0 0.0 +v 1.671875 0.0 0.0 +v 1.679880190867154 0.022756893798949192 0.0 +v 1.7109375 0.008596986153045362 0.0 +v 1.532956128776596 0.06880933859382728 0.0 +v 1.6377758123369832 0.05577357918618583 0.0 +v 1.5546875 0.0932689233952261 0.0 +v 1.6953125 0.005031751509259802 0.0 +v 1.5559331147497364 0.06681562158604279 0.0 +v 1.5582632539603722 0.08359646082749203 0.0 +v 1.5446138398712974 0.06976266764501382 0.0 +v 1.5378291047120451 0.08097490354996298 0.0 +v 1.5483779901669388 0.07969932937642478 0.0 +v 1.546875 0.1 0.0 +v 1.544223340646153 0.08959770711741566 0.0 +v 1.5323812948431337 0.08974110064818727 0.0 +v 1.515625 0.1 0.0 +v 1.5138848470969664 0.08796956308899144 0.0 +v 1.52275258453199 0.09282795298698977 0.0 +v 1.5916469108336109 0.06852204379937131 0.0 +v 1.5954949344028202 0.08919575740394296 0.0 +v 1.578125 0.1 0.0 +v 1.6640625 0.005548728624138246 0.0 +v 1.6744390871447965 0.01188447085500239 0.0 +v 1.6868914795908372 0.012450383010560522 0.0 +v 1.71875 0.0 0.0 +v 1.7037774460622177 0.03147559507643273 0.0 +v 1.703125 0.0 0.0 +v 1.692930489508627 0.024096653188926884 0.0 +v 1.7001691551768867 0.012954287718944031 0.0 +v 1.6485053676282362 0.05778776093795518 0.0 +v 1.6854932087154753 0.03231164357161807 0.0 +v 1.6875 0.1 0.0 +v 1.5756889316575668 0.07971674797477117 0.0 +v 1.7318530366122065 0.019755660192092593 0.0 +v 1.7228052475778264 0.011533493659225173 0.0 +v 1.568286880472175 0.08754398942563368 0.0 +v 1.657660842334791 0.04805546171878553 0.0 +v 1.6712364262720587 0.02955191676977677 0.0 +v 1.4887851635321292 0.08097638883806392 0.0 +v 1.7213217570859458 0.03401563167780731 0.0 +v 1.734375 0.0 0.0 +v 1.7130155197440597 0.02952746554362996 0.0 +v 1.720070704016135 0.021705814788507857 0.0 +v 1.7110516274308636 0.0430922848620513 0.0 +v 1.6688983790506768 0.02000408432829798 0.0 +v 1.4799787089258174 0.07380556017167661 0.0 +v 1.4760488031019376 0.08762484872300359 0.0 +v 1.6724115517696376 0.05696629715174191 0.0 +v 1.644675994542358 0.0711913149677223 0.0 +v 1.7341450084336276 0.058873588398207874 0.0 +v 1.6952306530217576 0.04491328891667506 0.0 +v 1.4855184655773466 0.09024208441600741 0.0 +v 1.4651022068557729 0.08950857497133365 0.0 +v 1.4589286196421347 0.08058976493069066 0.0 +v 1.4694051260560204 0.07704705771709754 0.0 +v 1.666909999689703 0.04940904701429661 0.0 +v 1.6786453682039755 0.046647038583346886 0.0 +v 1.6883795306563805 0.059569940297230915 0.0 +v 1.6584967018667098 0.08236808424580536 0.0 +v 1.7245923999162598 0.04806492070691489 0.0 +v 1.4949042758767423 0.09193762510928374 0.0 +v 1.6539685161732953 0.07383401418778865 0.0 +v 1.6662427482390094 0.07278965982200121 0.0 +v 1.6462224698009953 0.083412438611593 0.0 +v 1.671875 0.1 0.0 +v 1.640625 0.1 0.0 +v 1.6635676024813848 0.09197332881811152 0.0 +v 1.6356162594552766 0.07854615264087167 0.0 +v 1.6677998183669624 0.08197017406395563 0.0 +v 1.6791653534181346 0.07532021001265732 0.0 +v 1.6530180439010516 0.090629078239455 0.0 +v 1.6369932985546822 0.08953626964230466 0.0 +v 1.7402899109803278 0.037005231845294126 0.0 +v 1.7114161576461155 0.06933556238788083 0.0 +v 1.6741341368977067 0.06675200225397646 0.0 +v 1.6794335845125197 0.08881611670541872 0.0 +v 1.7360096686487674 0.04760002666915729 0.0 +v 1.730239017741063 0.0391067182398655 0.0 +v 1.7573822764783018 0.05692607019725631 0.0 +v 1.7455175594280856 0.054963632204796595 0.0 +v 1.7536130119702018 0.042866986543174314 0.0 +v 1.7478898364133448 0.07746877211384123 0.0 +v 1.7498050975016504 0.06588928222557577 0.0 +v 1.765625 0.1 0.0 +v 1.8125 0.1 0.0 +v 1.7585528343932277 0.07328430622575732 0.0 +v 1.7931369266401336 0.022730059088957075 0.0 +v 1.7139301127995121 0.05617647198611684 0.0 +v 1.71875 0.1 0.0 +v 1.8125 0.0 0.0 +v 1.7230745058403403 0.06474312864135992 0.0 +v 1.7345269285235023 0.07296869791021598 0.0 +v 1.7218002945127406 0.07860256992018522 0.0 +v 1.7015203445068605 0.06062535200885202 0.0 +v 1.686882265175909 0.08191745531308253 0.0 +v 1.6896411757879624 0.07087838480107073 0.0 +v 1.700478373673511 0.07142624578924607 0.0 +v 1.750565485241896 0.03172184467715293 0.0 +v 1.741089438709425 0.025926127708238614 0.0 +v 1.763444828416636 0.03418934674660068 0.0 +v 1.7435322839557155 0.012271409120026968 0.0 +v 1.74046365329238 0.06575510784013208 0.0 +v 1.7083927528030216 0.08317490726605499 0.0 +v 1.766903943244569 0.046838479984993936 0.0 +v 1.7338184254567188 0.009967753137173699 0.0 +v 1.7733822004024415 0.021899694408077444 0.0 +v 1.7902009159212207 0.03366999452198203 0.0 +v 1.7582496785880999 0.02645970538662963 0.0 +v 1.7501236140658623 0.02016154049099885 0.0 +v 1.78125 0.0 0.0 +v 1.776888496083461 0.03731049604794383 0.0 +v 1.765625 0.0 0.0 +v 1.734375 0.1 0.0 +v 1.7178474070677119 0.08895520028785851 0.0 +v 1.703125 0.1 0.0 +v 1.7305291995448389 0.08786644905481258 0.0 +v 1.6972644465282252 0.08892794039986751 0.0 +v 1.6959746069739314 0.0796451380804086 0.0 +v 1.771458591029275 0.060675148867836044 0.0 +v 1.7815370054883652 0.0281485776713317 0.0 +v 1.7858853730375828 0.014028489312781124 0.0 +v 1.7530300876657774 0.00943715279876278 0.0 +v 1.7388833213997499 0.08212297158526456 0.0 +v 1.7480489982763912 0.08881829349520982 0.0 +v 1.739099824327842 0.09182618906544449 0.0 +v 1.7628340236715851 0.06475695488725045 0.0 +v 1.7881237914665187 0.052234836033627 0.0 +v 1.774604859834908 0.009975792705127938 0.0 +v 1.7636882840434642 0.01488105870363821 0.0 +v 1.796875 0.0 0.0 +v 1.7771038586100683 0.0511489169615098 0.0 +v 1.7854724611170056 0.0425395722108804 0.0 +v 1.7989929959616433 0.04405231518521883 0.0 +v 1.78125 0.1 0.0 +v 1.7890625 0.005198617207885829 0.0 +v 1.8008352955132492 0.014421180529622381 0.0 +v 1.7769142185709887 0.06860790430038914 0.0 +v 1.805548475308495 0.02958705920941543 0.0 +v 1.8177101877343822 0.016549798078432848 0.0 +v 1.8046875 0.005608929542675206 0.0 +v 1.804313918811436 0.06696742429994569 0.0 +v 1.799483231285466 0.05601379951191503 0.0 +v 1.8229041183836203 0.0490631182348026 0.0 +v 1.790537044717248 0.06650119095249826 0.0 +v 1.8124672022234367 0.05682971321268508 0.0 +v 1.8113842797413537 0.04447848597999928 0.0 +v 1.828125 0.1 0.0 +v 1.8227003092803222 0.03280992375755065 0.0 +v 1.818703481455848 0.07019276519697829 0.0 +v 1.8004818554045356 0.08716519910663363 0.0 +v 1.828427685132251 0.06114359286775886 0.0 +v 1.8318645542985228 0.07481998855245157 0.0 +v 1.785488625948618 0.07895475303696557 0.0 +v 1.7971361263218075 0.07528456153253546 0.0 +v 1.809282665374277 0.07851134469726273 0.0 +v 1.8495673309082727 0.04417488267990935 0.0 +v 1.84375 0.1 0.0 +v 1.8126894968323164 0.08898644676492175 0.0 +v 1.8188554830999395 0.07985874840997792 0.0 +v 1.796875 0.1 0.0 +v 1.7727462668232863 0.0807880945932232 0.0 +v 1.7804138873688824 0.08888213790320904 0.0 +v 1.763001878116077 0.08204427883259935 0.0 +v 1.836853388607057 0.04998809044472856 0.0 +v 1.8392638424105046 0.08672246768954314 0.0 +v 1.8046875 0.09527127825378055 0.0 +v 1.7907324255579844 0.09134960186081863 0.0 +v 1.830454656214112 0.040840562172689505 0.0 +v 1.8695860380313394 0.030412325844668693 0.0 +v 1.84375 0.0 0.0 +v 1.8578376857504513 0.03476409564939738 0.0 +v 1.8454323864914286 0.033011887886804724 0.0 +v 1.8541590662941179 0.016018335951289552 0.0 +v 1.8723219141302787 0.05583253004793216 0.0 +v 1.8527450011213258 0.026029649686307662 0.0 +v 1.8390308797328374 0.018987052200703063 0.0 +v 1.8624176506844907 0.047128499993750045 0.0 +v 1.8340263474092138 0.029632654049264864 0.0 +v 1.8260270630538153 0.022893185785333826 0.0 +v 1.828125 0.0 0.0 +v 1.8443523998919278 0.06441176192241667 0.0 +v 1.859375 0.1 0.0 +v 1.8452095463708205 0.010442742583082207 0.0 +v 1.859375 0.0 0.0 +v 1.875107900020437 0.04267535753205959 0.0 +v 1.834314594889266 0.009070402436981257 0.0 +v 1.835603475363608 0.06661036085466876 0.0 +v 1.8422617200765585 0.07459895822614576 0.0 +v 1.8572145927640902 0.07235955049110562 0.0 +v 1.849445516839068 0.08280776280349336 0.0 +v 1.846447459510526 0.05416127815866075 0.0 +v 1.8987959281349744 0.020882040484599056 0.0 +v 1.860062942837242 0.059794923818640475 0.0 +v 1.8787613082752737 0.07688706796319852 0.0 +v 1.869276953342144 0.0684888708114205 0.0 +v 1.8677870868594715 0.07972601763804824 0.0 +v 1.8758533123177468 0.08827634838686232 0.0 +v 1.9375 0.1 0.0 +v 1.90625 0.1 0.0 +v 1.890625 0.1 0.0 +v 1.8898660848669029 0.08578830809387282 0.0 +v 1.9375 0.0 0.0 +v 1.8997466017116746 0.05476606532417768 0.0 +v 1.8984375 0.0924566972959733 0.0 +v 1.9060316170037825 0.07385241197067445 0.0 +v 1.8996238167640491 0.0820888712234446 0.0 +v 1.8907259728898647 0.07333802787798842 0.0 +v 1.921875 0.1 0.0 +v 1.881597905357765 0.0645075206378827 0.0 +v 1.8922684245819128 0.0626105194655783 0.0 +v 1.8848338624384315 0.051750567666077565 0.0 +v 1.883960092110936 0.09322980186421216 0.0 +v 1.90625 0.0 0.0 +v 1.8782964473742858 0.012432538090282545 0.0 +v 1.9358744778375674 0.0640913748311632 0.0 +v 1.909800838526179 0.08850513954494024 0.0 +v 1.877054768473963 0.025234600508746808 0.0 +v 1.8679103067211362 0.020019543493651926 0.0 +v 1.890625 0.0 0.0 +v 1.8872657732536058 0.019763724578483678 0.0 +v 1.8879941979385124 0.009720189398593926 0.0 +v 1.891493729555214 0.036171038413126314 0.0 +v 1.9236240724686038 0.07713813922598915 0.0 +v 1.8633258139148239 0.010144855838610519 0.0 +v 1.8995235560271722 0.009370348863152769 0.0 +v 1.919195996506464 0.016392640827447607 0.0 +v 1.9083511239277575 0.015707161053050603 0.0 +v 1.912580473430657 0.0349255478341504 0.0 +v 1.9077053508256736 0.02592384033146933 0.0 +v 1.8936923883189274 0.046324140827944026 0.0 +v 1.9022502104967294 0.03915632923118234 0.0 +v 1.9122149901912295 0.0487601987656801 0.0 +v 1.8844762912120492 0.04238448507386879 0.0 +v 1.914061589605972 0.0795979622200835 0.0 +v 1.9162058805177482 0.06811699029582434 0.0 +v 1.9214411599273697 0.0884689708065304 0.0 +v 1.9066563559847478 0.06306870775387743 0.0 +v 1.9263006056834557 0.06737659025342618 0.0 +v 1.9204757867946631 0.057146834456987275 0.0 +v 1.935837964621824 0.0853668630782878 0.0 +v 1.9341731764108827 0.07472623049558128 0.0 +v 1.96875 0.1 0.0 +v 1.9455335084391099 0.07839939030416941 0.0 +v 1.927227542111627 0.04223464661863609 0.0 +v 1.9464197798871528 0.06738677903017054 0.0 +v 1.9469853002930746 0.04705691309392454 0.0 +v 1.943833699804819 0.05714204289326361 0.0 +v 1.9328852596440835 0.05263515628016199 0.0 +v 1.9453125 0.0917016986224076 0.0 +v 1.9632764180698934 0.05768288531494852 0.0 +v 1.953125 0.1 0.0 +v 1.9300440044708889 0.0934358981912814 0.0 +v 1.9181993529532924 0.04199614028869084 0.0 +v 1.9229691101903832 0.03243778516130974 0.0 +v 1.940527333206769 0.03062964190752416 0.0 +v 1.9558258880981358 0.08348284085063287 0.0 +v 2.0 0.05 0.0 +v 1.9169569697508666 0.025385369173855567 0.0 +v 1.3359375 0.0058346883532210325 0.0 +v 1.3273674680796466 0.027375797352662243 0.0 +v 1.3896937553839608 0.08568192880360219 0.0 +v 1.3966018014536032 0.0797537575789373 0.0 +v 1.3986953532554485 0.09200439652626774 0.0 +v 1.3828125 0.09331880392372474 0.0 +v 1.5390625 0.007395868602421024 0.0 +v 1.5703125 0.005765944919490628 0.0 +v 1.6484375 0.006710031182146362 0.0 +v 1.6515865800422262 0.01533256930725254 0.0 +v 1.6229911059588915 0.04229708173961501 0.0 +v 1.5240350972659715 0.07291117937837695 0.0 +v 1.5347850656551827 0.056915933480674426 0.0 +v 1.539267732478338 0.049024200841396096 0.0 +v 1.6947862266007172 0.03519375923357724 0.0 +v 1.686500631827616 0.04159613887832297 0.0 +v 1.62498491563084 0.08787761738721535 0.0 +v 1.6155005312761743 0.08235813961865836 0.0 +v 1.6159963307226104 0.0924146595517014 0.0 +v 1.606208570454156 0.087856727055209 0.0 +v 1.7038958199664287 0.050559482260338504 0.0 +v 1.7702827219328294 0.09080070451892838 0.0 +v 1.813390733649991 0.03510296561607677 0.0 +v 1.8237484359141545 0.08981054658339784 0.0 +v 1.8359375 0.09524300679962719 0.0 +v 1.830625410829257 0.08384147184100993 0.0 +v 1.8086650300262423 0.020303201962797723 0.0 +v 1.8203125 0.00663550963805723 0.0 +v 1.9379671676121815 0.04111913538150203 0.0 +v 1.9307803152317702 0.022134725059581944 0.0 +v 1.921875 0.0 0.0 +v 1.950182841010334 0.02537887087364773 0.0 +v 1.9398918356517127 0.021519513918955666 0.0 +v 1.9346193985420848 0.011212851030028866 0.0 +v 1.96875 0.0 0.0 +v 1.9500503144888894 0.03636895113340986 0.0 +v 1.953125 0.0 0.0 +v 1.9718606426697614 0.031136117030947964 0.0 +v 1.9602942720962775 0.0309966411743927 0.0 +v 1.9599445622345173 0.04498981183469544 0.0 +v 1.9611426619009198 0.017560950165006417 0.0 +v 1.9767224767539082 0.04736954251942953 0.0 +v 1.9678338152322803 0.049702762980619854 0.0 +v 1.975056007194574 0.0591187223720223 0.0 +v 2.0 0.07500000000000001 0.0 +v 1.9674048666353776 0.07285086420872658 0.0 +v 1.94918004228749 0.012402640052044354 0.0 +v 1.9876573894478498 0.05491329231735705 0.0 +v 2.0 0.0625 0.0 +v 1.9876552851826037 0.06875 0.0 +v 1.9757457852203248 0.08995977501736682 0.0 +v 1.9904894912922708 0.07846836412861727 0.0 +v 2.0 0.08750000000000001 0.0 +v 1.978375575472629 0.07800856872763638 0.0 +v 1.984375 0.1 0.0 +v 1.977987747166319 0.0683401210089788 0.0 +v 1.9635352168195939 0.0907654721576604 0.0 +v 1.4140625 0.09340693185300022 0.0 +v 1.4258712740234818 0.08845606023326925 0.0 +v 1.4453125 0.0940139399084271 0.0 +v 1.4550273672131007 0.09008936386734873 0.0 +v 1.57724612312645 0.09058094072708421 0.0 +v 1.7584465116815646 0.09187922922345018 0.0 +v 1.9140625 0.0069490459796570335 0.0 +v 1.9252855455260578 0.008972607369090627 0.0 +v 2.0 0.025 0.0 +v 1.95631875407426 0.07239845315397546 0.0 +v 1.9609375 0.007043864469999123 0.0 +v 1.9725577790059203 0.012077725985068613 0.0 +v 1.9700730868848428 0.02152878429156701 0.0 +v 1.9853577648941225 0.02049499741977224 0.0 +v 1.984375 0.0 0.0 +v 2.0 0.0125 0.0 +v 1.9882985125827786 0.04228683115720464 0.0 +v 1.982865845917119 0.010343427342164958 0.0 +v 1.9911966042185143 0.027565224322289743 0.0 +v 2.0 0.037500000000000006 0.0 +v 1.979391827484008 0.037725323722284355 0.0 +v 0.5234375 0.005958134932592752 0.0 +v 0.5142784680705396 0.010903468646606625 0.0 +v 0.3673263878441546 0.07730173818107369 0.0 +v 0.2773350004082201 0.05986878937254789 0.0 +v 0.2715350125191014 0.0509997517419236 0.0 +v 1.0510395680699367 0.052287043311661154 0.0 +v 1.3261517319245435 0.08551799591213766 0.0 +v 1.3203125 0.0947564015888808 0.0 +v 1.8649878316527357 0.09044671381163405 0.0 +v 1.8515625 0.0930486219291116 0.0 +v 1.9889107302570925 0.08965403782136555 0.0 +v 1.9691281861547774 0.08259834667604866 0.0 +f 613 589 593 +f 1401 1428 1431 +f 44 45 48 +f 40 45 51 +f 388 421 423 +f 785 788 790 +f 1231 1214 1213 +f 1109 1113 1101 +f 206 183 186 +f 728 729 724 +f 670 298 301 +f 489 502 481 +f 1309 1314 1307 +f 1569 1565 1568 +f 1042 1028 1007 +f 959 944 943 +f 114 94 154 +f 40 58 63 +f 723 724 754 +f 584 612 638 +f 263 262 257 +f 40 42 45 +f 388 447 463 +f 363 365 678 +f 1620 1318 1320 +f 1223 1224 1216 +f 1533 1508 1697 +f 1431 1434 1453 +f 1025 1027 1032 +f 1116 1157 1156 +f 785 787 794 +f 946 954 978 +f 72 68 78 +f 59 46 54 +f 60 101 79 +f 100 87 77 +f 69 74 73 +f 64 51 55 +f 65 74 95 +f 240 63 62 +f 40 64 58 +f 41 42 240 +f 240 40 63 +f 41 8 42 +f 50 44 48 +f 49 42 8 +f 92 106 108 +f 46 52 54 +f 52 51 45 +f 47 48 49 +f 48 61 50 +f 49 45 42 +f 55 46 60 +f 61 47 43 +f 47 49 8 +f 45 49 48 +f 83 67 241 +f 106 92 104 +f 52 45 44 +f 55 51 46 +f 56 54 52 +f 46 51 52 +f 44 50 98 +f 103 128 119 +f 56 52 44 +f 82 90 117 +f 46 59 60 +f 55 36 58 +f 50 61 43 +f 43 83 50 +f 91 92 80 +f 60 36 55 +f 93 36 60 +f 41 240 62 +f 54 92 91 +f 118 102 117 +f 60 59 101 +f 79 81 60 +f 47 61 48 +f 70 63 69 +f 84 85 83 +f 43 67 83 +f 69 68 70 +f 39 41 62 +f 51 64 40 +f 55 58 64 +f 94 112 115 +f 70 62 63 +f 54 91 59 +f 124 284 126 +f 68 73 76 +f 72 62 70 +f 72 86 62 +f 71 72 87 +f 70 68 72 +f 69 63 58 +f 89 58 36 +f 71 86 72 +f 89 69 58 +f 76 78 68 +f 243 76 73 +f 68 69 73 +f 60 81 93 +f 65 73 74 +f 65 95 123 +f 242 88 111 +f 243 73 65 +f 75 77 78 +f 112 116 97 +f 71 87 35 +f 75 78 76 +f 72 78 77 +f 92 54 56 +f 81 79 66 +f 136 128 7 +f 79 82 66 +f 94 115 140 +f 89 93 74 +f 143 105 246 +f 90 59 91 +f 241 67 37 +f 85 50 83 +f 132 187 107 +f 122 102 118 +f 90 82 79 +f 98 85 104 +f 71 39 86 +f 62 86 39 +f 72 77 87 +f 110 100 77 +f 110 75 99 +f 87 100 35 +f 93 89 36 +f 69 89 74 +f 104 56 98 +f 247 119 102 +f 91 80 118 +f 92 56 104 +f 135 81 66 +f 74 93 95 +f 1688 129 96 +f 126 109 123 +f 93 81 137 +f 137 134 146 +f 75 110 77 +f 97 110 99 +f 116 242 97 +f 99 130 115 +f 44 98 56 +f 50 85 98 +f 243 75 76 +f 97 100 110 +f 111 35 100 +f 59 90 101 +f 79 101 90 +f 92 108 80 +f 132 107 84 +f 84 107 85 +f 106 104 107 +f 117 102 119 +f 97 99 112 +f 122 151 144 +f 104 85 107 +f 106 105 108 +f 120 286 142 +f 106 107 246 +f 108 105 133 +f 80 108 122 +f 147 163 131 +f 125 243 123 +f 82 119 128 +f 130 113 115 +f 112 94 114 +f 97 111 100 +f 139 153 244 +f 147 131 140 +f 94 155 154 +f 178 88 251 +f 99 115 112 +f 152 147 140 +f 112 114 116 +f 114 251 242 +f 118 117 90 +f 122 144 248 +f 91 118 90 +f 118 80 122 +f 103 119 57 +f 82 117 119 +f 150 156 260 +f 133 156 121 +f 141 37 142 +f 121 173 144 +f 151 108 133 +f 126 146 124 +f 146 126 123 +f 123 109 125 +f 137 81 135 +f 125 244 138 +f 109 175 191 +f 244 130 138 +f 146 123 95 +f 134 285 124 +f 130 244 113 +f 20 179 177 +f 7 128 103 +f 66 82 128 +f 136 7 129 +f 134 124 146 +f 99 75 130 +f 138 130 75 +f 131 166 168 +f 140 155 94 +f 141 132 84 +f 141 245 132 +f 156 157 149 +f 133 121 144 +f 247 145 57 +f 247 249 145 +f 136 135 66 +f 1689 129 1688 +f 128 136 66 +f 285 1688 96 +f 1689 137 135 +f 93 137 95 +f 243 138 75 +f 125 139 244 +f 283 284 285 +f 226 166 164 +f 152 115 113 +f 169 131 168 +f 241 141 84 +f 171 197 198 +f 149 157 159 +f 132 245 187 +f 157 156 133 +f 121 156 150 +f 1689 135 136 +f 247 248 249 +f 286 245 142 +f 95 137 146 +f 153 113 244 +f 113 153 147 +f 166 131 163 +f 179 20 178 +f 159 143 171 +f 260 156 149 +f 254 256 160 +f 173 53 249 +f 108 151 122 +f 133 144 151 +f 115 152 140 +f 113 147 152 +f 192 193 175 +f 154 162 170 +f 153 289 147 +f 175 195 201 +f 169 140 131 +f 155 148 162 +f 157 133 105 +f 149 159 200 +f 105 143 157 +f 159 157 143 +f 260 253 259 +f 53 173 161 +f 171 187 245 +f 296 276 172 +f 160 161 254 +f 139 125 191 +f 228 19 227 +f 255 150 158 +f 114 154 170 +f 193 195 175 +f 165 226 235 +f 288 250 287 +f 218 278 184 +f 281 204 211 +f 148 155 169 +f 289 195 250 +f 203 168 165 +f 202 206 186 +f 206 211 213 +f 165 168 166 +f 169 168 167 +f 169 167 186 +f 169 155 140 +f 180 162 148 +f 170 177 179 +f 187 171 143 +f 286 188 197 +f 265 268 267 +f 296 260 189 +f 121 190 173 +f 190 161 173 +f 139 191 175 +f 162 154 155 +f 192 175 109 +f 163 288 166 +f 182 20 177 +f 180 170 162 +f 177 170 180 +f 186 185 148 +f 179 114 170 +f 179 178 251 +f 148 185 180 +f 202 186 167 +f 177 184 182 +f 184 177 180 +f 206 213 183 +f 185 184 180 +f 185 218 184 +f 148 169 186 +f 185 183 218 +f 210 209 277 +f 183 185 186 +f 107 187 143 +f 245 141 142 +f 290 295 291 +f 198 199 276 +f 159 171 198 +f 276 296 189 +f 150 190 121 +f 254 161 190 +f 284 109 126 +f 109 191 125 +f 283 193 192 +f 228 287 174 +f 153 139 201 +f 193 274 195 +f 38 194 274 +f 274 194 275 +f 212 280 669 +f 202 208 206 +f 197 171 245 +f 197 188 198 +f 188 291 198 +f 200 198 189 +f 291 292 290 +f 295 271 1690 +f 198 200 159 +f 260 200 189 +f 175 201 139 +f 289 201 195 +f 203 202 167 +f 209 207 208 +f 168 203 167 +f 209 229 207 +f 277 208 202 +f 603 223 580 +f 211 208 281 +f 211 206 208 +f 207 233 281 +f 203 210 277 +f 203 165 210 +f 238 217 237 +f 213 216 183 +f 214 219 223 +f 196 239 19 +f 229 209 230 +f 227 235 226 +f 281 208 207 +f 211 204 213 +f 212 282 196 +f 229 230 225 +f 215 213 204 +f 215 216 213 +f 215 219 214 +f 220 222 181 +f 204 237 219 +f 222 279 224 +f 214 216 215 +f 181 182 278 +f 232 233 207 +f 577 236 575 +f 183 216 218 +f 220 218 216 +f 204 219 215 +f 580 223 217 +f 279 216 214 +f 278 220 181 +f 236 573 575 +f 601 586 579 +f 217 223 219 +f 279 222 220 +f 223 603 221 +f 222 224 176 +f 214 223 224 +f 207 229 232 +f 235 210 165 +f 166 226 165 +f 227 226 164 +f 228 227 164 +f 239 227 19 +f 225 567 232 +f 164 288 287 +f 234 209 210 +f 235 282 234 +f 692 205 570 +f 569 231 567 +f 225 232 229 +f 232 231 233 +f 238 231 236 +f 237 233 238 +f 234 210 235 +f 209 234 230 +f 565 567 225 +f 282 235 196 +f 573 236 571 +f 219 237 217 +f 204 233 237 +f 231 238 233 +f 238 236 577 +f 196 235 239 +f 227 239 235 +f 40 240 42 +f 141 241 37 +f 83 241 84 +f 97 242 111 +f 114 242 116 +f 123 243 65 +f 243 125 138 +f 134 1688 285 +f 283 127 38 +f 106 246 105 +f 107 143 246 +f 119 247 57 +f 102 122 248 +f 173 249 144 +f 102 248 247 +f 145 249 53 +f 144 249 248 +f 250 288 289 +f 275 250 195 +f 242 251 88 +f 179 251 114 +f 253 265 264 +f 120 252 292 +f 158 257 256 +f 255 190 150 +f 254 255 256 +f 255 158 256 +f 190 255 254 +f 257 158 259 +f 258 160 256 +f 257 264 263 +f 256 262 258 +f 260 259 158 +f 262 256 257 +f 253 257 259 +f 150 260 158 +f 260 149 200 +f 172 270 269 +f 325 313 324 +f 253 264 257 +f 258 262 300 +f 297 261 304 +f 258 300 24 +f 253 296 265 +f 263 264 267 +f 265 269 268 +f 267 268 297 +f 673 674 672 +f 319 268 273 +f 264 265 267 +f 172 269 265 +f 268 305 273 +f 268 319 297 +f 172 276 270 +f 268 269 305 +f 199 291 295 +f 199 295 270 +f 309 1690 306 +f 267 299 263 +f 316 308 314 +f 290 271 295 +f 314 308 311 +f 307 329 328 +f 38 274 193 +f 275 194 174 +f 275 174 287 +f 195 274 275 +f 198 276 189 +f 270 276 199 +f 208 277 209 +f 203 277 202 +f 184 278 182 +f 220 278 218 +f 216 279 220 +f 224 279 214 +f 280 205 565 +f 280 565 669 +f 204 281 233 +f 212 230 282 +f 234 282 230 +f 193 283 38 +f 284 283 192 +f 284 192 109 +f 284 124 285 +f 96 127 285 +f 285 127 283 +f 286 120 292 +f 245 286 197 +f 164 287 228 +f 275 287 250 +f 166 288 164 +f 289 288 163 +f 147 289 163 +f 289 153 201 +f 293 290 292 +f 329 307 294 +f 198 291 199 +f 292 291 188 +f 292 188 286 +f 293 292 252 +f 252 23 293 +f 266 339 330 +f 305 309 308 +f 273 305 308 +f 23 271 293 +f 290 293 271 +f 265 296 172 +f 260 296 253 +f 302 304 670 +f 344 303 343 +f 302 300 263 +f 297 299 267 +f 302 24 300 +f 262 263 300 +f 263 299 302 +f 261 343 304 +f 24 302 301 +f 299 304 302 +f 341 333 343 +f 304 299 297 +f 304 303 670 +f 269 270 305 +f 312 310 311 +f 271 23 307 +f 1690 309 270 +f 270 309 305 +f 311 308 306 +f 306 308 309 +f 326 324 321 +f 316 273 308 +f 312 311 306 +f 315 314 311 +f 312 306 328 +f 339 332 340 +f 315 313 314 +f 341 319 320 +f 315 311 310 +f 314 313 316 +f 338 310 337 +f 315 310 321 +f 325 335 313 +f 297 319 261 +f 331 348 340 +f 326 327 672 +f 315 324 313 +f 298 670 344 +f 273 316 319 +f 320 319 316 +f 313 320 316 +f 320 318 333 +f 294 337 312 +f 321 317 326 +f 294 322 337 +f 321 330 317 +f 330 339 317 +f 336 318 335 +f 321 324 315 +f 325 324 323 +f 671 336 325 +f 320 335 318 +f 327 326 317 +f 324 326 323 +f 345 361 350 +f 342 675 272 +f 1690 328 306 +f 271 307 328 +f 294 312 329 +f 328 329 312 +f 266 330 338 +f 321 310 338 +f 339 266 332 +f 339 340 354 +f 333 318 346 +f 304 343 303 +f 340 348 353 +f 13 481 437 +f 313 335 320 +f 336 335 325 +f 671 325 323 +f 520 521 518 +f 338 337 322 +f 310 312 337 +f 266 338 322 +f 338 330 321 +f 332 331 340 +f 351 345 348 +f 340 353 354 +f 319 341 261 +f 320 333 341 +f 318 336 530 +f 298 344 342 +f 341 343 261 +f 343 333 344 +f 346 344 333 +f 675 344 346 +f 345 351 352 +f 354 353 350 +f 347 346 318 +f 675 346 347 +f 334 518 521 +f 676 675 347 +f 352 351 349 +f 345 350 353 +f 357 361 345 +f 331 349 351 +f 361 1691 350 +f 331 351 348 +f 352 349 14 +f 411 373 400 +f 345 352 357 +f 345 353 348 +f 354 350 327 +f 327 317 354 +f 339 354 317 +f 363 356 357 +f 362 367 368 +f 356 361 357 +f 361 360 1691 +f 356 360 361 +f 363 357 364 +f 365 372 378 +f 362 364 366 +f 363 364 362 +f 356 363 678 +f 531 1692 503 +f 14 358 677 +f 376 372 370 +f 364 357 352 +f 365 363 362 +f 677 352 14 +f 364 677 366 +f 366 367 362 +f 368 372 365 +f 359 374 358 +f 367 366 374 +f 370 367 391 +f 365 362 368 +f 392 370 391 +f 368 367 370 +f 379 382 385 +f 373 680 378 +f 390 427 384 +f 370 369 376 +f 393 377 376 +f 378 377 373 +f 370 372 368 +f 411 400 431 +f 377 372 376 +f 378 380 678 +f 358 374 366 +f 679 374 359 +f 383 413 355 +f 413 391 679 +f 372 377 378 +f 393 394 377 +f 379 393 376 +f 687 505 507 +f 379 376 369 +f 385 389 379 +f 418 381 417 +f 379 369 382 +f 421 388 390 +f 502 489 504 +f 386 390 384 +f 383 382 369 +f 385 382 415 +f 418 417 416 +f 438 471 470 +f 384 385 386 +f 387 408 402 +f 383 414 382 +f 384 396 389 +f 414 415 382 +f 420 390 386 +f 387 389 396 +f 394 402 401 +f 384 389 385 +f 439 491 462 +f 394 393 398 +f 416 386 415 +f 390 428 427 +f 367 374 679 +f 679 395 413 +f 383 369 392 +f 370 392 369 +f 389 398 379 +f 398 387 394 +f 424 402 408 +f 377 394 400 +f 681 418 414 +f 413 392 391 +f 425 407 426 +f 682 417 381 +f 420 421 390 +f 379 398 393 +f 387 398 389 +f 409 403 435 +f 377 400 373 +f 394 401 400 +f 396 384 410 +f 402 394 387 +f 433 403 431 +f 399 408 387 +f 433 431 426 +f 462 468 410 +f 504 680 409 +f 410 457 462 +f 455 405 408 +f 405 424 408 +f 455 408 468 +f 402 424 432 +f 410 399 396 +f 401 402 432 +f 432 425 426 +f 399 387 396 +f 454 430 404 +f 409 680 411 +f 26 456 474 +f 454 371 425 +f 431 400 401 +f 409 411 403 +f 410 427 428 +f 371 407 425 +f 392 413 383 +f 395 355 413 +f 681 383 355 +f 383 681 414 +f 414 418 415 +f 415 386 385 +f 25 683 419 +f 416 397 420 +f 418 416 415 +f 416 417 397 +f 381 418 375 +f 416 420 386 +f 381 419 682 +f 420 397 421 +f 422 397 682 +f 421 397 422 +f 422 440 423 +f 421 422 423 +f 449 453 446 +f 469 439 457 +f 433 426 684 +f 405 425 424 +f 405 454 425 +f 406 684 407 +f 428 390 388 +f 410 384 427 +f 447 423 445 +f 469 464 482 +f 528 477 889 +f 453 429 446 +f 411 431 403 +f 404 430 475 +f 432 431 401 +f 425 432 424 +f 431 432 426 +f 437 435 436 +f 403 433 435 +f 433 436 435 +f 406 434 436 +f 437 434 13 +f 406 436 433 +f 437 436 434 +f 522 530 521 +f 435 437 502 +f 445 423 440 +f 429 447 445 +f 484 474 456 +f 410 428 457 +f 422 683 442 +f 487 685 493 +f 444 442 25 +f 886 442 444 +f 443 451 465 +f 440 422 442 +f 444 443 450 +f 440 446 445 +f 442 886 440 +f 429 494 447 +f 449 450 459 +f 429 445 446 +f 447 438 458 +f 388 423 447 +f 448 459 460 +f 448 478 452 +f 446 886 449 +f 448 452 453 +f 444 450 886 +f 1250 486 1249 +f 465 466 460 +f 465 460 459 +f 448 460 477 +f 470 471 464 +f 448 453 449 +f 429 453 452 +f 454 405 455 +f 404 371 454 +f 468 408 399 +f 454 455 430 +f 504 489 505 +f 473 481 499 +f 388 463 428 +f 457 439 462 +f 458 438 470 +f 457 428 458 +f 449 459 448 +f 465 450 443 +f 460 889 477 +f 455 468 430 +f 474 484 491 +f 482 474 439 +f 468 462 430 +f 447 458 463 +f 428 463 458 +f 494 452 485 +f 412 484 456 +f 465 459 450 +f 466 465 451 +f 451 441 466 +f 466 441 889 +f 495 479 480 +f 448 477 478 +f 410 468 399 +f 457 458 469 +f 470 469 458 +f 485 483 494 +f 469 470 464 +f 535 464 509 +f 492 532 490 +f 2 1137 1138 +f 466 889 460 +f 499 686 517 +f 503 507 505 +f 26 474 482 +f 484 412 475 +f 404 475 412 +f 462 475 430 +f 510 493 511 +f 480 487 496 +f 480 478 472 +f 478 477 472 +f 471 438 483 +f 1249 1248 1250 +f 526 524 525 +f 527 516 495 +f 485 452 496 +f 500 517 686 +f 481 473 489 +f 469 482 439 +f 471 510 509 +f 475 491 484 +f 464 535 482 +f 475 462 491 +f 452 478 496 +f 474 491 439 +f 545 544 542 +f 515 524 526 +f 480 479 497 +f 494 483 438 +f 508 506 473 +f 481 13 501 +f 680 504 380 +f 471 509 464 +f 514 537 536 +f 498 511 685 +f 447 494 438 +f 471 483 493 +f 493 483 485 +f 485 487 493 +f 487 497 685 +f 527 480 472 +f 452 494 429 +f 479 538 524 +f 480 496 478 +f 485 496 487 +f 479 498 685 +f 487 480 497 +f 495 538 479 +f 498 514 511 +f 501 499 481 +f 686 488 461 +f 887 272 676 +f 508 503 506 +f 13 488 501 +f 499 501 488 +f 435 502 409 +f 481 502 437 +f 508 473 517 +f 503 508 531 +f 502 504 409 +f 473 506 489 +f 503 1692 507 +f 380 504 505 +f 503 505 506 +f 489 506 505 +f 360 356 507 +f 687 507 356 +f 518 508 517 +f 334 674 531 +f 510 471 493 +f 490 532 513 +f 493 685 511 +f 556 562 529 +f 537 476 512 +f 550 689 688 +f 513 536 490 +f 515 537 514 +f 510 511 513 +f 513 511 514 +f 515 514 498 +f 516 1249 495 +f 479 524 498 +f 498 524 515 +f 528 516 527 +f 543 547 689 +f 499 517 473 +f 518 517 500 +f 518 500 520 +f 334 508 518 +f 476 688 547 +f 347 522 676 +f 500 686 888 +f 888 519 887 +f 520 887 521 +f 336 334 521 +f 676 522 887 +f 530 522 347 +f 671 334 336 +f 673 350 1691 +f 534 533 509 +f 513 532 534 +f 544 529 541 +f 525 486 541 +f 480 527 495 +f 528 527 472 +f 477 528 472 +f 889 892 553 +f 1136 1135 554 +f 525 546 526 +f 318 530 347 +f 336 521 530 +f 334 531 508 +f 523 1691 1692 +f 533 532 492 +f 510 534 509 +f 492 26 533 +f 535 26 482 +f 513 534 510 +f 533 534 532 +f 509 533 535 +f 26 535 533 +f 514 536 513 +f 512 490 536 +f 537 515 549 +f 536 537 512 +f 544 545 558 +f 524 538 525 +f 541 529 691 +f 495 486 538 +f 516 528 553 +f 556 560 562 +f 551 560 556 +f 525 538 486 +f 691 529 562 +f 542 1247 552 +f 1250 541 486 +f 541 546 525 +f 550 526 546 +f 476 549 688 +f 545 542 552 +f 539 545 540 +f 551 556 555 +f 688 526 550 +f 561 557 560 +f 537 549 476 +f 552 554 540 +f 688 515 526 +f 552 540 545 +f 548 539 540 +f 553 528 889 +f 1132 1248 1251 +f 890 467 1133 +f 1247 1132 1136 +f 548 540 554 +f 558 559 556 +f 539 555 559 +f 558 556 529 +f 550 691 564 +f 560 557 562 +f 544 558 529 +f 545 539 559 +f 556 559 555 +f 545 559 558 +f 563 564 557 +f 563 561 1 +f 563 557 561 +f 560 551 561 +f 546 691 550 +f 564 563 543 +f 564 562 557 +f 689 564 543 +f 693 606 568 +f 669 565 225 +f 586 601 574 +f 569 571 231 +f 567 692 569 +f 232 567 231 +f 205 692 565 +f 591 584 638 +f 568 606 637 +f 571 569 566 +f 611 692 637 +f 594 573 571 +f 231 571 236 +f 612 605 606 +f 576 583 574 +f 594 571 566 +f 575 573 576 +f 576 574 575 +f 176 221 602 +f 576 573 572 +f 577 575 574 +f 582 576 572 +f 583 582 585 +f 583 586 574 +f 577 217 238 +f 587 597 582 +f 614 609 617 +f 695 694 696 +f 604 603 601 +f 217 577 580 +f 574 601 577 +f 581 585 589 +f 586 581 694 +f 587 582 572 +f 585 582 578 +f 582 583 576 +f 604 602 603 +f 572 605 587 +f 614 619 592 +f 578 590 589 +f 583 585 581 +f 696 613 630 +f 224 223 221 +f 573 594 572 +f 597 595 578 +f 615 614 596 +f 621 620 596 +f 578 589 585 +f 620 625 590 +f 588 595 597 +f 578 595 590 +f 588 650 615 +f 602 221 603 +f 703 635 702 +f 572 594 605 +f 566 605 594 +f 596 620 590 +f 608 618 649 +f 627 593 625 +f 582 597 578 +f 596 590 595 +f 635 704 631 +f 644 588 591 +f 587 584 597 +f 597 584 588 +f 614 592 596 +f 611 606 566 +f 625 589 590 +f 584 591 588 +f 593 627 630 +f 581 586 583 +f 601 580 577 +f 695 604 579 +f 176 224 221 +f 580 601 603 +f 601 579 604 +f 602 604 600 +f 606 605 566 +f 584 587 605 +f 638 12 591 +f 584 605 612 +f 648 639 644 +f 593 630 613 +f 616 659 653 +f 592 623 621 +f 610 621 628 +f 569 611 566 +f 570 637 692 +f 615 596 595 +f 606 693 612 +f 648 591 12 +f 617 618 656 +f 588 615 595 +f 609 614 615 +f 668 622 623 +f 617 619 614 +f 617 609 698 +f 617 616 619 +f 644 698 650 +f 656 658 659 +f 616 653 647 +f 628 623 622 +f 621 596 592 +f 625 620 610 +f 619 668 623 +f 610 620 621 +f 647 619 616 +f 621 623 628 +f 619 623 592 +f 634 622 699 +f 625 629 627 +f 642 636 629 +f 626 636 642 +f 589 625 593 +f 629 625 610 +f 694 695 579 +f 627 636 630 +f 634 628 622 +f 642 640 633 +f 628 640 610 +f 642 633 645 +f 694 613 696 +f 589 613 581 +f 634 635 631 +f 645 624 642 +f 630 636 11 +f 695 600 604 +f 634 633 628 +f 646 598 641 +f 634 631 646 +f 667 668 647 +f 667 666 652 +f 629 636 627 +f 11 636 626 +f 611 637 606 +f 570 568 637 +f 693 638 612 +f 12 638 607 +f 610 640 629 +f 632 696 11 +f 628 633 640 +f 626 642 624 +f 641 645 646 +f 640 642 629 +f 647 655 643 +f 661 664 643 +f 639 649 644 +f 698 618 617 +f 646 645 633 +f 641 624 645 +f 634 646 633 +f 598 646 631 +f 618 658 656 +f 647 643 667 +f 639 648 12 +f 644 591 648 +f 608 649 639 +f 698 649 618 +f 615 650 609 +f 588 644 650 +f 661 643 655 +f 653 655 647 +f 643 664 666 +f 699 635 634 +f 616 656 659 +f 653 651 655 +f 618 608 658 +f 701 661 655 +f 895 893 701 +f 655 651 701 +f 617 656 616 +f 608 654 658 +f 659 651 653 +f 599 700 659 +f 659 658 654 +f 599 659 654 +f 817 806 726 +f 708 705 711 +f 651 700 701 +f 758 660 896 +f 700 599 662 +f 893 662 657 +f 756 898 715 +f 735 631 704 +f 660 664 661 +f 663 665 666 +f 705 708 706 +f 699 702 635 +f 663 666 664 +f 666 665 702 +f 666 667 643 +f 668 667 652 +f 668 652 699 +f 647 668 619 +f 225 230 669 +f 212 669 230 +f 670 301 302 +f 344 670 303 +f 672 671 323 +f 531 674 523 +f 326 672 323 +f 673 672 327 +f 350 673 327 +f 673 523 674 +f 334 671 674 +f 674 671 672 +f 344 675 342 +f 887 522 521 +f 272 675 676 +f 366 677 358 +f 352 677 364 +f 378 678 365 +f 687 678 380 +f 367 679 391 +f 359 395 679 +f 378 680 380 +f 411 680 373 +f 355 375 681 +f 418 681 375 +f 417 682 397 +f 683 682 419 +f 442 683 25 +f 682 683 422 +f 433 684 406 +f 426 407 684 +f 479 685 497 +f 488 686 499 +f 888 686 461 +f 678 687 356 +f 505 687 380 +f 688 549 515 +f 688 689 547 +f 564 689 550 +f 1132 1134 1136 +f 553 690 1251 +f 691 546 541 +f 564 691 562 +f 569 692 611 +f 565 692 567 +f 568 607 693 +f 638 693 607 +f 586 694 579 +f 613 694 581 +f 600 695 632 +f 11 696 630 +f 632 695 696 +f 708 703 706 +f 740 707 727 +f 650 698 609 +f 649 698 644 +f 668 699 622 +f 702 699 652 +f 659 700 651 +f 895 700 662 +f 660 661 701 +f 666 702 652 +f 702 665 703 +f 705 706 663 +f 703 665 706 +f 708 707 703 +f 635 703 704 +f 712 711 705 +f 716 697 712 +f 663 706 665 +f 722 739 697 +f 703 707 704 +f 697 711 712 +f 714 898 712 +f 663 664 758 +f 697 720 722 +f 735 736 598 +f 756 765 748 +f 708 711 727 +f 714 712 705 +f 738 739 713 +f 712 710 716 +f 713 722 22 +f 707 708 727 +f 705 663 714 +f 663 715 898 +f 805 770 21 +f 723 728 724 +f 716 710 719 +f 697 716 720 +f 730 729 734 +f 745 750 744 +f 716 721 720 +f 760 767 768 +f 805 731 899 +f 723 721 719 +f 721 716 719 +f 720 721 764 +f 797 722 764 +f 710 746 719 +f 713 709 738 +f 796 718 763 +f 763 737 749 +f 766 751 730 +f 734 745 744 +f 822 823 821 +f 750 726 804 +f 743 724 733 +f 739 711 697 +f 727 709 740 +f 719 728 723 +f 746 728 719 +f 741 745 728 +f 746 756 741 +f 767 760 766 +f 733 729 730 +f 759 751 747 +f 733 747 732 +f 724 729 733 +f 747 762 732 +f 728 734 729 +f 733 732 743 +f 745 741 750 +f 631 735 598 +f 704 707 735 +f 763 749 796 +f 707 736 735 +f 743 755 754 +f 709 727 738 +f 727 711 739 +f 713 739 722 +f 727 739 738 +f 709 736 740 +f 736 707 740 +f 746 710 756 +f 817 750 748 +f 800 814 902 +f 752 778 782 +f 902 766 769 +f 744 899 768 +f 748 741 756 +f 747 751 742 +f 728 745 734 +f 748 750 741 +f 710 898 756 +f 728 746 741 +f 759 733 730 +f 747 773 762 +f 722 720 764 +f 715 663 758 +f 795 723 718 +f 809 762 784 +f 730 767 766 +f 724 743 754 +f 773 777 807 +f 810 788 900 +f 823 737 763 +f 755 743 821 +f 732 821 743 +f 723 754 718 +f 765 756 715 +f 718 754 755 +f 896 893 894 +f 755 763 718 +f 831 21 770 +f 660 758 664 +f 758 757 765 +f 733 759 747 +f 730 751 759 +f 772 813 799 +f 813 772 771 +f 789 825 842 +f 776 834 775 +f 773 747 742 +f 783 777 778 +f 823 763 755 +f 795 764 721 +f 758 765 715 +f 796 797 764 +f 897 765 757 +f 767 730 734 +f 769 766 760 +f 734 744 767 +f 768 767 744 +f 744 804 899 +f 772 731 771 +f 772 799 760 +f 769 798 800 +f 770 805 804 +f 804 726 770 +f 731 772 768 +f 768 772 760 +f 798 799 813 +f 775 773 742 +f 773 775 777 +f 742 776 775 +f 794 826 812 +f 766 902 751 +f 777 775 774 +f 841 814 800 +f 789 792 825 +f 833 834 903 +f 778 777 774 +f 810 752 790 +f 762 807 784 +f 752 781 783 +f 774 833 782 +f 819 808 717 +f 782 786 752 +f 784 807 783 +f 774 782 778 +f 780 787 786 +f 752 783 778 +f 783 781 784 +f 784 820 819 +f 762 809 732 +f 786 787 785 +f 812 871 794 +f 780 786 782 +f 854 855 849 +f 832 825 792 +f 787 780 792 +f 779 901 791 +f 752 786 790 +f 905 907 793 +f 771 725 813 +f 785 790 786 +f 873 900 788 +f 810 790 788 +f 810 781 752 +f 834 776 903 +f 812 792 789 +f 853 856 909 +f 871 885 794 +f 826 787 792 +f 812 789 844 +f 723 795 721 +f 796 795 718 +f 797 796 749 +f 796 764 795 +f 749 22 797 +f 722 797 22 +f 800 798 803 +f 769 760 799 +f 798 802 803 +f 769 799 798 +f 847 841 803 +f 725 801 802 +f 798 725 802 +f 802 801 803 +f 801 815 803 +f 847 815 816 +f 750 804 744 +f 726 806 770 +f 806 831 770 +f 773 807 762 +f 817 748 897 +f 894 897 896 +f 783 807 777 +f 819 809 784 +f 779 808 820 +f 822 717 753 +f 847 830 841 +f 824 810 901 +f 840 839 829 +f 875 848 867 +f 867 876 875 +f 776 742 814 +f 798 813 725 +f 902 742 751 +f 801 761 815 +f 816 815 761 +f 829 6 904 +f 816 811 830 +f 750 817 726 +f 748 765 897 +f 819 717 822 +f 818 897 894 +f 820 784 781 +f 820 781 824 +f 808 819 820 +f 732 809 821 +f 822 821 809 +f 822 809 819 +f 823 822 753 +f 737 823 753 +f 821 823 755 +f 820 824 779 +f 781 810 824 +f 832 792 780 +f 835 825 811 +f 787 826 794 +f 792 812 826 +f 850 857 860 +f 861 863 844 +f 850 785 794 +f 832 811 825 +f 816 761 839 +f 830 776 841 +f 897 831 806 +f 818 21 831 +f 833 832 780 +f 811 832 903 +f 782 833 780 +f 834 833 774 +f 775 834 774 +f 903 776 830 +f 811 816 840 +f 913 835 904 +f 851 854 849 +f 838 846 845 +f 845 871 843 +f 866 869 863 +f 863 843 844 +f 846 850 885 +f 761 829 839 +f 829 904 840 +f 811 840 835 +f 816 839 840 +f 814 841 776 +f 803 841 800 +f 825 835 842 +f 843 838 845 +f 844 836 861 +f 913 864 836 +f 789 842 836 +f 836 844 789 +f 846 856 857 +f 850 874 785 +f 846 851 856 +f 846 838 851 +f 815 847 803 +f 830 847 816 +f 864 828 861 +f 812 844 843 +f 838 1693 854 +f 863 828 866 +f 857 858 872 +f 849 909 856 +f 865 877 880 +f 920 931 937 +f 793 907 906 +f 850 846 857 +f 838 854 851 +f 854 852 932 +f 917 933 916 +f 793 906 911 +f 849 856 851 +f 856 853 857 +f 853 858 857 +f 872 858 837 +f 1139 858 853 +f 860 873 788 +f 860 872 873 +f 837 859 872 +f 874 788 785 +f 862 864 913 +f 863 861 828 +f 836 864 861 +f 848 864 862 +f 876 828 875 +f 869 870 863 +f 842 835 913 +f 848 875 864 +f 865 876 877 +f 869 868 870 +f 914 869 865 +f 867 877 876 +f 876 865 866 +f 880 882 914 +f 1693 868 916 +f 865 869 866 +f 1693 870 868 +f 838 843 870 +f 863 870 843 +f 843 871 812 +f 885 845 846 +f 873 872 859 +f 857 872 860 +f 859 5 873 +f 873 5 900 +f 850 860 874 +f 788 874 860 +f 864 875 828 +f 876 866 828 +f 877 867 827 +f 912 937 931 +f 827 881 883 +f 883 884 879 +f 881 1131 883 +f 916 914 882 +f 883 877 827 +f 880 879 882 +f 917 882 918 +f 880 883 879 +f 1693 916 852 +f 883 880 877 +f 1131 881 878 +f 935 940 34 +f 929 928 923 +f 794 885 850 +f 845 885 871 +f 449 886 450 +f 886 446 440 +f 888 887 520 +f 887 519 272 +f 500 888 520 +f 461 519 888 +f 892 889 441 +f 690 1134 1132 +f 548 554 1135 +f 892 690 553 +f 441 890 892 +f 890 1133 892 +f 893 657 894 +f 701 893 660 +f 657 818 894 +f 818 831 897 +f 700 895 701 +f 662 893 895 +f 758 896 757 +f 893 896 660 +f 817 897 806 +f 896 897 757 +f 712 898 710 +f 663 898 714 +f 805 899 804 +f 731 768 899 +f 5 791 900 +f 900 791 901 +f 824 901 779 +f 900 901 810 +f 800 902 769 +f 742 902 814 +f 811 903 830 +f 833 903 832 +f 913 904 6 +f 840 904 835 +f 849 855 906 +f 837 1139 905 +f 910 1140 908 +f 905 1139 909 +f 849 906 907 +f 849 907 909 +f 1140 911 1141 +f 905 909 907 +f 1141 906 855 +f 1141 912 1140 +f 910 793 911 +f 933 920 932 +f 955 939 954 +f 862 913 6 +f 842 913 836 +f 869 914 868 +f 880 914 865 +f 918 879 919 +f 919 921 918 +f 914 916 868 +f 917 916 882 +f 918 882 879 +f 917 915 920 +f 919 879 884 +f 917 918 921 +f 922 923 926 +f 917 921 915 +f 920 915 931 +f 932 920 855 +f 934 926 923 +f 921 919 923 +f 926 927 922 +f 928 915 921 +f 934 919 884 +f 923 922 929 +f 947 878 940 +f 927 924 935 +f 925 929 930 +f 931 955 912 +f 934 923 919 +f 927 926 924 +f 975 936 945 +f 942 944 945 +f 923 928 921 +f 938 928 925 +f 930 929 922 +f 928 929 925 +f 927 958 922 +f 1253 995 935 +f 915 938 931 +f 955 938 939 +f 854 932 855 +f 933 932 852 +f 916 933 852 +f 917 920 933 +f 947 934 884 +f 926 934 924 +f 1131 947 884 +f 1038 1050 1051 +f 939 925 942 +f 939 942 936 +f 1141 937 912 +f 920 937 855 +f 928 938 915 +f 939 938 925 +f 942 925 930 +f 936 954 939 +f 930 951 944 +f 947 935 924 +f 983 1013 999 +f 942 930 944 +f 927 995 997 +f 1142 978 953 +f 951 930 958 +f 968 971 972 +f 955 931 938 +f 936 942 945 +f 936 978 954 +f 1140 912 1142 +f 935 947 940 +f 934 947 924 +f 1011 987 965 +f 956 949 957 +f 997 958 927 +f 951 956 944 +f 958 930 922 +f 984 975 945 +f 959 962 970 +f 959 943 962 +f 956 951 949 +f 955 954 946 +f 912 955 946 +f 948 943 956 +f 956 957 948 +f 976 961 971 +f 908 1142 953 +f 970 985 984 +f 977 968 972 +f 944 956 943 +f 970 984 959 +f 995 927 935 +f 951 958 949 +f 967 962 964 +f 945 944 959 +f 943 948 964 +f 975 988 978 +f 964 969 967 +f 967 970 962 +f 957 974 948 +f 961 991 990 +f 963 974 972 +f 966 971 961 +f 943 964 962 +f 964 963 966 +f 963 972 971 +f 967 969 965 +f 966 961 969 +f 969 964 966 +f 967 965 970 +f 994 981 992 +f 1001 1004 950 +f 990 969 961 +f 990 980 1009 +f 987 985 970 +f 985 975 984 +f 963 971 966 +f 968 981 976 +f 963 948 974 +f 1144 979 992 +f 1144 992 977 +f 981 996 973 +f 1003 1006 1002 +f 991 980 990 +f 974 999 972 +f 957 998 974 +f 968 976 971 +f 990 1009 969 +f 975 978 936 +f 974 998 999 +f 949 997 957 +f 964 948 963 +f 997 995 982 +f 976 981 973 +f 33 953 978 +f 968 992 981 +f 1252 982 1253 +f 1253 1143 1252 +f 983 998 982 +f 952 1000 1013 +f 959 984 945 +f 987 986 985 +f 988 986 33 +f 986 988 985 +f 1009 1010 1011 +f 965 987 970 +f 985 988 975 +f 986 987 960 +f 33 978 988 +f 950 989 1010 +f 965 1009 1011 +f 991 961 976 +f 980 1001 1009 +f 973 1015 976 +f 1002 1006 1008 +f 977 992 968 +f 994 992 979 +f 1026 1028 1042 +f 994 996 981 +f 1253 935 34 +f 1020 1021 1049 +f 957 997 982 +f 1020 996 994 +f 1021 1020 994 +f 1002 1008 1019 +f 982 998 957 +f 997 949 958 +f 999 998 983 +f 1013 983 1252 +f 972 999 977 +f 1013 1000 1014 +f 1013 1014 1145 +f 1004 1003 950 +f 1010 1009 1001 +f 1005 980 991 +f 980 1005 1004 +f 1004 1001 980 +f 1003 1004 1006 +f 1015 991 976 +f 991 1015 1005 +f 1004 1005 1006 +f 1017 1005 1015 +f 1015 973 1018 +f 1007 1008 1017 +f 1017 1006 1005 +f 1046 1019 1039 +f 965 969 1009 +f 950 1010 1001 +f 989 960 1011 +f 1010 989 1011 +f 987 1011 960 +f 1044 17 1052 +f 1023 1024 1041 +f 1000 941 1014 +f 1037 1014 941 +f 1014 1144 1145 +f 1041 1018 973 +f 1008 1006 1017 +f 1023 996 1020 +f 1048 1052 1079 +f 1018 1017 1015 +f 1017 1016 1042 +f 1041 973 996 +f 1017 1018 1016 +f 1021 994 979 +f 1007 1039 1008 +f 1043 1023 1020 +f 979 1037 1051 +f 1049 1050 993 +f 1032 1031 1035 +f 1024 1026 1016 +f 1043 1020 1012 +f 1024 1023 1022 +f 1026 1024 1027 +f 1016 1018 1041 +f 1026 1027 1025 +f 1032 1030 1045 +f 1027 1024 1022 +f 1026 1025 1028 +f 1022 1034 1031 +f 1027 1022 1031 +f 1055 1028 1025 +f 1040 1039 1007 +f 1064 1036 1060 +f 1080 1066 1055 +f 1097 1062 1060 +f 18 1046 1040 +f 1023 1043 1022 +f 1034 1084 1031 +f 1031 1032 1027 +f 1030 1054 1045 +f 1035 1057 1075 +f 1028 1055 1082 +f 1049 1048 1012 +f 1058 1084 1052 +f 1254 1078 1088 +f 1032 1035 1030 +f 1057 1089 1090 +f 1066 1082 1055 +f 1070 1064 1059 +f 1144 1037 979 +f 1007 1028 1040 +f 1037 941 1038 +f 1019 1008 1039 +f 1045 1025 1032 +f 1055 1025 1045 +f 1023 1041 996 +f 1016 1041 1024 +f 1017 1042 1007 +f 1026 1042 1016 +f 1079 1043 1012 +f 1022 1043 1034 +f 993 1044 1048 +f 1084 1035 1031 +f 1081 1150 1083 +f 1045 1054 1056 +f 1019 1046 18 +f 1039 1040 1046 +f 1030 1035 1075 +f 1098 1102 1103 +f 993 1048 1049 +f 1044 1052 1048 +f 1020 1049 1012 +f 1049 1021 1050 +f 1051 1050 1021 +f 1038 993 1050 +f 979 1051 1021 +f 1038 1051 1037 +f 1079 1034 1043 +f 1070 1068 1073 +f 1092 1102 1094 +f 1052 17 1063 +f 1065 1056 1059 +f 1064 1073 1036 +f 1059 1072 1070 +f 1065 1062 1061 +f 1056 1080 1045 +f 1096 1097 1060 +f 1063 1058 1052 +f 1054 1059 1056 +f 1080 1065 1066 +f 1072 1068 1070 +f 1065 1064 1062 +f 1036 1091 1060 +f 1097 1053 1085 +f 1074 1058 1063 +f 1090 1087 1088 +f 1063 1071 1074 +f 1064 1070 1073 +f 1035 1058 1089 +f 1062 1064 1060 +f 1064 1065 1059 +f 1065 1061 1066 +f 1083 1082 1066 +f 1066 1151 1083 +f 1073 1077 1067 +f 1085 1047 1151 +f 1078 1258 1077 +f 1072 1059 1054 +f 1085 1061 1062 +f 1102 1091 1094 +f 1063 17 1071 +f 1054 1075 1072 +f 1075 1146 1072 +f 1077 1073 1068 +f 1067 1036 1073 +f 1087 1074 1071 +f 1089 1074 1090 +f 1030 1075 1054 +f 1146 1075 1057 +f 1100 1168 1095 +f 1166 1168 1173 +f 1077 1068 1148 +f 1077 1076 1099 +f 1146 1068 1072 +f 1164 1176 1175 +f 1048 1079 1012 +f 1052 1034 1079 +f 1045 1080 1055 +f 1065 1080 1056 +f 1082 1150 1040 +f 1028 1082 1040 +f 18 1150 1081 +f 1151 1066 1061 +f 1047 1081 1083 +f 1052 1084 1034 +f 1035 1084 1058 +f 1047 1085 1053 +f 1154 1103 1155 +f 1102 1694 1091 +f 1071 1069 1087 +f 1069 1261 1087 +f 1256 1254 1255 +f 1078 1148 1147 +f 1035 1089 1057 +f 1058 1074 1089 +f 1087 1090 1074 +f 1090 1088 1147 +f 1094 1036 1067 +f 1106 1093 1095 +f 1153 1093 1099 +f 1067 1093 1092 +f 1102 1098 1694 +f 1091 1096 1060 +f 1036 1094 1091 +f 1067 1092 1094 +f 1158 1157 1105 +f 1099 1093 1067 +f 1033 1053 1097 +f 1097 1085 1062 +f 1033 1097 1096 +f 1092 1107 1103 +f 1033 1096 1695 +f 1077 1099 1067 +f 1099 1076 1100 +f 1254 1256 1078 +f 1172 1173 1168 +f 1110 1170 1171 +f 1106 1109 1101 +f 1103 1102 1092 +f 1154 1104 1098 +f 1107 1093 1106 +f 1158 1156 1157 +f 1098 1104 1694 +f 1096 1091 1694 +f 1113 1117 1156 +f 1105 1086 1158 +f 1106 1110 1109 +f 1101 1155 1107 +f 1093 1107 1092 +f 1101 1107 1106 +f 1187 1183 1182 +f 1123 1612 1117 +f 1110 1106 1095 +f 1118 1114 1161 +f 1110 1095 1170 +f 1109 1110 1171 +f 1153 1100 1095 +f 1167 1273 1275 +f 1124 1130 1121 +f 1117 1612 1116 +f 1114 1113 1109 +f 1114 1118 1113 +f 1114 1108 1161 +f 1109 1108 1114 +f 1160 1108 1162 +f 1203 1199 1205 +f 1118 1115 1121 +f 1157 31 1105 +f 1117 1113 1118 +f 1117 1116 1156 +f 1123 1121 1120 +f 1613 1614 1612 +f 1129 1121 1115 +f 1123 1118 1121 +f 1668 1266 1267 +f 1124 1121 1129 +f 1612 1615 1116 +f 1127 1129 1160 +f 1121 1130 1120 +f 1613 1120 1149 +f 1118 1123 1117 +f 1127 1174 1175 +f 1263 1120 1130 +f 1175 1209 1127 +f 1209 1124 1129 +f 1190 1183 1187 +f 1160 1115 1161 +f 1174 1127 1160 +f 1175 1178 1210 +f 1265 1667 1149 +f 1160 1129 1115 +f 1209 1129 1127 +f 1211 1130 1124 +f 1263 1130 1128 +f 947 1131 878 +f 883 1131 884 +f 1132 1247 1248 +f 1133 1134 690 +f 1133 467 1134 +f 892 1133 690 +f 1247 1136 554 +f 1134 467 891 +f 891 1136 1134 +f 548 1135 1137 +f 1135 1136 1138 +f 1138 1136 891 +f 2 1138 891 +f 1137 1135 1138 +f 858 1139 837 +f 909 1139 853 +f 911 1140 910 +f 1142 912 946 +f 906 1141 911 +f 937 1141 855 +f 1140 1142 908 +f 978 1142 946 +f 1145 1144 977 +f 952 1252 1143 +f 977 999 1145 +f 1037 1144 1014 +f 1013 1145 999 +f 1147 1146 1057 +f 1077 1148 1078 +f 1090 1147 1057 +f 1078 1147 1088 +f 1068 1146 1148 +f 1147 1148 1146 +f 1276 1125 1278 +f 1077 1258 1076 +f 1040 1150 18 +f 1083 1150 1082 +f 1085 1151 1061 +f 1047 1083 1151 +f 1093 1153 1095 +f 1087 1261 1088 +f 1099 1100 1153 +f 1104 1154 1086 +f 1103 1154 1098 +f 1107 1155 1103 +f 1156 1155 1101 +f 1156 1101 1113 +f 1154 1155 1158 +f 1154 1158 1086 +f 1157 1116 1615 +f 1156 1158 1155 +f 1108 1171 1162 +f 1149 1120 1263 +f 1108 1160 1161 +f 1161 1115 1118 +f 1111 1165 1162 +f 1167 1160 1162 +f 1162 1165 1167 +f 1125 1165 1166 +f 1166 1165 1111 +f 1273 1167 1125 +f 1167 1165 1125 +f 1168 1166 1111 +f 1276 1169 32 +f 1125 1276 1273 +f 1167 1164 1174 +f 1170 1168 1111 +f 1168 1100 1172 +f 1170 1111 1171 +f 1278 1173 1277 +f 1095 1168 1170 +f 1162 1171 1111 +f 1109 1171 1108 +f 1258 1259 1611 +f 1257 1159 1610 +f 1277 1172 1610 +f 1169 1277 1159 +f 1167 1174 1160 +f 1175 1174 1164 +f 1176 1180 1178 +f 1178 1175 1176 +f 1274 1176 1164 +f 1122 1181 1177 +f 1183 1178 1181 +f 1163 1177 1180 +f 1195 1211 1179 +f 1178 1180 1181 +f 1194 1195 1179 +f 1163 1180 1176 +f 1181 1180 1177 +f 1200 1279 1201 +f 1181 1122 1182 +f 1198 1192 1196 +f 1194 1183 1190 +f 1181 1182 1183 +f 1210 1179 1209 +f 1200 1201 1197 +f 1188 1187 1182 +f 1182 1122 1185 +f 1193 1189 1191 +f 1188 1182 1185 +f 1191 1190 1187 +f 1280 1185 1184 +f 1187 1188 1279 +f 1196 1191 1186 +f 1128 1211 1195 +f 1191 1187 1186 +f 1194 1190 1189 +f 1198 1196 1200 +f 1189 1190 1191 +f 1238 1285 1616 +f 1196 1193 1191 +f 1203 1193 1192 +f 1189 1193 1208 +f 1194 1189 1195 +f 1194 1179 1210 +f 1208 1205 1207 +f 1128 1195 1207 +f 1197 1198 1200 +f 1192 1193 1196 +f 1199 1203 1206 +f 1198 1197 1212 +f 1200 1196 1186 +f 1212 1204 1198 +f 1204 1192 1198 +f 1406 1398 1404 +f 1279 1186 1187 +f 1280 1201 1279 +f 1243 9 1220 +f 1220 1213 1217 +f 1214 1212 1217 +f 1202 1212 1214 +f 1215 1192 1204 +f 1199 1268 1205 +f 1223 1206 1204 +f 1203 1192 1215 +f 1207 1205 1126 +f 1203 1205 1208 +f 1223 1204 1202 +f 1227 1310 1390 +f 1128 1207 1126 +f 1208 1195 1189 +f 1208 1193 1203 +f 1195 1208 1207 +f 1210 1209 1175 +f 1124 1209 1179 +f 1178 1183 1210 +f 1194 1210 1183 +f 1130 1211 1128 +f 1179 1211 1124 +f 1217 1213 1214 +f 1202 1204 1212 +f 1236 1231 1244 +f 1331 1329 1621 +f 1217 1212 1197 +f 9 1245 1220 +f 1204 1206 1215 +f 1203 1215 1206 +f 1202 1230 1224 +f 1329 1281 1226 +f 1197 1218 1217 +f 1282 1243 1201 +f 1197 1201 1218 +f 1280 1282 1201 +f 1230 1225 1281 +f 1231 1230 1214 +f 1218 1220 1217 +f 1243 1220 1218 +f 1236 1237 1232 +f 1225 1230 1231 +f 1616 1285 1283 +f 1311 1334 1339 +f 1224 1223 1202 +f 1206 1223 1216 +f 1281 1331 1224 +f 1216 1224 1227 +f 1214 1230 1202 +f 1269 1229 1398 +f 1239 1237 1236 +f 1281 1225 1226 +f 1224 1230 1281 +f 1310 1227 1331 +f 1233 1225 1231 +f 1314 1318 1307 +f 1271 1669 1267 +f 1216 1227 1390 +f 1244 1231 1213 +f 1232 1233 1236 +f 1237 1239 1284 +f 1225 1233 1226 +f 1287 1291 1305 +f 1303 1228 1308 +f 1246 1244 1245 +f 1221 1245 9 +f 1395 1402 1377 +f 1291 1341 1344 +f 1231 1236 1233 +f 1222 1288 1285 +f 1232 1237 1305 +f 1213 1245 1244 +f 1341 1292 1338 +f 1234 1305 1237 +f 1283 1287 1234 +f 1287 1288 1302 +f 1333 1301 1334 +f 1234 1287 1305 +f 1388 1321 1319 +f 1228 1303 1304 +f 1281 1329 1331 +f 1201 1243 1218 +f 9 1243 1219 +f 1220 1245 1213 +f 1239 1236 1244 +f 1245 1221 1246 +f 1238 1246 1221 +f 1244 1246 1239 +f 552 1247 554 +f 1248 1247 542 +f 542 544 1250 +f 1251 1248 516 +f 495 1249 486 +f 516 1248 1249 +f 542 1250 1248 +f 1250 544 541 +f 553 1251 516 +f 1132 1251 690 +f 1013 1252 952 +f 982 1252 983 +f 995 1253 982 +f 34 1143 1253 +f 1261 1254 1088 +f 1255 1254 1152 +f 1152 1029 1255 +f 1255 1029 1256 +f 1260 1029 1257 +f 1076 1611 1100 +f 1078 1256 1258 +f 1259 1260 1610 +f 1258 1256 1259 +f 1260 1259 1256 +f 1611 1172 1100 +f 1029 1260 1256 +f 1260 1257 1610 +f 1069 1152 1261 +f 1254 1261 1152 +f 1126 1266 1128 +f 1262 1612 1614 +f 1112 1264 1668 +f 1119 1667 1264 +f 1149 1263 1265 +f 1266 1265 1263 +f 1264 1667 1668 +f 1128 1266 1263 +f 1126 1267 1266 +f 1268 1126 1205 +f 1267 1268 1271 +f 1406 1404 1405 +f 1126 1268 1267 +f 1268 1405 1271 +f 1216 1390 1397 +f 1669 1270 1112 +f 1405 1268 1199 +f 1270 1669 1670 +f 1274 1163 1176 +f 1274 1275 1272 +f 1275 1274 1164 +f 1273 32 1272 +f 1163 1274 1272 +f 1167 1275 1164 +f 1272 1275 1273 +f 1273 1276 32 +f 1278 1125 1166 +f 1169 1276 1278 +f 1172 1277 1173 +f 1173 1278 1166 +f 1169 1278 1277 +f 1186 1279 1200 +f 1280 1279 1188 +f 1185 1280 1188 +f 1280 1184 1282 +f 1226 1233 1232 +f 1305 1344 1350 +f 1184 1219 1282 +f 1243 1282 1219 +f 1234 1284 1283 +f 1283 1284 1616 +f 1237 1284 1234 +f 1239 1238 1616 +f 1238 1239 1246 +f 1296 1301 1292 +f 1292 1337 1338 +f 1288 1287 1283 +f 1291 1287 1286 +f 1285 1288 1283 +f 1299 1302 1617 +f 1293 1291 1286 +f 1303 1294 1300 +f 1222 1290 1617 +f 1292 1293 1296 +f 1297 1296 1293 +f 1306 1242 1298 +f 1297 1294 1296 +f 1303 1308 1294 +f 27 1228 1304 +f 1324 1619 1618 +f 1304 1303 1240 +f 1242 1307 1620 +f 1297 1293 1286 +f 1313 1306 1301 +f 1286 1299 1297 +f 1290 1300 1299 +f 1289 1240 1300 +f 1242 1306 1308 +f 1302 1299 1286 +f 1300 1290 1289 +f 1294 1297 1300 +f 1299 1300 1297 +f 1313 1296 1294 +f 1341 1291 1293 +f 1287 1302 1286 +f 1617 1288 1222 +f 1298 1301 1306 +f 1289 1304 1240 +f 1344 1347 1350 +f 1294 1308 1306 +f 1300 1240 1303 +f 1318 1321 1316 +f 27 1317 1228 +f 1292 1301 1333 +f 1307 1242 1309 +f 1228 1317 1309 +f 1308 1228 1309 +f 1630 1402 1395 +f 1308 1309 1242 +f 10 1368 1367 +f 1298 1620 1320 +f 1340 1384 1387 +f 1292 1333 1337 +f 1355 1329 1226 +f 1296 1313 1301 +f 1294 1306 1313 +f 1315 1309 1317 +f 1319 1321 1322 +f 1315 1317 1351 +f 1410 1418 1422 +f 1351 1235 1618 +f 1315 1314 1309 +f 1314 1322 1321 +f 1318 1316 1320 +f 1324 1319 1322 +f 1379 1382 1353 +f 1365 1357 1364 +f 1333 1334 1311 +f 1314 1321 1318 +f 1388 1407 1325 +f 1317 27 1351 +f 1314 1315 1322 +f 1419 1336 1345 +f 1381 1325 1400 +f 1374 1295 1375 +f 1324 1373 1374 +f 1388 1319 1326 +f 1375 1358 1380 +f 1324 1374 1396 +f 1319 1389 1326 +f 1377 1327 1393 +f 1391 1393 1394 +f 1388 1326 1407 +f 1323 1671 1384 +f 1362 1621 1355 +f 1368 10 1310 +f 1622 1347 1361 +f 1622 1361 1355 +f 1359 1340 1360 +f 1331 1227 1224 +f 1622 1355 1226 +f 1403 1412 1269 +f 1334 1301 1298 +f 1370 1337 1333 +f 1320 1342 1298 +f 1370 1335 1337 +f 1339 1348 1311 +f 1312 1338 1340 +f 1336 1339 1342 +f 1342 1345 1336 +f 1370 1333 1311 +f 1338 1337 1335 +f 1340 1338 1335 +f 1338 1312 1341 +f 1342 1339 1334 +f 1371 1346 1343 +f 1335 1323 1384 +f 1331 1332 1368 +f 1341 1312 1344 +f 1292 1341 1293 +f 1298 1342 1334 +f 1342 1320 1345 +f 1344 1312 1347 +f 1311 1348 1349 +f 1291 1344 1305 +f 1356 1345 1320 +f 1340 1387 1360 +f 1627 1336 1626 +f 1628 28 1346 +f 1359 1312 1340 +f 1361 1362 1355 +f 1627 1339 1336 +f 1627 1348 1339 +f 1349 1348 1629 +f 1379 1624 1382 +f 1623 1347 1622 +f 1232 1305 1350 +f 1315 1351 1618 +f 1619 1315 1618 +f 1375 1353 1374 +f 1324 1235 1373 +f 1379 1353 1375 +f 1372 1671 1343 +f 1393 1327 1394 +f 1359 1347 1312 +f 1320 1316 1356 +f 1381 1388 1325 +f 1323 1335 1349 +f 1381 1356 1316 +f 1380 1354 1327 +f 1356 1381 1400 +f 1384 1340 1335 +f 1347 1359 1361 +f 1330 1366 1365 +f 1621 1362 1332 +f 1360 1363 1359 +f 1362 1363 1365 +f 1363 1362 1361 +f 1369 1330 1367 +f 1359 1363 1361 +f 1387 1357 1360 +f 1357 1363 1360 +f 1363 1357 1365 +f 1330 1365 1364 +f 1332 1362 1366 +f 1365 1366 1362 +f 1331 1368 1310 +f 1368 1332 1369 +f 1369 1332 1366 +f 1330 1369 1366 +f 1367 1368 1369 +f 1311 1349 1370 +f 1335 1370 1349 +f 1348 1627 1629 +f 1387 1372 1241 +f 1241 1357 1387 +f 1343 1323 1371 +f 1235 1352 1373 +f 1374 1373 1352 +f 1295 1374 1352 +f 1353 1389 1396 +f 1413 1400 1325 +f 1379 1375 1380 +f 1376 1386 1354 +f 1354 1380 1378 +f 1388 1381 1316 +f 1295 1358 1375 +f 1295 1378 1358 +f 1624 1402 1625 +f 1354 1378 1376 +f 1379 1380 1327 +f 1378 1380 1358 +f 1671 1372 1387 +f 1624 1379 1377 +f 1393 1395 1377 +f 1419 1345 1400 +f 1326 1389 1382 +f 1371 1323 1349 +f 1456 1386 1392 +f 1345 1356 1400 +f 1241 1364 1357 +f 1428 1401 1411 +f 1425 1454 1448 +f 1321 1388 1316 +f 1392 16 1452 +f 1327 1377 1379 +f 1394 1354 1386 +f 1396 1319 1324 +f 1389 1353 1382 +f 1206 1216 1397 +f 1412 1310 10 +f 1327 1354 1394 +f 1386 1376 1392 +f 1395 1393 1391 +f 1386 1385 1394 +f 1391 1394 1385 +f 1395 1391 1411 +f 1410 1413 1414 +f 1353 1396 1374 +f 1319 1396 1389 +f 1390 1398 1397 +f 1397 1406 1206 +f 1403 1398 1390 +f 1404 1398 1229 +f 1408 1382 1625 +f 1408 1409 1399 +f 1385 1429 1391 +f 1413 1415 1400 +f 1473 1460 1452 +f 1439 1395 1411 +f 1408 1399 1407 +f 1390 1310 1412 +f 1269 1398 1403 +f 1239 1616 1284 +f 1404 1229 1270 +f 1404 1670 1405 +f 1406 1199 1206 +f 1398 1406 1397 +f 1199 1406 1405 +f 1408 1407 1326 +f 1325 1407 1399 +f 1382 1408 1326 +f 1408 1625 1409 +f 1445 1630 1439 +f 1418 1328 1422 +f 1325 1399 1426 +f 1400 1415 1419 +f 1385 1450 1429 +f 1439 1411 1442 +f 1269 1412 10 +f 1390 1412 1403 +f 1414 1413 1325 +f 1415 1413 1410 +f 1426 1409 1447 +f 1427 1418 1420 +f 1417 1423 1422 +f 1626 1419 1423 +f 1410 1414 1420 +f 1416 1418 1427 +f 1420 1418 1410 +f 1415 1423 1419 +f 1427 1420 1421 +f 1417 1422 1328 +f 1422 1423 1415 +f 1421 1420 1414 +f 1416 1328 1418 +f 1426 1421 1414 +f 1447 1445 1448 +f 1415 1410 1422 +f 1417 28 1423 +f 1626 1423 28 +f 1496 1497 1494 +f 1477 1434 1430 +f 1439 1442 1425 +f 1446 1427 1421 +f 1325 1426 1414 +f 1409 1426 1399 +f 1447 1446 1421 +f 1383 1416 1427 +f 1429 1411 1391 +f 1432 1431 1428 +f 1424 1428 1429 +f 1411 1429 1428 +f 1431 1432 1430 +f 1433 1434 1437 +f 1432 1428 1424 +f 1434 1431 1430 +f 1473 1480 1460 +f 1432 1424 1449 +f 1434 1477 1437 +f 1476 1464 1467 +f 16 1473 1452 +f 1433 1443 1453 +f 1432 1449 1451 +f 1513 1512 1504 +f 1477 1488 1512 +f 1672 15 1475 +f 1437 1477 1512 +f 1536 1520 1568 +f 1475 1514 1672 +f 1442 1411 1401 +f 1439 1425 1445 +f 1444 1442 1443 +f 1427 1446 1383 +f 1458 1462 1471 +f 1432 1455 1430 +f 1443 1401 1453 +f 1443 1442 1401 +f 1444 1443 1467 +f 1475 1476 1474 +f 1444 1465 1454 +f 1468 1446 1469 +f 1444 1454 1425 +f 1402 1445 1409 +f 1421 1426 1447 +f 1469 1454 1468 +f 1445 1447 1409 +f 1447 1448 1469 +f 1445 1425 1448 +f 1442 1444 1425 +f 1450 1449 1424 +f 1480 1463 1479 +f 1429 1450 1424 +f 1450 1385 1452 +f 1451 1449 1459 +f 1462 1455 1451 +f 1456 1452 1385 +f 1489 1484 1458 +f 1431 1453 1401 +f 1433 1453 1434 +f 1451 1455 1432 +f 1383 1446 1468 +f 1430 1455 1470 +f 1386 1456 1385 +f 1392 1452 1456 +f 1496 1500 1492 +f 1438 1471 1472 +f 1495 1478 1493 +f 1460 1449 1450 +f 1480 1459 1460 +f 1449 1460 1459 +f 1450 1452 1460 +f 1472 1471 1457 +f 1462 1451 1457 +f 1451 1459 1457 +f 1470 1455 1482 +f 1440 1465 1467 +f 1479 1461 1472 +f 1465 1444 1467 +f 1464 1440 1467 +f 1466 1465 1440 +f 1433 1474 1443 +f 1465 1466 1454 +f 1454 1466 1468 +f 1383 1468 1466 +f 1447 1469 1446 +f 1454 1469 1448 +f 1482 1462 1483 +f 1483 1462 1458 +f 1462 1457 1471 +f 1457 1479 1472 +f 1491 1487 1481 +f 1472 1486 1487 +f 1475 1474 1433 +f 1473 16 1463 +f 1433 1437 1514 +f 1467 1443 1474 +f 1476 15 1464 +f 1474 1476 1467 +f 15 1476 1475 +f 1430 1470 1477 +f 1477 1470 1488 +f 1483 1484 1478 +f 1505 1504 1495 +f 1480 1479 1457 +f 1463 1461 1479 +f 1459 1480 1457 +f 1463 1480 1473 +f 1462 1482 1455 +f 1472 1461 1486 +f 1488 1482 1478 +f 1484 1483 1458 +f 1482 1483 1478 +f 1438 1489 1458 +f 1492 1493 1496 +f 1493 1478 1484 +f 1438 1487 1636 +f 1461 1481 1486 +f 1487 1486 1481 +f 1515 1538 1502 +f 1471 1438 1458 +f 1488 1470 1482 +f 1481 1441 1491 +f 1635 1510 1503 +f 1472 1487 1438 +f 1488 1495 1504 +f 1520 1536 1528 +f 1514 1475 1433 +f 1484 1632 1497 +f 1493 1497 1496 +f 1491 1490 1636 +f 1497 1632 1494 +f 1495 1493 1492 +f 1506 1509 1501 +f 1495 1492 1505 +f 1478 1495 1488 +f 1497 1493 1484 +f 1500 1496 1502 +f 1489 1632 1484 +f 1636 1489 1438 +f 1503 1500 1502 +f 1506 1510 1509 +f 1499 1529 1519 +f 1521 1537 1531 +f 1502 1496 1494 +f 1503 1538 1539 +f 1492 1506 1505 +f 1518 1501 1511 +f 1494 1519 1515 +f 1494 1515 1502 +f 1510 1633 1509 +f 1517 1509 1436 +f 1501 1505 1506 +f 1505 1501 1504 +f 1506 1492 1500 +f 1436 1511 1517 +f 1516 1635 1503 +f 1506 1500 1510 +f 1522 1507 1523 +f 1485 1513 1518 +f 1516 1508 1634 +f 1509 1633 1436 +f 1513 1631 1512 +f 1498 1436 1633 +f 1503 1510 1500 +f 1515 1532 1538 +f 1435 1631 1485 +f 1488 1504 1512 +f 1518 1504 1501 +f 1485 1631 1513 +f 1514 1512 1631 +f 1437 1512 1514 +f 1519 1494 1499 +f 1542 1528 1544 +f 1542 1515 1507 +f 1541 1697 1516 +f 1511 1501 1517 +f 1509 1517 1501 +f 1485 1518 1511 +f 1504 1518 1513 +f 1499 1632 1636 +f 1515 1519 1523 +f 1522 1528 1507 +f 1522 1523 1526 +f 1527 1524 1526 +f 1535 1534 1521 +f 1543 1571 1572 +f 1565 1575 29 +f 1499 1530 1529 +f 1523 1507 1515 +f 1525 1528 1536 +f 1522 1526 1520 +f 1696 30 1533 +f 1527 1526 1523 +f 1526 1524 1569 +f 1523 1519 1529 +f 1537 1490 1637 +f 1520 1528 1522 +f 1538 1503 1502 +f 1530 1499 1490 +f 1523 1529 1527 +f 1491 1637 1490 +f 1527 1529 1530 +f 1530 1537 1527 +f 1537 1534 1527 +f 1525 1544 1528 +f 1546 1547 1540 +f 1503 1539 1516 +f 1540 1547 1541 +f 1524 1527 1534 +f 1530 1490 1537 +f 1578 1543 1576 +f 1534 1535 1524 +f 1563 30 1548 +f 1637 1491 1441 +f 1521 1534 1537 +f 1539 1538 1532 +f 1540 1539 1532 +f 1696 1541 1547 +f 1532 1542 1544 +f 1540 1544 1546 +f 1516 1539 1541 +f 1540 1541 1539 +f 1528 1542 1507 +f 1532 1515 1542 +f 1643 1553 1656 +f 1600 1594 1607 +f 1525 1560 1546 +f 1544 1540 1532 +f 1560 1562 1561 +f 1556 1585 1557 +f 1525 1546 1544 +f 1547 1546 1545 +f 1548 1547 1545 +f 1547 1548 1696 +f 1551 1555 1550 +f 30 1696 1548 +f 1545 1560 1558 +f 1552 1555 1551 +f 1563 1552 1551 +f 1552 1548 1545 +f 1556 1557 1558 +f 1565 1569 1575 +f 1643 1674 1553 +f 1582 1543 1580 +f 1550 1567 1559 +f 1558 1557 1552 +f 1555 1552 1557 +f 1590 1588 1583 +f 1555 1557 1567 +f 1558 1552 1545 +f 1557 1585 1567 +f 1545 1546 1560 +f 1556 1558 1561 +f 1559 1587 1603 +f 1567 1550 1555 +f 1525 1536 1562 +f 1525 1562 1560 +f 1561 1554 1588 +f 1558 1560 1561 +f 1571 1573 1568 +f 1554 1561 1562 +f 30 1563 1551 +f 1548 1552 1563 +f 1572 1570 1576 +f 29 1572 1565 +f 1581 1554 1562 +f 1568 1573 1584 +f 1565 1571 1568 +f 1600 1602 1549 +f 1586 1574 1585 +f 1569 1568 1520 +f 29 1570 1572 +f 1526 1569 1520 +f 1575 1569 1524 +f 1572 1571 1565 +f 1571 1543 1573 +f 1578 1580 1543 +f 1570 1564 1576 +f 1536 1584 1562 +f 1603 1591 1549 +f 1585 1587 1567 +f 1524 1535 1575 +f 29 1575 1535 +f 1673 1578 1576 +f 1543 1572 1576 +f 1277 1610 1159 +f 1554 1581 1582 +f 1673 1576 1564 +f 1609 1580 1578 +f 1590 1595 1599 +f 1554 1582 1583 +f 1609 1578 1577 +f 1580 1579 1582 +f 1584 1581 1562 +f 1582 1581 1573 +f 1582 1573 1543 +f 1582 1579 1583 +f 1604 1590 1583 +f 1592 1589 1566 +f 1568 1584 1536 +f 1581 1584 1573 +f 1586 1585 1556 +f 1587 1585 1574 +f 1588 1586 1556 +f 1586 1590 1589 +f 1587 1574 1591 +f 1567 1587 1559 +f 1561 1588 1556 +f 1583 1588 1554 +f 1574 1586 1589 +f 1586 1588 1590 +f 1604 1583 1579 +f 1589 1590 1599 +f 1592 1574 1589 +f 1592 1594 1591 +f 1596 1594 1592 +f 1574 1592 1591 +f 1598 1597 1601 +f 1607 1602 1600 +f 1596 1592 1566 +f 1641 1606 1642 +f 1605 1595 1604 +f 1645 1638 1606 +f 1598 1599 1597 +f 1596 1566 1598 +f 1605 1606 1638 +f 1666 1602 1607 +f 1599 1598 1566 +f 1594 1600 1591 +f 1599 1566 1589 +f 1599 1595 1638 +f 1597 1638 1645 +f 1655 1607 1676 +f 1596 1676 1594 +f 1655 1676 1601 +f 1591 1600 1549 +f 1559 1603 1549 +f 1587 1591 1603 +f 1605 1604 1579 +f 1590 1604 1595 +f 1609 1579 1580 +f 1605 1639 1606 +f 1605 1609 1639 +f 1606 1639 1642 +f 1601 1596 1598 +f 1639 1609 1577 +f 1601 1652 1653 +f 1655 1601 1653 +f 1258 1611 1076 +f 1579 1609 1605 +f 1610 1172 1259 +f 1172 1611 1259 +f 1613 1612 1123 +f 1615 1612 1262 +f 1120 1613 1123 +f 1614 1613 1149 +f 1667 1614 1149 +f 1262 1614 1119 +f 1262 31 1615 +f 1615 31 1157 +f 1299 1617 1290 +f 1288 1617 1302 +f 1324 1618 1235 +f 1324 1322 1619 +f 1315 1619 1322 +f 1242 1620 1298 +f 1318 1620 1307 +f 1331 1621 1332 +f 1329 1355 1621 +f 1226 1232 1622 +f 1350 1623 1232 +f 1347 1623 1350 +f 1622 1232 1623 +f 1377 1402 1624 +f 1624 1625 1382 +f 1409 1625 1402 +f 1626 1628 1627 +f 1419 1626 1336 +f 28 1628 1626 +f 1628 1346 1629 +f 1629 1346 1371 +f 1629 1371 1349 +f 1627 1628 1629 +f 1439 1630 1395 +f 1445 1402 1630 +f 1631 1435 1672 +f 1632 1489 1636 +f 1494 1632 1499 +f 1633 1635 1634 +f 1508 1498 1634 +f 1633 1634 1498 +f 1634 1635 1516 +f 1510 1635 1633 +f 1491 1636 1487 +f 1636 1490 1499 +f 1441 1531 1637 +f 1537 1637 1531 +f 1599 1638 1597 +f 1605 1638 1595 +f 1673 1577 1578 +f 1674 1643 1639 +f 1643 1642 1639 +f 1656 1553 1646 +f 1647 1648 1679 +f 1648 1645 1641 +f 1674 1639 1577 +f 1643 1656 1642 +f 1644 1677 1646 +f 1652 1601 1649 +f 1601 1597 1649 +f 1645 1606 1641 +f 1597 1645 1649 +f 1641 1650 1648 +f 1679 1648 1650 +f 1678 1650 1677 +f 1642 1656 1641 +f 1651 1652 1647 +f 1645 1648 1649 +f 1647 1649 1648 +f 1656 1646 1677 +f 1682 1675 1680 +f 1685 1687 1680 +f 1654 1659 1658 +f 1647 1652 1649 +f 1653 1652 1651 +f 1683 1687 1685 +f 1661 1663 1659 +f 1654 1661 1659 +f 1665 1657 1659 +f 1651 1657 1653 +f 1660 1663 1698 +f 1657 1658 1659 +f 1641 1656 1650 +f 1698 1663 1661 +f 1657 1608 1658 +f 1657 1665 1653 +f 1666 1660 1593 +f 1663 1660 1699 +f 3 1698 1662 +f 1698 3 1664 +f 1662 1698 1661 +f 1661 1654 1662 +f 1655 1653 1665 +f 1664 1593 1660 +f 1666 1699 1660 +f 1659 1663 1665 +f 1655 1665 1663 +f 1602 1666 1593 +f 1699 1607 1655 +f 1667 1119 1614 +f 1668 1667 1265 +f 1266 1668 1265 +f 1112 1668 1267 +f 1267 1669 1112 +f 1670 1669 1271 +f 1405 1670 1271 +f 1404 1270 1670 +f 1384 1671 1387 +f 1343 1671 1323 +f 1631 1672 1514 +f 15 1672 1435 +f 1564 1640 1673 +f 1673 1640 1674 +f 1673 1674 1577 +f 1553 1674 1640 +f 1594 1676 1607 +f 1685 1675 1686 +f 1678 1677 1644 +f 1601 1676 1596 +f 1656 1677 1650 +f 1681 4 1682 +f 1679 1680 1647 +f 1650 1678 1679 +f 1680 1679 1678 +f 1680 1678 1684 +f 1675 1685 1680 +f 1657 1683 1608 +f 1644 1681 1684 +f 1682 1680 1684 +f 1651 1683 1657 +f 1644 1684 1678 +f 1682 1684 1681 +f 1651 1647 1687 +f 1687 1683 1651 +f 1683 1686 1608 +f 1685 1686 1683 +f 1687 1647 1680 +f 134 137 1689 +f 129 1689 136 +f 134 1689 1688 +f 295 1690 270 +f 328 1690 271 +f 1692 1691 360 +f 673 1691 523 +f 1692 360 507 +f 523 1692 531 +f 854 1693 852 +f 870 1693 838 +f 1695 1694 1104 +f 1694 1695 1096 +f 1104 1033 1695 +f 1696 1533 1697 +f 1516 1697 1508 +f 1696 1697 1541 +f 1660 1698 1664 +f 1607 1699 1666 +f 1663 1699 1655 diff --git a/src/Mandos/Core/CMakeLists.txt b/src/Mandos/Core/CMakeLists.txt index f2a0ff7f1499a1a054ad27d0086eb0fa1c6067b7..bb28b29b5bbc359c32b03ec8d8a5280deeda9f3f 100644 --- a/src/Mandos/Core/CMakeLists.txt +++ b/src/Mandos/Core/CMakeLists.txt @@ -1,120 +1,141 @@ -set(HEADERS - utility_functions.hpp - SimulationObject.hpp - SimulationObjectHandle.hpp - MechanicalState.hpp - Energies.hpp - KinematicGraph.hpp - Model.hpp - SystemMatrix.hpp - LaggedSystemMatrix.hpp - Simulation.hpp - KinematicGraph.hpp - Colliders.hpp - RotationUtilities.hpp - MechanicalStates/Particle3D.hpp - MechanicalStates/RigidBody.hpp - MechanicalStates/RigidBodyGlobal.hpp - MechanicalStates/RigidBodyCommon.hpp - Energies/StableNeoHookean.hpp - Energies/ARAP.hpp - Energies/LumpedMassInertia.hpp - Energies/GravityEnergy.hpp - Energies/ConstantForce.hpp - Energies/RigidBodyInertia.hpp - Energies/MassSpring.hpp - Energies/CosseratBendingRod.hpp - Energies/CosseratRodAlignment.hpp - Energies/CollisionSpring.hpp - Energies/Friction.hpp - Energies/AirDrag.hpp - Energies/PlaneField.hpp - Projections/FixedProjection.hpp - Mappings/IdentityMapping.hpp - Mappings/BarycentricMapping.hpp - Mappings/RigidBodyPointMapping.hpp - Mappings/RigidBodyGlobalPointMapping.hpp - Mappings/CollisionMapping.hpp - Collisions/SDF.hpp - Collisions/SimulationCollider.hpp - Collisions/SphereCloud.hpp - Collisions/ContactEvent.hpp - Collisions/CollisionDetection.hpp - Collisions/CollisionMesh.cpp - # async_simulation_loop.hpp - Differentiable.hpp - DiffRigidBody.hpp - # edge.hpp - # fem_element.hpp - # gravity.hpp - # inertia_energies.hpp - # integrators.hpp - linear_algebra.hpp - # mesh.hpp - # particle.hpp - # particle_rigid_body_coupling.hpp - # physics_state.hpp - # rigid_body.hpp - # rod_segment.hpp - # simulation.hpp - # sinc.hpp - # spring.hpp - # mandos.hpp +add_library(Core ${HEADERS} ${SOURCES}) + +target_sources( + Core + PRIVATE Model.cpp + Simulation.cpp + SystemMatrix.cpp + LaggedSystemMatrix.cpp + RotationUtilities.cpp + MechanicalStates/Particle3D.cpp + MechanicalStates/RigidBody.cpp + MechanicalStates/RigidBodyGlobal.cpp + MechanicalStates/RigidBodyCommon.cpp + Energies/StableNeoHookean.cpp + Energies/ARAP.cpp + Energies/LumpedMassInertia.cpp + Energies/GravityEnergy.cpp + Energies/ConstantForce.cpp + Energies/RigidBodyInertia.cpp + Energies/MassSpring.cpp + Energies/CosseratBendingRod.cpp + Energies/CosseratRodAlignment.cpp + Energies/CollisionSpring.cpp + Energies/Friction.cpp + Energies/AirDrag.cpp + Energies/PlaneField.cpp + Projections/FixedProjection.cpp + Mappings/IdentityMapping.cpp + Mappings/BarycentricMapping.cpp + Mappings/RigidBodyPointMapping.cpp + Mappings/RigidBodyGlobalPointMapping.cpp + Mappings/CollisionMapping.cpp + Collisions/SDF.cpp + Collisions/SphereCloud.cpp + Collisions/CollisionDetection.cpp + Collisions/CollisionMesh.cpp + # async_simulation_loop.cpp + # compute_tetrahedrons.cpp + Differentiable.cpp + # edge.cpp + # fem_element.cpp + # inertia_energies.cpp + linear_algebra.cpp + # mandos.cpp + # particle_rigidbody_coupling.cpp + # rigid_body.cpp + # rod_segment.cpp + # simulation.cpp + # sinc.cpp + # spring.cpp ) -set(SOURCES - utility_functions.hpp - Model.cpp - Simulation.cpp - SystemMatrix.cpp - LaggedSystemMatrix.cpp - RotationUtilities.cpp - MechanicalStates/Particle3D.cpp - MechanicalStates/RigidBody.cpp - MechanicalStates/RigidBodyGlobal.cpp - MechanicalStates/RigidBodyCommon.cpp - Energies/StableNeoHookean.cpp - Energies/ARAP.cpp - Energies/LumpedMassInertia.cpp - Energies/GravityEnergy.cpp - Energies/ConstantForce.cpp - Energies/RigidBodyInertia.cpp - Energies/MassSpring.cpp - Energies/CosseratBendingRod.cpp - Energies/CosseratRodAlignment.cpp - Energies/CollisionSpring.cpp - Energies/Friction.cpp - Energies/AirDrag.cpp - Energies/PlaneField.cpp - Projections/FixedProjection.cpp - Mappings/IdentityMapping.cpp - Mappings/BarycentricMapping.cpp - Mappings/RigidBodyPointMapping.cpp - Mappings/RigidBodyGlobalPointMapping.cpp - Mappings/CollisionMapping.cpp - Collisions/SDF.cpp - Collisions/SphereCloud.cpp - Collisions/CollisionDetection.cpp - Collisions/CollisionMesh.cpp - # async_simulation_loop.cpp - # compute_tetrahedrons.cpp - Differentiable.cpp - DiffRigidBody.cpp - # edge.cpp - # fem_element.cpp - # inertia_energies.cpp - # integrators.cpp - linear_algebra.cpp - # mandos.cpp - # particle_rigidbody_coupling.cpp - # rigid_body.cpp - # rod_segment.cpp - # simulation.cpp - # sinc.cpp - # spring.cpp +target_sources( + Core + PUBLIC FILE_SET + core_export_headers + TYPE + HEADERS + BASE_DIRS + ${CMAKE_CURRENT_BINARY_DIR}/../.. + FILES + ${CMAKE_CURRENT_BINARY_DIR}/core_export.h ) -add_library(Core ${HEADERS} ${SOURCES}) +target_sources( + Core + PUBLIC FILE_SET + core_public_headers + TYPE + HEADERS + BASE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + FILES + utility_functions.hpp + SimulationObject.hpp + MechanicalState.hpp + Energies.hpp + Model.hpp + Kernel.hpp + SystemMatrix.hpp + LaggedSystemMatrix.hpp + Simulation.hpp + Colliders.hpp + Mesh.hpp + Mappings.hpp + Projections.hpp + RotationUtilities.hpp + MechanicalStates/Particle3D.hpp + MechanicalStates/RigidBody.hpp + MechanicalStates/RigidBodyGlobal.hpp + MechanicalStates/RigidBodyCommon.hpp + Energies/StableNeoHookean.hpp + Energies/ARAP.hpp + Energies/LumpedMassInertia.hpp + Energies/GravityEnergy.hpp + Energies/ConstantForce.hpp + Energies/RigidBodyInertia.hpp + Energies/MassSpring.hpp + Energies/CosseratBendingRod.hpp + Energies/CosseratRodAlignment.hpp + Energies/CollisionSpring.hpp + Energies/Friction.hpp + Energies/AirDrag.hpp + Energies/PlaneField.hpp + Projections/FixedProjection.hpp + Mappings/IdentityMapping.hpp + Mappings/BarycentricMapping.hpp + Mappings/RigidBodyPointMapping.hpp + Mappings/RigidBodyGlobalPointMapping.hpp + Mappings/CollisionMapping.hpp + Collisions/SDF.hpp + Collisions/SimulationCollider.hpp + Collisions/SphereCloud.hpp + Collisions/ContactEvent.hpp + Collisions/CollisionDetection.hpp + Collisions/CollisionMesh.hpp + Collisions/CollisionPair.hpp + # async_simulation_loop.hpp + Differentiable.hpp + DiffRigidBody.hpp + DiffParameters.hpp + # edge.hpp + # fem_element.hpp + # gravity.hpp + # inertia_energies.hpp + # integrators.hpp + linear_algebra.hpp + # mesh.hpp + # particle.hpp + # particle_rigid_body_coupling.hpp + # physics_state.hpp + # rigid_body.hpp + # rod_segment.hpp + # simulation.hpp + # sinc.hpp + # spring.hpp + # mandos.hpp +) # Add the src directory as the include directory. Includes should be added as target_include_directories( @@ -134,8 +155,9 @@ target_link_libraries( embree::embree Eigen3::Eigen TinyAD - PRIVATE Mandos::Tracing - spdlog::spdlog + EnTT::EnTT + Mandos::Tracing + PRIVATE spdlog::spdlog onetbb::onetbb ) @@ -143,4 +165,13 @@ add_library(Mandos::Core ALIAS Core) generate_export_header(Core PREFIX_NAME MANDOS_) install(TARGETS Core LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) +install(TARGETS Core LIBRARY) +install(TARGETS Core FILE_SET core_public_headers) +install(TARGETS Core FILE_SET core_export_headers) + set_target_properties(Core PROPERTIES INSTALL_RPATH "$ORIGIN") + +target_compile_definitions( + Core + PUBLIC ENTT_TYPE_ID=std::uint64_t +) diff --git a/src/Mandos/Core/Colliders.hpp b/src/Mandos/Core/Colliders.hpp index 15cf4e06be77bb6e70cadb1f5a2cb05b38004a26..93e05d12824611443b3a0bd3cd9a487b50a6d203 100644 --- a/src/Mandos/Core/Colliders.hpp +++ b/src/Mandos/Core/Colliders.hpp @@ -17,26 +17,28 @@ namespace mandos::core // List of the possible colliders each SimulationObject<> can handle template struct Colliders { + using type = std::tuple<>; }; template <> struct Colliders { - using types = utilities::typelist; - types::template map::template as m_colliders; + using type = std::tuple, + std::vector>; }; template <> struct Colliders { - using types = utilities::typelist; - types::template map::template as m_colliders; + using type = std::tuple>; }; template <> struct Colliders { - using types = utilities::typelist; - types::template map::template as m_colliders; + using type = std::tuple>; }; +template +using Colliders_t = Colliders::type; + } // namespace mandos::core #endif // MANDOS_CORE_COLLIDERS_H \ No newline at end of file diff --git a/src/Mandos/Core/Collisions/CollisionDetection.hpp b/src/Mandos/Core/Collisions/CollisionDetection.hpp index a7a31e99c530c8557b34b7bc25f1171ed6e6042e..aa3c06096fabc9373cf7fc880e035164ae2283f3 100644 --- a/src/Mandos/Core/Collisions/CollisionDetection.hpp +++ b/src/Mandos/Core/Collisions/CollisionDetection.hpp @@ -3,20 +3,24 @@ #include +#include +#include #include #include -#include +#include #include #include #include -#include #include namespace mandos::core::collisions { +template +class Model; + template void collisions(const C0 &, const C1 &) { diff --git a/src/Mandos/Core/Collisions/CollisionPair.hpp b/src/Mandos/Core/Collisions/CollisionPair.hpp index b10a5c6e6f7a14538d1600cddcc2e8508c6532c3..cb389078cc1dd13eb2296716281b3e136bc7ef93 100644 --- a/src/Mandos/Core/Collisions/CollisionPair.hpp +++ b/src/Mandos/Core/Collisions/CollisionPair.hpp @@ -2,6 +2,7 @@ #define MANDOS_CORE_COLLISIONS_COLLISIONPAIR_H #include +#include #include @@ -13,9 +14,15 @@ namespace mandos::core::collisions SimulationObject holding the collision particles */ -template +template struct CollisionPair { - SimulationObjectHandle collisionParticlesHandle; + using SimulationColliderT0 = SimulationColliderT0T; + using SimulationColliderT1 = SimulationColliderT1T; + using Tag0 = SimulationColliderT0::Tag; + using ColliderT0 = SimulationColliderT0::Collider; // std::remove_cvref_t; + using ToT0 = typename collisions::CollisionParticlesTag::type; + + SimulationObject collisionParticles; SimulationColliderT0 c0SimulationCollider{}; SimulationColliderT1 c1SimulationCollider{}; @@ -25,11 +32,6 @@ struct CollisionPair { Scalar coulombConst{0}; Scalar vMax{1e-2}; Scalar threshold{0}; - - SimulationObject &collisionState() - { - return collisionParticlesHandle.simulationObject(); - } }; } // namespace mandos::core::collisions diff --git a/src/Mandos/Core/Collisions/SimulationCollider.hpp b/src/Mandos/Core/Collisions/SimulationCollider.hpp index 4f72680128698af4f6b0f9d8860a20d55e28678a..50ebc5099fdf541c0d9925f2619cab1cf81517a9 100644 --- a/src/Mandos/Core/Collisions/SimulationCollider.hpp +++ b/src/Mandos/Core/Collisions/SimulationCollider.hpp @@ -1,51 +1,41 @@ #ifndef MANDOS_CORE_COLLISIONS_SIMULATIONCOLLIDER_H #define MANDOS_CORE_COLLISIONS_SIMULATIONCOLLIDER_H -#include +#include +#include namespace mandos::core::collisions { -template +template struct SimulationCollider { + using Tag = TagT; + using Collider = ColliderT; + SimulationCollider() = default; - SimulationCollider(SimulationObjectHandle handle, int colliderIndex) + SimulationCollider(SimulationObject handle, int colliderIndex) : m_handle(handle) , m_colliderIndex(colliderIndex) { } - SimulationObject &simulationObject() - { - return m_handle.simulationObject(); - } - - const SimulationObject &simulationObject() const - { - return m_handle.simulationObject(); - } - - SimulationObjectHandle handle() + SimulationObject handle() const { return m_handle; } - ColliderT &collider() + ColliderT &collider(entt::registry ®istry) { - auto &simObject = simulationObject(); - auto &colliders = std::get>(simObject.colliders()); - return colliders[static_cast(m_colliderIndex)]; + return registry.get>(m_handle.entt)[m_colliderIndex]; } - const ColliderT &collider() const + const ColliderT &collider(const entt::registry ®istry) const { - const auto &simObject = simulationObject(); - const auto &colliders = std::get>(simObject.colliders()); - return colliders[static_cast(m_colliderIndex)]; + return registry.get>(m_handle.entt)[m_colliderIndex]; } private: - SimulationObjectHandle m_handle; + SimulationObject m_handle; int m_colliderIndex{-1}; }; diff --git a/src/Mandos/Core/DiffParameters.hpp b/src/Mandos/Core/DiffParameters.hpp index 66067ed14eaf18ae01c51646edfe6266c43705c1..1f7356bced3931a2ce7651941055377ae8df7c8c 100644 --- a/src/Mandos/Core/DiffParameters.hpp +++ b/src/Mandos/Core/DiffParameters.hpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include namespace mandos::core @@ -30,12 +30,12 @@ struct DiffParameterHandle { } }(); - explicit DiffParameterHandle(SimulationObjectHandle simObj) + explicit DiffParameterHandle(SimulationObject simObj) : m_simObj(simObj) { } - SimulationObjectHandle m_simObj; // + SimulationObject m_simObj; // std::vector m_indices; int size() const diff --git a/src/Mandos/Core/DiffRigidBody.cpp b/src/Mandos/Core/DiffRigidBody.cpp deleted file mode 100644 index 59eff8eabe1188f1ea2abeb524f5c6ead48a5b57..0000000000000000000000000000000000000000 --- a/src/Mandos/Core/DiffRigidBody.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include - -#include -#include - -namespace mandos::core -{ - -void applyGlobalToLocal(const Model &model, Eigen::Ref x) -{ - auto offset = 0; - for (auto node : model.freeSimulationObjects()) { - const auto &simObjectV = model.graph()[node]; - if (const auto *handle = std::get_if>>(&simObjectV)) { - const auto &simObj = handle->simulationObject(); - const auto size = simObj.mstate.size(); - for (auto i = 0; i < static_cast(simObj.mstate.m_x.size()); ++i) { - const Vec3 theta = handle->simulationObject().mstate.m_x[static_cast(i)].segment(3, 3); - const Mat3 &J = computeGlobalToLocalAxisAngleJacobian(theta); - x.segment<3>(offset + (6 * i) + 3) = x.segment<3>(offset + (6 * i) + 3).transpose() * J; - } - offset += size; - } - } -} - -void applyLocalToGlobal(const Model &model, Eigen::Ref x) -{ - auto offset = 0; - for (auto node : model.freeSimulationObjects()) { - const auto &simObjectV = model.graph()[node]; - if (const auto *handle = std::get_if>>(&simObjectV)) { - const auto &simObj = handle->simulationObject(); - const auto size = simObj.mstate.size(); - for (auto i = 0; i < static_cast(simObj.mstate.m_x.size()); ++i) { - const Vec3 theta = handle->simulationObject().mstate.m_x[static_cast(i)].segment(3, 3); - const Mat3 &J = computeLocalToGlobalAxisAngleJacobian(theta); - x.segment<3>(offset + (6 * i) + 3) = x.segment<3>(offset + (6 * i) + 3).transpose() * J; - } - offset += size; - } - } -} - -void applyComposeAxisAngleJacobian(Scalar h, const Model &model, Eigen::Ref v) -{ - unsigned int offset = 0; - for (auto node : model.freeSimulationObjects()) { - const auto &simObjectV = model.graph()[node]; - std::visit( - [&model, &offset, &v, h](const auto &handle) { - const auto &simulationObject = model.simulationObject(handle); - using SimulationObjectT = std::remove_reference_t>; - const auto size = simulationObject.mstate.size(); - if constexpr (std::is_same>()) { - for (unsigned int i = 0; i < simulationObject.mstate.m_x.size(); ++i) { - const Vec3 theta = simulationObject.mstate.m_x[i].template segment<3>(3); - const Vec3 omega = simulationObject.mstate.m_v[i].template segment<3>(3); - const Mat3 J = rotationExpMap(h * omega); - v.segment<3>(offset + (6 * i) + 3) = v.segment<3>(offset + (6 * i) + 3).transpose() * J; - } - } - offset += static_cast(size); - }, - simObjectV); - } -} - -} // namespace mandos::core diff --git a/src/Mandos/Core/DiffRigidBody.hpp b/src/Mandos/Core/DiffRigidBody.hpp index 92b5b2f15445dfc89c0db88c5df3a49844c9d16b..a62feb9187e18341516a435e6b3c670f575033b5 100644 --- a/src/Mandos/Core/DiffRigidBody.hpp +++ b/src/Mandos/Core/DiffRigidBody.hpp @@ -2,6 +2,7 @@ #define MANDOS_CORE_DIFF_RIGID_BODY_H_ #include +#include #include namespace mandos::core @@ -17,7 +18,8 @@ namespace mandos::core * @param model The simulation model * @param x The derivative vector (size = nDof) */ -void applyGlobalToLocal(const Model &model, Eigen::Ref x); +template +void applyGlobalToLocal(const Model &model, Eigen::Ref x); /** * @brief Transform the angular segments of a derivative vector from Local to Global Axis Angle derivatives. @@ -29,9 +31,86 @@ void applyGlobalToLocal(const Model &model, Eigen::Ref x); * @param model The simulation model * @param x The derivative vector (size = nDof) */ -void applyLocalToGlobal(const Model &model, Eigen::Ref x); +template +void applyLocalToGlobal(const Model &model, Eigen::Ref x); -void applyComposeAxisAngleJacobian(Scalar h, const Model &model, Eigen::Ref v); +template +void applyComposeAxisAngleJacobian(Scalar h, const Model &model, Eigen::Ref v); + +template +void applyGlobalToLocal(const Model &model, Eigen::Ref x) +{ + auto offset = 0; + for (auto node : model.freeSimulationObjects()) { + const auto &simObjectV = model.graph()[node]; + std::visit(mandos::core::utilities::overloaded{ + [&model, &x, &offset](SimulationObject handle) { + const auto &mstate = + model.registry().template get>(handle.entt); + const auto size = mstate.size(); + for (auto i = 0; i < static_cast(mstate.m_x.size()); ++i) { + const Vec3 theta = mstate.m_x[static_cast(i)].segment(3, 3); + const Mat3 &J = computeGlobalToLocalAxisAngleJacobian(theta); + x.segment<3>(offset + (6 * i) + 3) = x.segment<3>(offset + (6 * i) + 3).transpose() * J; + } + offset += size; + }, + [](auto handle) {}, + + }, + simObjectV); + } +} + +template +void applyLocalToGlobal(const Model &model, Eigen::Ref x) +{ + auto offset = 0; + for (auto node : model.freeSimulationObjects()) { + const auto &simObjectV = model.graph()[node]; + + std::visit(mandos::core::utilities::overloaded{ + [&model, &offset, &x](SimulationObject handle) { + const auto &mstate = + model.registry().template get>(handle.entt); + const auto size = mstate.size(); + for (auto i = 0; i < static_cast(mstate.m_x.size()); ++i) { + const Vec3 theta = mstate.m_x[static_cast(i)].segment(3, 3); + const Mat3 &J = computeLocalToGlobalAxisAngleJacobian(theta); + x.segment<3>(offset + (6 * i) + 3) = x.segment<3>(offset + (6 * i) + 3).transpose() * J; + } + offset += size; + }, + [](auto handle) {}} + + , + simObjectV); + } +} + +template +void applyComposeAxisAngleJacobian(Scalar h, const Model &model, Eigen::Ref v) +{ + unsigned int offset = 0; + for (auto node : model.freeSimulationObjects()) { + const auto &simObjectV = model.graph()[node]; + std::visit(utilities::overloaded{ + [&model, &offset, &v, h](SimulationObject handle) { + const auto &mstate = + model.registry().template get>(handle.entt); + const auto size = mstate.size(); + for (unsigned int i = 0; i < mstate.m_x.size(); ++i) { + const Vec3 theta = mstate.m_x[i].template segment<3>(3); + const Vec3 omega = mstate.m_v[i].template segment<3>(3); + const Mat3 J = rotationExpMap(h * omega); + v.segment<3>(offset + (6 * i) + 3) = v.segment<3>(offset + (6 * i) + 3).transpose() * J; + } + offset += static_cast(size); + }, + [](auto handle) {}}, + simObjectV); + } +} } // namespace mandos::core diff --git a/src/Mandos/Core/Differentiable.cpp b/src/Mandos/Core/Differentiable.cpp index d75528611ed3d31c1498289f5a54862b08926263..ce4bc22048bd489c6133c0a5d0fbce954df8819a 100644 --- a/src/Mandos/Core/Differentiable.cpp +++ b/src/Mandos/Core/Differentiable.cpp @@ -1,228 +1,5 @@ -#include #include -#include -#include -#include +#include -namespace -{ - -mandos::core::Vec approximateHzFiniteDiff(mandos::core::Model &model, - const mandos::core::Vec &grad, - const mandos::core::Vec &x0, - const mandos::core::Vec &z, - mandos::core::Scalar h, - mandos::core::Scalar dx) -{ - ZoneScopedN("approximateHzFiniteDiff"); - mandos::core::Vec gradient = mandos::core::Vec::Zero(model.nDof()); - mandos::core::Vec x_current = mandos::core::Vec::Zero(model.nDof()); - mandos::core::Vec v_current = mandos::core::Vec::Zero(model.nDof()); - - // Finite diff - model.state(x_current, v_current); - model.updateState(z * dx, x0, h); - model.computeEnergyAndGradient(h, gradient); - - model.setState(x_current, v_current); // Recover state - - return (gradient - grad) / dx; -} - -} // namespace - -namespace mandos::core -{ - -BackpropagationResult computeLossFunctionGradientBackpropagation(Scalar h, - Model &model, - const Trajectory &trajectory, - const LossFunctionAndGradients &loss, - const DiffParameters &diffParameters, - unsigned int maxIterations) -{ - const int nParameters = loss.getNParameters(); - const int nDof = trajectory.getNDof(); - const int nSteps = trajectory.getNSteps(); - - if (static_cast(nParameters) != diffParameters.size()) { - throw std::invalid_argument( - "Size missmatch, parameter sizes are different in the loss function parameter gradient and the input " - "diffParameters"); - } - - // Initialize the loss function gradients dg_dp, dg_dx and dg_dv - // ------------------------------------------------------------------------- - Vec lossGradient = loss.lossParameterPartialDerivative; - Vec lossPositionGradient = loss.lossPositionPartialDerivative[static_cast(nSteps)]; - Vec lossVelocityGradient = loss.lossVelocityPartialDerivative[static_cast(nSteps)]; - // Backward loop - // ------------------------------------------------------------------------- - const Scalar oneOverH = 1.0 / h; - for (int i = nSteps - 1; i >= 0; --i) { - // Set the correc state - const Vec &x = trajectory.positions[static_cast(i) + 1]; - const Vec &v = trajectory.velocities[static_cast(i) + 1]; - const Vec &x0 = trajectory.positions[static_cast(i)]; - const Vec &v0 = trajectory.velocities[static_cast(i)]; - - model.setState(x0, v0); - model.computeAdvection(h); - model.setState(x, v); - - // Compute 2nd order derivatives - SystemMatrix hessian(static_cast(nDof), static_cast(nDof)); - - Vec grad = Vec::Zero(nDof); - model.computeEnergyGradientAndHessian(h, grad, hessian); - - const Vec equation_vector = -(lossPositionGradient + oneOverH * lossVelocityGradient); - - Eigen::ConjugateGradient solver; - solver.compute(hessian); - Vec z = solver.solve(equation_vector); // Adjoint - - // Multiple backward iterations - Vec h_grad = -equation_vector; - Vec dz = Vec::Zero(nDof); - constexpr Scalar dx = 1e-8; - for (unsigned int j = 0; j < maxIterations; ++j) { - const Vec Hz = approximateHzFiniteDiff(model, grad, x0, z, h, dx); - if (Hz.hasNaN()) { - throw std::runtime_error("Backward Step: NaN detected"); - } - h_grad = Hz - equation_vector; - dz = solver.solve(-h_grad); - - z += dz; - } - - // Update the loss function gradients - // ------------------------------------------------------------------------- - applyComposeAxisAngleJacobian(h, model, lossVelocityGradient); - - LaggedSystemMatrix laggedMat(static_cast(nDof), static_cast(nDof)); - model.computeLaggedPositionHessian(h, x, v, x0, v0, laggedMat); - lossPositionGradient = -oneOverH * lossVelocityGradient + laggedMat * z; - - model.computeLaggedVelocityHessian(h, x, v, x0, v0, laggedMat); - lossVelocityGradient = laggedMat * z; - - // lossGradient += z.transpose() * dgradE_dp; - model.computeEnergyGradientParameterDerivative(z, diffParameters, lossGradient); - } - // Add the initial conditions term - // ------------------------------------------------------------------------- - - applyLocalToGlobal(model, lossPositionGradient); - return {.gradient = lossGradient, .dLoss_dx0 = lossPositionGradient, .dLoss_dv0 = lossVelocityGradient}; -} - -/* All three loss vectors here are mutable references. The nolint directive avoids clang tidy to complain that the - * parameter is const when it is not.*/ -void BackwardEngine::backwardStep(Eigen::Ref lossPositionGradient, - Eigen::Ref lossVelocityGradient, - Eigen::Ref lossParameterGradient /*NOLINT*/, - const DiffParameters &diffParameters, - unsigned int maxIterations) -{ - FrameMark; - FrameMarkNamed("Backward"); - ZoneScopedN("BackwardEngine.backwardStep"); - if (static_cast(lossParameterGradient.size()) != diffParameters.size()) { - throw std::invalid_argument( - "Size missmatch, parameter sizes are different in the loss function parameter gradient (" + - std::to_string(lossParameterGradient.size()) + - ") and the input " - "diffParameters (" + - std::to_string(diffParameters.size()) + ")"); - } - - // Decrease the step - if (m_NSteps < --m_step) { - throw std::out_of_range("Trying to go beyond the initial conditions of the problem!"); - return; - } - // Set the correc state - const Vec &x = m_trajectory.positions[static_cast(m_step) + 1]; - const Vec &v = m_trajectory.velocities[static_cast(m_step) + 1]; - const Vec &x0 = m_trajectory.positions[static_cast(m_step)]; - const Vec &v0 = m_trajectory.velocities[static_cast(m_step)]; - - m_model.setState(x0, v0); - m_model.computeAdvection(m_h); - m_model.setState(x, v); - - // Compute 2nd order derivatives - SystemMatrix hessian(static_cast(m_NDof), static_cast(m_NDof)); - - Vec grad = Vec::Zero(m_NDof); - m_model.computeEnergyGradientAndHessian(m_h, grad, hessian); - - const Vec equation_vector = -(lossPositionGradient + m_oneOverH * lossVelocityGradient); - - Eigen::ConjugateGradient solver; - solver.compute(hessian); - auto solve = [&solver](const Vec &vector) { - ZoneScopedN("cgSolve"); - return solver.solve(vector).eval(); - }; - Vec z = solve(equation_vector); // Adjoint - // Multiple backward iterations - Vec h_grad = -equation_vector; - Vec dz = Vec::Zero(m_NDof); - constexpr Scalar dx = 1e-8; - for (unsigned int j = 0; j < maxIterations; ++j) { - ZoneScopedN("backwardIteration"); - const Vec Hz = approximateHzFiniteDiff(m_model, grad, x0, z, m_h, dx); - if (Hz.hasNaN()) { - throw std::runtime_error("ERROR::BackwardStep: NaN detected"); - } - h_grad = Hz - equation_vector; - [[maybe_unused]] const Scalar backwardIterationEnergy = z.transpose() * (0.5 * Hz - equation_vector); - TracyPlot("backwardIterationEnergy", backwardIterationEnergy); - TracyPlot("h_grad.norm()", h_grad.norm()); - dz = solve(-h_grad); - - // z = TinyAD::line_search(z, dz, backwardIterationEnergy, h_grad, computeEnergy); - z += dz; - } - TracyPlot("z.norm", z.norm()); - // Update the loss function gradients - // ------------------------------------------------------------------------- - applyComposeAxisAngleJacobian(m_h, m_model, lossVelocityGradient); - - LaggedSystemMatrix laggedMat(static_cast(m_NDof), static_cast(m_NDof)); - m_model.computeLaggedPositionHessian(m_h, x, v, x0, v0, laggedMat); - lossPositionGradient = -m_oneOverH * lossVelocityGradient + laggedMat * z; - - m_model.computeLaggedVelocityHessian(m_h, x, v, x0, v0, laggedMat); - lossVelocityGradient = laggedMat * z; - - { - ZoneScopedN("computeEnergyGradientParameterDerivative"); - // lossGradient += z.transpose() * dgradE_dp; - m_model.computeEnergyGradientParameterDerivative(z, diffParameters, lossParameterGradient); - } -} - -BackpropagationResult BackwardEngine::backwardSimulation(const LossFunctionAndGradients &loss, - const DiffParameters &diffParameters, - unsigned int maxIterations) -{ - Vec lossPositionGradient = loss.lossPositionPartialDerivative[static_cast(m_NSteps)]; - Vec lossVelocityGradient = loss.lossVelocityPartialDerivative[static_cast(m_NSteps)]; - Vec lossParameterGradient = loss.lossParameterPartialDerivative; - - for (int i = m_NSteps - 1; i >= 0; i--) { - backwardStep(lossPositionGradient, lossVelocityGradient, lossParameterGradient, diffParameters, maxIterations); - // Accumulate loss function partial derivatives - lossPositionGradient += loss.lossPositionPartialDerivative[static_cast(i)]; - lossVelocityGradient += loss.lossVelocityPartialDerivative[static_cast(i)]; - } - applyLocalToGlobal(m_model, lossPositionGradient); - return {.gradient = lossParameterGradient, .dLoss_dx0 = lossPositionGradient, .dLoss_dv0 = lossVelocityGradient}; -} - -} // namespace mandos::core +template class mandos::core::BackwardEngine; \ No newline at end of file diff --git a/src/Mandos/Core/Differentiable.hpp b/src/Mandos/Core/Differentiable.hpp index 66b115230aaa62320db5f471426496d137e810ff..ba82d92d45afae715a3876830c246c400339af68 100644 --- a/src/Mandos/Core/Differentiable.hpp +++ b/src/Mandos/Core/Differentiable.hpp @@ -1,7 +1,10 @@ #ifndef MANDOS_CORE_DIFFERENTIABLE_HPP #define MANDOS_CORE_DIFFERENTIABLE_HPP +#include #include +#include +#include #include namespace mandos::core @@ -42,8 +45,9 @@ struct BackpropagationResult { Vec dLoss_dv0; }; -struct MANDOS_CORE_EXPORT BackwardEngine { - BackwardEngine(Scalar h, Model &model, const Trajectory &trajectory) +template +struct BackwardEngine { + BackwardEngine(Scalar h, Model &model, const Trajectory &trajectory) : m_model(model) , m_trajectory(trajectory) , m_NDof(trajectory.getNDof()) @@ -53,7 +57,7 @@ struct MANDOS_CORE_EXPORT BackwardEngine { , m_step(m_trajectory.getNSteps()) { } - Model &m_model; + Model &m_model; const Trajectory &m_trajectory; const int m_NDof; const int m_NSteps; @@ -77,13 +81,230 @@ struct MANDOS_CORE_EXPORT BackwardEngine { unsigned int maxIterations); }; -MANDOS_CORE_EXPORT BackpropagationResult -computeLossFunctionGradientBackpropagation(Scalar h, - Model &model, - const Trajectory &trajectory, - const LossFunctionAndGradients &loss, +template +BackpropagationResult computeLossFunctionGradientBackpropagation(Scalar h, + Model &model, + const Trajectory &trajectory, + const LossFunctionAndGradients &loss, + const DiffParameters &diffParameters, + unsigned int maxIterations = 0); + +template +mandos::core::Vec approximateHzFiniteDiff(mandos::core::Model &model, + const mandos::core::Vec &grad, + const mandos::core::Vec &x0, + const mandos::core::Vec &z, + mandos::core::Scalar h, + mandos::core::Scalar dx) +{ + ZoneScopedN("approximateHzFiniteDiff"); + mandos::core::Vec gradient = mandos::core::Vec::Zero(model.nDof()); + mandos::core::Vec x_current = mandos::core::Vec::Zero(model.nDof()); + mandos::core::Vec v_current = mandos::core::Vec::Zero(model.nDof()); + + // Finite diff + model.state(x_current, v_current); + model.updateState(z * dx, x0, h); + model.computeEnergyAndGradient(h, gradient); + + model.setState(x_current, v_current); // Recover state + + return (gradient - grad) / dx; +} + +template +BackpropagationResult computeLossFunctionGradientBackpropagation(Scalar h, + Model &model, + const Trajectory &trajectory, + const LossFunctionAndGradients &loss, + const DiffParameters &diffParameters, + unsigned int maxIterations) +{ + const int nParameters = loss.getNParameters(); + const int nDof = trajectory.getNDof(); + const int nSteps = trajectory.getNSteps(); + + if (static_cast(nParameters) != diffParameters.size()) { + throw std::invalid_argument( + "Size missmatch, parameter sizes are different in the loss function parameter gradient and the input " + "diffParameters"); + } + + // Initialize the loss function gradients dg_dp, dg_dx and dg_dv + // ------------------------------------------------------------------------- + Vec lossGradient = loss.lossParameterPartialDerivative; + Vec lossPositionGradient = loss.lossPositionPartialDerivative[static_cast(nSteps)]; + Vec lossVelocityGradient = loss.lossVelocityPartialDerivative[static_cast(nSteps)]; + // Backward loop + // ------------------------------------------------------------------------- + const Scalar oneOverH = 1.0 / h; + for (int i = nSteps - 1; i >= 0; --i) { + // Set the correc state + const Vec &x = trajectory.positions[static_cast(i) + 1]; + const Vec &v = trajectory.velocities[static_cast(i) + 1]; + const Vec &x0 = trajectory.positions[static_cast(i)]; + const Vec &v0 = trajectory.velocities[static_cast(i)]; + + model.setState(x0, v0); + model.computeAdvection(h); + model.setState(x, v); + + // Compute 2nd order derivatives + SystemMatrix hessian(static_cast(nDof), static_cast(nDof)); + + Vec grad = Vec::Zero(nDof); + model.computeEnergyGradientAndHessian(h, grad, hessian); + + const Vec equation_vector = -(lossPositionGradient + oneOverH * lossVelocityGradient); + + Eigen::ConjugateGradient, Eigen::Upper | Eigen::Lower, Eigen::IdentityPreconditioner> + solver; + solver.compute(hessian); + Vec z = solver.solve(equation_vector); // Adjoint + + // Multiple backward iterations + Vec h_grad = -equation_vector; + Vec dz = Vec::Zero(nDof); + constexpr Scalar dx = 1e-8; + for (unsigned int j = 0; j < maxIterations; ++j) { + const Vec Hz = approximateHzFiniteDiff(model, grad, x0, z, h, dx); + if (Hz.hasNaN()) { + throw std::runtime_error("Backward Step: NaN detected"); + } + h_grad = Hz - equation_vector; + dz = solver.solve(-h_grad); + + z += dz; + } + + // Update the loss function gradients + // ------------------------------------------------------------------------- + applyComposeAxisAngleJacobian(h, model, lossVelocityGradient); + + LaggedSystemMatrix laggedMat(static_cast(nDof), static_cast(nDof)); + model.computeLaggedPositionHessian(h, x, v, x0, v0, laggedMat); + lossPositionGradient = -oneOverH * lossVelocityGradient + laggedMat * z; + + model.computeLaggedVelocityHessian(h, x, v, x0, v0, laggedMat); + lossVelocityGradient = laggedMat * z; + + // lossGradient += z.transpose() * dgradE_dp; + model.computeEnergyGradientParameterDerivative(z, diffParameters, lossGradient); + } + // Add the initial conditions term + // ------------------------------------------------------------------------- + + applyLocalToGlobal(model, lossPositionGradient); + return {.gradient = lossGradient, .dLoss_dx0 = lossPositionGradient, .dLoss_dv0 = lossVelocityGradient}; +} + +/* All three loss vectors here are mutable references. The nolint directive avoids clang tidy to complain that the + * parameter is const when it is not.*/ +template +void BackwardEngine::backwardStep(Eigen::Ref lossPositionGradient, + Eigen::Ref lossVelocityGradient, + Eigen::Ref lossParameterGradient /*NOLINT*/, const DiffParameters &diffParameters, - unsigned int maxIterations = 0); + unsigned int maxIterations) +{ + FrameMark; + FrameMarkNamed("Backward"); + ZoneScopedN("BackwardEngine.backwardStep"); + if (static_cast(lossParameterGradient.size()) != diffParameters.size()) { + throw std::invalid_argument( + "Size missmatch, parameter sizes are different in the loss function parameter gradient (" + + std::to_string(lossParameterGradient.size()) + + ") and the input " + "diffParameters (" + + std::to_string(diffParameters.size()) + ")"); + } + + // Decrease the step + if (m_NSteps < --m_step) { + throw std::out_of_range("Trying to go beyond the initial conditions of the problem!"); + return; + } + // Set the correc state + const Vec &x = m_trajectory.positions[static_cast(m_step) + 1]; + const Vec &v = m_trajectory.velocities[static_cast(m_step) + 1]; + const Vec &x0 = m_trajectory.positions[static_cast(m_step)]; + const Vec &v0 = m_trajectory.velocities[static_cast(m_step)]; + + m_model.setState(x0, v0); + m_model.computeAdvection(m_h); + m_model.setState(x, v); + + // Compute 2nd order derivatives + SystemMatrix hessian(static_cast(m_NDof), static_cast(m_NDof)); + + Vec grad = Vec::Zero(m_NDof); + m_model.computeEnergyGradientAndHessian(m_h, grad, hessian); + + const Vec equation_vector = -(lossPositionGradient + m_oneOverH * lossVelocityGradient); + + Eigen::ConjugateGradient, Eigen::Upper | Eigen::Lower, Eigen::IdentityPreconditioner> solver; + solver.compute(hessian); + auto solve = [&solver](const Vec &vector) { + ZoneScopedN("cgSolve"); + return solver.solve(vector).eval(); + }; + Vec z = solve(equation_vector); // Adjoint + // Multiple backward iterations + Vec h_grad = -equation_vector; + Vec dz = Vec::Zero(m_NDof); + constexpr Scalar dx = 1e-8; + for (unsigned int j = 0; j < maxIterations; ++j) { + ZoneScopedN("backwardIteration"); + const Vec Hz = approximateHzFiniteDiff(m_model, grad, x0, z, m_h, dx); + if (Hz.hasNaN()) { + throw std::runtime_error("ERROR::BackwardStep: NaN detected"); + } + h_grad = Hz - equation_vector; + [[maybe_unused]] const Scalar backwardIterationEnergy = z.transpose() * (0.5 * Hz - equation_vector); + TracyPlot("backwardIterationEnergy", backwardIterationEnergy); + TracyPlot("h_grad.norm()", h_grad.norm()); + dz = solve(-h_grad); + + // z = TinyAD::line_search(z, dz, backwardIterationEnergy, h_grad, computeEnergy); + z += dz; + } + TracyPlot("z.norm", z.norm()); + // Update the loss function gradients + // ------------------------------------------------------------------------- + applyComposeAxisAngleJacobian(m_h, m_model, lossVelocityGradient); + + LaggedSystemMatrix laggedMat(static_cast(m_NDof), static_cast(m_NDof)); + m_model.computeLaggedPositionHessian(m_h, x, v, x0, v0, laggedMat); + lossPositionGradient = -m_oneOverH * lossVelocityGradient + laggedMat * z; + + m_model.computeLaggedVelocityHessian(m_h, x, v, x0, v0, laggedMat); + lossVelocityGradient = laggedMat * z; + + { + ZoneScopedN("computeEnergyGradientParameterDerivative"); + // lossGradient += z.transpose() * dgradE_dp; + m_model.computeEnergyGradientParameterDerivative(z, diffParameters, lossParameterGradient); + } +} + +template +BackpropagationResult BackwardEngine::backwardSimulation(const LossFunctionAndGradients &loss, + const DiffParameters &diffParameters, + unsigned int maxIterations) +{ + Vec lossPositionGradient = loss.lossPositionPartialDerivative[static_cast(m_NSteps)]; + Vec lossVelocityGradient = loss.lossVelocityPartialDerivative[static_cast(m_NSteps)]; + Vec lossParameterGradient = loss.lossParameterPartialDerivative; + + for (int i = m_NSteps - 1; i >= 0; i--) { + backwardStep(lossPositionGradient, lossVelocityGradient, lossParameterGradient, diffParameters, maxIterations); + // Accumulate loss function partial derivatives + lossPositionGradient += loss.lossPositionPartialDerivative[static_cast(i)]; + lossVelocityGradient += loss.lossVelocityPartialDerivative[static_cast(i)]; + } + applyLocalToGlobal(m_model, lossPositionGradient); + return {.gradient = lossParameterGradient, .dLoss_dx0 = lossPositionGradient, .dLoss_dv0 = lossVelocityGradient}; +} } // namespace mandos::core diff --git a/src/Mandos/Core/Energies.hpp b/src/Mandos/Core/Energies.hpp index 4bafa1174d7fd803d14c7a9d732e8ca977fb833d..e631532038f0b013b21164e7aa4710589809fd07 100644 --- a/src/Mandos/Core/Energies.hpp +++ b/src/Mandos/Core/Energies.hpp @@ -5,22 +5,22 @@ #include #include +#include #include +#include +#include #include #include #include +#include #include #include #include +#include #include #include -#include -#include -#include -#include - #include namespace mandos::core @@ -38,14 +38,17 @@ namespace mandos::core */ template struct Inertias { + using type = std::tuple<>; }; template struct Potentials { + using type = std::tuple<>; }; template struct DissipativePotentials { + using type = std::tuple<>; }; /** @@ -55,62 +58,63 @@ struct DissipativePotentials { */ template <> struct Inertias { - std::tuple m_inertias; + using type = std::tuple; }; template <> struct Potentials { - std::tuple - m_potentials; + using type = std::tuple; }; template <> struct DissipativePotentials { - std::tuple m_dissipativePotentials; + using type = std::tuple; }; template <> struct Inertias { - std::tuple m_inertias; + using type = std::tuple; }; template <> struct Potentials { - std::tuple - m_potentials; + using type = std::tuple; }; template <> struct DissipativePotentials { - std::tuple m_dissipativePotentials; + using type = std::tuple; }; template <> struct Inertias { - std::tuple m_inertias; + using type = std::tuple; }; template <> struct Potentials { - std::tuple - m_potentials; + using type = std::tuple; }; -template <> -struct DissipativePotentials { - std::tuple<> m_dissipativePotentials; -}; +template +using Inertias_t = Inertias::type; + +template +using Potentials_t = Potentials::type; + +template +using DissipativePotentials_t = DissipativePotentials::type; } // namespace mandos::core diff --git a/src/Mandos/Core/Energies/ARAP.hpp b/src/Mandos/Core/Energies/ARAP.hpp index 4f915b658a38f2fc0476a106c9f830443b428df2..c15ae6a94e238e3396118d50080a815be61773ca 100644 --- a/src/Mandos/Core/Energies/ARAP.hpp +++ b/src/Mandos/Core/Energies/ARAP.hpp @@ -80,7 +80,7 @@ private: std::vector> m_parallelGroups; }; -void initialize(ARAP &snh, const MechanicalState &mstate); +void MANDOS_CORE_EXPORT initialize(ARAP &snh, const MechanicalState &mstate); Scalar MANDOS_CORE_EXPORT computeEnergy(const ARAP &arap, const MechanicalState &mstate); Scalar MANDOS_CORE_EXPORT computeEnergyAndGradient(const ARAP &arap, MechanicalState &mstate); diff --git a/src/Mandos/Core/Energies/AirDrag.hpp b/src/Mandos/Core/Energies/AirDrag.hpp index f3562eba0317314e8845df8a11ed83d1dd5c9159..0b07e147a4ed08c5c1e984e0a67452bd67d941cf 100644 --- a/src/Mandos/Core/Energies/AirDrag.hpp +++ b/src/Mandos/Core/Energies/AirDrag.hpp @@ -45,8 +45,8 @@ private: * @param h Time step size * @return Scalar Total drag energy */ -Scalar computeEnergy(const AirDrag &drag, const MechanicalState &mstate, Scalar h); -Scalar computeEnergy(const AirDrag &drag, const MechanicalState &mstate, Scalar h); +Scalar MANDOS_CORE_EXPORT computeEnergy(const AirDrag &drag, const MechanicalState &mstate, Scalar h); +Scalar MANDOS_CORE_EXPORT computeEnergy(const AirDrag &drag, const MechanicalState &mstate, Scalar h); /** * @brief Computes the air drag energy and corresponding gradient (drag force) @@ -56,8 +56,8 @@ Scalar computeEnergy(const AirDrag &drag, const MechanicalState &m * @param h Time step size * @return Scalar Total drag energy */ -Scalar computeEnergyAndGradient(const AirDrag &drag, MechanicalState &mstate, Scalar h); -Scalar computeEnergyAndGradient(const AirDrag &drag, MechanicalState &mstate, Scalar h); +Scalar MANDOS_CORE_EXPORT computeEnergyAndGradient(const AirDrag &drag, MechanicalState &mstate, Scalar h); +Scalar MANDOS_CORE_EXPORT computeEnergyAndGradient(const AirDrag &drag, MechanicalState &mstate, Scalar h); /** * @brief Computes the air drag energy, gradient (drag force), and Hessian @@ -67,8 +67,8 @@ Scalar computeEnergyAndGradient(const AirDrag &drag, MechanicalState &mstate, Scalar h); -Scalar computeEnergyGradientAndHessian(const AirDrag &drag, MechanicalState &mstate, Scalar h); +Scalar MANDOS_CORE_EXPORT computeEnergyGradientAndHessian(const AirDrag &drag, MechanicalState &mstate, Scalar h); +Scalar MANDOS_CORE_EXPORT computeEnergyGradientAndHessian(const AirDrag &drag, MechanicalState &mstate, Scalar h); } // namespace mandos::core #endif // MANDOS_ENERGIES_AIRDRAG_H_ diff --git a/src/Mandos/Core/Energies/CollisionSpring.hpp b/src/Mandos/Core/Energies/CollisionSpring.hpp index 8209aac24974942465cb0c345c0959dd743d8183..20de2e96d27f45f3952112290538bae1b3c3871d 100644 --- a/src/Mandos/Core/Energies/CollisionSpring.hpp +++ b/src/Mandos/Core/Energies/CollisionSpring.hpp @@ -5,7 +5,7 @@ namespace mandos::core { -class CollisionSpring +class MANDOS_CORE_EXPORT CollisionSpring { public: /** @@ -37,9 +37,10 @@ private: std::vector m_normal; }; -Scalar computeEnergy(const CollisionSpring &cs, const MechanicalState &mstate); -Scalar computeEnergyAndGradient(const CollisionSpring &cs, MechanicalState &mstate); -Scalar computeEnergyGradientAndHessian(const CollisionSpring &cs, MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergy(const CollisionSpring &cs, const MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergyAndGradient(const CollisionSpring &cs, MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergyGradientAndHessian(const CollisionSpring &cs, + MechanicalState &mstate); } // namespace mandos::core diff --git a/src/Mandos/Core/Energies/ConstantForce.hpp b/src/Mandos/Core/Energies/ConstantForce.hpp index 10091b6df9426c157405d2accc97c04b22fa1612..b0f66584e720bb2ee5af8a90b9a182f540d68db8 100644 --- a/src/Mandos/Core/Energies/ConstantForce.hpp +++ b/src/Mandos/Core/Energies/ConstantForce.hpp @@ -77,9 +77,10 @@ private: std::vector m_forceVector; }; -Scalar computeEnergy(const ConstantForce &cf, const MechanicalState &mstate); -Scalar computeEnergyAndGradient(const ConstantForce &cf, MechanicalState &mstate); -Scalar computeEnergyGradientAndHessian(const ConstantForce &cf, MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergy(const ConstantForce &cf, const MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergyAndGradient(const ConstantForce &cf, MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergyGradientAndHessian(const ConstantForce &cf, + MechanicalState &mstate); } // namespace mandos::core diff --git a/src/Mandos/Core/Energies/Friction.hpp b/src/Mandos/Core/Energies/Friction.hpp index 61c22add304610cfe4eb5b4b701f5135e3abb27b..5906101e654079c4b1fefc63c2c7d359de0d7fbf 100644 --- a/src/Mandos/Core/Energies/Friction.hpp +++ b/src/Mandos/Core/Energies/Friction.hpp @@ -3,9 +3,11 @@ #include +#include + namespace mandos::core { -class Friction +class MANDOS_CORE_EXPORT Friction { public: struct MANDOS_CORE_EXPORT ParameterSet { @@ -47,7 +49,9 @@ private: * @param mstate Current state * @return Scalar Sum of the potential energy of all the spring elements */ -Scalar computeEnergy(const Friction &friction, const MechanicalState &mstate, Scalar h); +Scalar MANDOS_CORE_EXPORT computeEnergy(const Friction &friction, + const MechanicalState &mstate, + Scalar h); /** * @brief Computes the potential energy and gradient for the spring elements given the current state of @@ -56,7 +60,9 @@ Scalar computeEnergy(const Friction &friction, const MechanicalState &mstate, Scalar h); +Scalar MANDOS_CORE_EXPORT computeEnergyAndGradient(const Friction &friction, + MechanicalState &mstate, + Scalar h); /** * @brief Computes the potential energy, the gradient and the hessian for the spring elements given the @@ -65,7 +71,9 @@ Scalar computeEnergyAndGradient(const Friction &friction, MechanicalState &mstate, Scalar h); +Scalar MANDOS_CORE_EXPORT computeEnergyGradientAndHessian(const Friction &friction, + MechanicalState &mstate, + Scalar h); } // namespace mandos::core diff --git a/src/Mandos/Core/Energies/GravityEnergy.hpp b/src/Mandos/Core/Energies/GravityEnergy.hpp index ca4974ca7ffac7d43aebf7d2871e80bad167be48..60b77522dba45c97da5046ea80936e7e4c58d0fc 100644 --- a/src/Mandos/Core/Energies/GravityEnergy.hpp +++ b/src/Mandos/Core/Energies/GravityEnergy.hpp @@ -49,17 +49,23 @@ private: std::vector m_vertexMass; }; -Scalar computeEnergy(const GravityEnergy &gravity, const MechanicalState &mstate); -Scalar computeEnergy(const GravityEnergy &gravity, const MechanicalState &mstate); -Scalar computeEnergy(const GravityEnergy &gravity, const MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergy(const GravityEnergy &gravity, const MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergy(const GravityEnergy &gravity, const MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergy(const GravityEnergy &gravity, + const MechanicalState &mstate); -Scalar computeEnergyAndGradient(const GravityEnergy &gravity, MechanicalState &mstate); -Scalar computeEnergyAndGradient(const GravityEnergy &gravity, MechanicalState &mstate); -Scalar computeEnergyAndGradient(const GravityEnergy &gravity, MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergyAndGradient(const GravityEnergy &gravity, + MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergyAndGradient(const GravityEnergy &gravity, MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergyAndGradient(const GravityEnergy &gravity, + MechanicalState &mstate); -Scalar computeEnergyGradientAndHessian(const GravityEnergy &gravity, MechanicalState &mstate); -Scalar computeEnergyGradientAndHessian(const GravityEnergy &gravity, MechanicalState &mstate); -Scalar computeEnergyGradientAndHessian(const GravityEnergy &gravity, MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergyGradientAndHessian(const GravityEnergy &gravity, + MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergyGradientAndHessian(const GravityEnergy &gravity, + MechanicalState &mstate); +Scalar MANDOS_CORE_EXPORT computeEnergyGradientAndHessian(const GravityEnergy &gravity, + MechanicalState &mstate); } // namespace mandos::core #endif // MANDOS_ENERGIES_GRAVITYENERGY_H diff --git a/src/Mandos/Core/Energies/PlaneField.hpp b/src/Mandos/Core/Energies/PlaneField.hpp index e6b029a3bea53aedb8618a5b2b49ecbb911af846..ba40d81d1b40ae05a1239c28ae26df4d4c17fbe0 100644 --- a/src/Mandos/Core/Energies/PlaneField.hpp +++ b/src/Mandos/Core/Energies/PlaneField.hpp @@ -61,7 +61,7 @@ private: * @param h Time step (not used in energy itself but may relate to time-dependent logic) * @return Scalar Contact potential energy (zero if no penetration) */ -Scalar computeEnergy(const PlaneField &pf, const MechanicalState &mstate, Scalar h); +Scalar MANDOS_CORE_EXPORT computeEnergy(const PlaneField &pf, const MechanicalState &mstate, Scalar h); /** * @brief Computes contact potential energy and its gradient (force) with respect to the rigid body state @@ -70,7 +70,9 @@ Scalar computeEnergy(const PlaneField &pf, const MechanicalState & * @param h Time step * @return Scalar Contact potential energy */ -Scalar computeEnergyAndGradient(const PlaneField &pf, MechanicalState &mstate, Scalar h); +Scalar MANDOS_CORE_EXPORT computeEnergyAndGradient(const PlaneField &pf, + MechanicalState &mstate, + Scalar h); /** * @brief Computes energy, gradient (force), and Hessian (stiffness matrix) for the plane contact @@ -79,7 +81,9 @@ Scalar computeEnergyAndGradient(const PlaneField &pf, MechanicalState &mstate, Scalar h); +Scalar MANDOS_CORE_EXPORT computeEnergyGradientAndHessian(const PlaneField &pf, + MechanicalState &mstate, + Scalar h); } // namespace mandos::core diff --git a/src/Mandos/Core/Energies/RigidBodyInertia.hpp b/src/Mandos/Core/Energies/RigidBodyInertia.hpp index c7c0269f40325c97b0822c4ec2fa03cd29818520..a1f5dd1b478021c4b4efc232082049af6c86eee9 100644 --- a/src/Mandos/Core/Energies/RigidBodyInertia.hpp +++ b/src/Mandos/Core/Energies/RigidBodyInertia.hpp @@ -20,7 +20,7 @@ namespace mandos::core * * The mass and the inertia tensor are not initialized and should be provided directly by the user */ -class RigidBodyInertia +class MANDOS_CORE_EXPORT RigidBodyInertia { public: /** @@ -30,8 +30,8 @@ public: * @param h Time span for moving from x to x_advect * @return Scalar */ - MANDOS_CORE_EXPORT Scalar computeEnergy(const MechanicalState &mstate, Scalar h) const; - MANDOS_CORE_EXPORT Scalar computeEnergy(const MechanicalState &mstate, Scalar h) const; + Scalar computeEnergy(const MechanicalState &mstate, Scalar h) const; + Scalar computeEnergy(const MechanicalState &mstate, Scalar h) const; /** * @brief Computes the inertia energy and its gradient @@ -50,9 +50,8 @@ public: * @param h Time span for moving from x to x_advect * @return Scalar */ - MANDOS_CORE_EXPORT Scalar computeEnergyGradientAndHessian(MechanicalState &mstate, Scalar h) const; - MANDOS_CORE_EXPORT Scalar computeEnergyGradientAndHessian(MechanicalState &mstate, - Scalar h) const; + Scalar computeEnergyGradientAndHessian(MechanicalState &mstate, Scalar h) const; + Scalar computeEnergyGradientAndHessian(MechanicalState &mstate, Scalar h) const; void computeLaggedPositionHessian(MechanicalState &mstate, Scalar h) const; void computeLaggedVelocityHessian(MechanicalState &mstate, Scalar h) const; @@ -65,32 +64,32 @@ public: * * @return std::vector& */ - MANDOS_CORE_EXPORT std::vector &mass(); + std::vector &mass(); /** * @brief Read accessor to rigid body mass * * @return const std::vector& */ - MANDOS_CORE_EXPORT const std::vector &mass() const; + const std::vector &mass() const; - /** - * @brief Read/Write accessor to vertex inertia tensor - * - * @return std::vector& - */ - MANDOS_CORE_EXPORT std::vector &inertiaTensor(); + /** + * @brief Read/Write accessor to vertex inertia tensor + * + * @return std::vector& + */ + std::vector &inertiaTensor(); - /** - * @brief Read accessor to vertex inertia tensor - * - * @return const std::vector& - */ - MANDOS_CORE_EXPORT const std::vector &inertiaTensor() const; + /** + * @brief Read accessor to vertex inertia tensor + * + * @return const std::vector& + */ + const std::vector &inertiaTensor() const; -private: - std::vector m_mass; - std::vector m_inertiaTensor0; + private: + std::vector m_mass; + std::vector m_inertiaTensor0; }; } // namespace mandos::core diff --git a/src/Mandos/Core/Energies/StableNeoHookean.hpp b/src/Mandos/Core/Energies/StableNeoHookean.hpp index b93f2f89031508c399c260308f81dec5f592ac40..461c06c7532eb4a2c8982b660ac98353b269e28e 100644 --- a/src/Mandos/Core/Energies/StableNeoHookean.hpp +++ b/src/Mandos/Core/Energies/StableNeoHookean.hpp @@ -85,7 +85,7 @@ private: std::vector> m_parallelGroups; }; -void initialize(StableNeoHookean &snh, const MechanicalState &mstate); +void MANDOS_CORE_EXPORT initialize(StableNeoHookean &snh, const MechanicalState &mstate); Scalar MANDOS_CORE_EXPORT computeEnergy(const StableNeoHookean &snh, const MechanicalState &mstate); Scalar MANDOS_CORE_EXPORT computeEnergyAndGradient(const StableNeoHookean &snh, MechanicalState &mstate); diff --git a/src/Mandos/Core/Kernel.hpp b/src/Mandos/Core/Kernel.hpp new file mode 100644 index 0000000000000000000000000000000000000000..301aef21b47bbc79195048788ef86cea673d7dcb --- /dev/null +++ b/src/Mandos/Core/Kernel.hpp @@ -0,0 +1,84 @@ +#ifndef MANDOS_CORE_KERNEL_HPP +#define MANDOS_CORE_KERNEL_HPP + +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace mandos::core +{ + +struct Kernel { + using Tags = utilities::typelist; + + using SimulationObjects = Tags::map::as; + + using KinematicGraph = boost::adjacency_list::as>; + + using CollisionPairs = std::tuple, + collisions::SimulationCollider>>, + std::vector, + collisions::SimulationCollider>>>; + + template + using Projections_t = mandos::core::Projections_t; + + template + using Potentials_t = mandos::core::Potentials_t; + + template + using Inertias_t = mandos::core::Inertias_t; + + template + using DissipativePotentials_t = mandos::core::DissipativePotentials_t; + + template + using Colliders_t = mandos::core::Colliders_t; + + template + using Mappings_t = mandos::core::Mappings_t; + + template + static void populateSimulationObject(SimulationObject handle, entt::registry ®istry) + { + // Inertias + utilities::static_for_each>([&]() { registry.emplace(handle.entt); }); + utilities::static_for_each>([&]() { registry.emplace(handle.entt); }); + utilities::static_for_each>( + [&]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>([&]() { registry.emplace(handle.entt); }); + utilities::static_for_each>([&]() { registry.emplace(handle.entt); }); + utilities::static_for_each>([&]() { registry.emplace(handle.entt); }); + } +}; + +} // namespace mandos::core + +#endif // MANDOS_CORE_KERNEL_HPP \ No newline at end of file diff --git a/src/Mandos/Core/KinematicGraph.hpp b/src/Mandos/Core/KinematicGraph.hpp deleted file mode 100644 index 8672b65bd7a9d434fa84b2da981a86a26738f891..0000000000000000000000000000000000000000 --- a/src/Mandos/Core/KinematicGraph.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef MANDOS_CORE_KINEMATICGRAPH_HPP -#define MANDOS_CORE_KINEMATICGRAPH_HPP - -#include - -#include -#include -#include -#include -#include - -#include - -namespace mandos::core -{ -using SimulationObjects = utilities::typelist, // - SimulationObject, - SimulationObject>; - -using KinematicGraph = boost::adjacency_list::as>; -} // namespace mandos::core - -#endif // MANDOS_CORE_KINEMATICGRAPH_HPP diff --git a/src/Mandos/Core/LaggedSystemMatrix.cpp b/src/Mandos/Core/LaggedSystemMatrix.cpp index afcc712846886122cd7b73207c07fca6c8b69646..e805196b7ff7713cf0c1d36ce8fbed7457b891c9 100644 --- a/src/Mandos/Core/LaggedSystemMatrix.cpp +++ b/src/Mandos/Core/LaggedSystemMatrix.cpp @@ -1,74 +1,5 @@ #include -namespace mandos::core -{ +#include -LaggedSystemMatrix::LaggedSystemMatrix(int rows, int cols) - : m_rows(rows) - , m_cols(cols) -{ -} - -int LaggedSystemMatrix::rows() const -{ - return m_rows; -} - -int LaggedSystemMatrix::cols() const -{ - return m_cols; -} - -void LaggedSystemMatrix::clear() -{ -} - -void LaggedSystemMatrix::setForwardSortedList( - const std::vector &forwardSortedList) -{ - m_forwardSortedList = std::addressof(forwardSortedList); -} - -const std::vector &LaggedSystemMatrix::forwardSortedList() const -{ - return *m_forwardSortedList; -} - -void LaggedSystemMatrix::setBackwardSortedList( - const std::vector &backwardSortedList) -{ - m_backwardSortedList = std::addressof(backwardSortedList); -} - -const std::vector &LaggedSystemMatrix::backwardSortedList() const -{ - return *m_backwardSortedList; -} - -void LaggedSystemMatrix::setFreeSimulationObject( - const std::vector &freeSimulationObjects) -{ - m_freeSimulationObjects = std::addressof(freeSimulationObjects); -} - -const std::vector &LaggedSystemMatrix::freeSimulationObjects() const -{ - return *m_freeSimulationObjects; -} - -const Model::KinematicGraph &LaggedSystemMatrix::graph() const -{ - return m_model->graph(); -} - -Model &LaggedSystemMatrix::model() const -{ - return *m_model; -} - -void LaggedSystemMatrix::setModel(Model &model) -{ - m_model = std::addressof(model); -} - -} // namespace mandos::core +template class mandos::core::LaggedSystemMatrix; \ No newline at end of file diff --git a/src/Mandos/Core/LaggedSystemMatrix.hpp b/src/Mandos/Core/LaggedSystemMatrix.hpp index 4fbba3013e8f6d9ed68ad1405b0ef24020872cea..2e8c45d5aadce8f3ce3505edc69c31cb1cf702b7 100644 --- a/src/Mandos/Core/LaggedSystemMatrix.hpp +++ b/src/Mandos/Core/LaggedSystemMatrix.hpp @@ -1,28 +1,32 @@ #ifndef MANDOS_LAGGED_SYSTEMMATRIX_H #define MANDOS_LAGGED_SYSTEMMATRIX_H -#include - -#include -#include -#include +#include #include -#include #include #include +#include +#include + +#include + namespace mandos::core { +template struct LaggedSystemMatrix; -} + +template +class Model; +} // namespace mandos::core namespace Eigen::internal { // MatrixReplacement looks-like a SparseMatrix, so let's inherits its traits: -template <> -struct traits : public traits { +template +struct traits> : public traits { }; } // namespace Eigen::internal @@ -41,7 +45,8 @@ namespace mandos::core * */ -struct LaggedSystemMatrix : public Eigen::EigenBase { +template +struct LaggedSystemMatrix : public Eigen::EigenBase> { public: using Scalar = mandos::core::Scalar; using RealScalar = mandos::core::Scalar; @@ -60,98 +65,174 @@ public: void clear(); template - Eigen::Product operator*(const Eigen::MatrixBase &x) const + Eigen::Product, Rhs, Eigen::AliasFreeProduct> operator*( + const Eigen::MatrixBase &x) const { - return ::Eigen::Product(*this, x.derived()); + return ::Eigen::Product, Rhs, Eigen::AliasFreeProduct>(*this, x.derived()); } - void setModel(Model &model); - Model &model() const; - const Model::KinematicGraph &graph() const; + void setModel(Model &model); + Model &model() const; - void setFreeSimulationObject(const std::vector &freeSimulationObjects); - const std::vector &freeSimulationObjects() const; + void setFreeSimulationObject( + const std::vector &freeSimulationObjects); + const std::vector &freeSimulationObjects() const; - void setForwardSortedList(const std::vector &forwardSortedList); - const std::vector &forwardSortedList() const; + void setForwardSortedList( + const std::vector &forwardSortedList); + const std::vector &forwardSortedList() const; - void setBackwardSortedList(const std::vector &backwardSortedList); - const std::vector &backwardSortedList() const; + void setBackwardSortedList( + const std::vector &backwardSortedList); + const std::vector &backwardSortedList() const; - const Vec *m_x{nullptr}; - const Vec *m_v{nullptr}; - const Vec *m_x0{nullptr}; - const Vec *m_v0{nullptr}; + Eigen::Map m_x{nullptr, 0}; + Eigen::Map m_v{nullptr, 0}; + Eigen::Map m_x0{nullptr, 0}; + Eigen::Map m_v0{nullptr, 0}; private: int m_rows; int m_cols; - Model *m_model{nullptr}; - const std::vector *m_freeSimulationObjects{nullptr}; - const std::vector *m_forwardSortedList{nullptr}; - const std::vector *m_backwardSortedList{nullptr}; + Model *m_model{nullptr}; + const std::vector *m_freeSimulationObjects{nullptr}; + const std::vector *m_forwardSortedList{nullptr}; + const std::vector *m_backwardSortedList{nullptr}; }; +template +LaggedSystemMatrix::LaggedSystemMatrix(int rows, int cols) + : m_rows(rows) + , m_cols(cols) +{ +} + +template +int LaggedSystemMatrix::rows() const +{ + return m_rows; +} + +template +int LaggedSystemMatrix::cols() const +{ + return m_cols; +} + +template +void LaggedSystemMatrix::clear() +{ +} + +template +void LaggedSystemMatrix::setForwardSortedList( + const std::vector &forwardSortedList) +{ + m_forwardSortedList = std::addressof(forwardSortedList); +} + +template +const std::vector &LaggedSystemMatrix::forwardSortedList() + const +{ + return *m_forwardSortedList; +} + +template +void LaggedSystemMatrix::setBackwardSortedList( + const std::vector &backwardSortedList) +{ + m_backwardSortedList = std::addressof(backwardSortedList); +} + +template +const std::vector & +LaggedSystemMatrix::backwardSortedList() const +{ + return *m_backwardSortedList; +} + +template +void LaggedSystemMatrix::setFreeSimulationObject( + const std::vector &freeSimulationObjects) +{ + m_freeSimulationObjects = std::addressof(freeSimulationObjects); +} + +template +const std::vector & +LaggedSystemMatrix::freeSimulationObjects() const +{ + return *m_freeSimulationObjects; +} + +template +Model &LaggedSystemMatrix::model() const +{ + return *m_model; +} + +template +void LaggedSystemMatrix::setModel(Model &model) +{ + m_model = std::addressof(model); +} + } // namespace mandos::core // Implementation of MatrixReplacement * Eigen::DenseVector though a specialization of internal::generic_product_impl: namespace Eigen::internal { -template -struct generic_product_impl +struct generic_product_impl, Rhs, SparseShape, DenseShape, GemvProduct> // GEMV stands for // matrix-vector - : generic_product_impl_base, Rhs, - generic_product_impl> { - typedef typename Product::Scalar Scalar; + generic_product_impl, Rhs>> { + typedef typename Product, Rhs>::Scalar Scalar; template static void scaleAndAddTo(Dest &dst, - const mandos::core::LaggedSystemMatrix &lhs, + const mandos::core::LaggedSystemMatrix &lhs, const Rhs &rhs, const Scalar &alpha) { ZoneScopedN("H*dx"); + auto ®istry = lhs.model().registry(); // This method should implement "dst += alpha * lhs * rhs" inplace, // however, for iterative solvers, alpha is always equal to 1, so let's not bother about it. assert(alpha == Scalar(1) && "scaling is not implemented"); EIGEN_ONLY_USED_FOR_DEBUG(alpha); // Clear the gradient so we can store there the dx - auto a = lhs.forwardSortedList(); for (auto node : lhs.forwardSortedList()) { std::visit( - [&lhs](auto handle) { - auto &simulationObject = lhs.model().simulationObject(handle); - simulationObject.mstate.clearGradient(); + [®istry](mandos::core::SimulationObject handle) { + registry.template get>(handle.entt).clearGradient(); }, - lhs.graph()[node]); + lhs.model().graph()[node]); } // Set the dx for the free SimulationObjects, applying projections if needed int offset = 0; for (auto node : lhs.freeSimulationObjects()) { - const auto &simObjectV = lhs.graph()[node]; + const auto &simObjectV = lhs.model().graph()[node]; std::visit( - [&lhs, &offset, &rhs](auto handle) { - auto &simulationObject = lhs.model().simulationObject(handle); - const auto size = simulationObject.mstate.size(); - simulationObject.mstate.setGradient(rhs.segment(offset, size)); - - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasProjections) { - mandos::core::utilities::static_for_each( - [&simulationObject](const auto &projection) { - projection.applyP(simulationObject.mstate.gradientView()); - }, - simulationObject.projections); - } + [®istry, &offset, &rhs](mandos::core::SimulationObject handle) { + auto &mstate = registry.template get>(handle.entt); + const auto size = mstate.size(); + mstate.setGradient(rhs.segment(offset, size)); + + mandos::core::utilities::static_for_each>( + [®istry, &mstate, handle]() { + registry.template get(handle.entt).applyP(mstate.gradientView()); + }); offset += size; }, @@ -160,23 +241,23 @@ struct generic_product_impl>; - if constexpr (SimulationObjectT::hasMappings) { - mandos::core::utilities::static_for_each( - [&lhs, &simulationObject](const auto &mappings) { - for (const auto &mapping : mappings) { - auto to = mapping.to(); - - mapping.applyJ(simulationObject.mstate.m_grad, - lhs.model().simulationObject(to).mstate.m_grad); - } - }, - simulationObject.m_mappings); - } + [®istry](mandos::core::SimulationObject handle) { + const auto &mstate = registry.template get>(handle.entt); + mandos::core::utilities::static_for_each>( + [®istry, &mstate, handle]() { + using Mapping = MappingsVec::value_type; + const auto &mappings = registry.template get>(handle.entt); + for (const auto &mapping : mappings) { + auto to = mapping.to(); + + auto toMstate = + registry.template get>(to.entt); + + mapping.applyJ(mstate.m_grad, toMstate.m_grad); + } + }); }, simObjectV); } @@ -184,63 +265,61 @@ struct generic_product_impl(mandos::core::SimulationObject handle) { + registry.template get>(handle.entt).scaleGradByHessian(); }, - lhs.graph()[node]); + lhs.model().graph()[node]); } // Set the lagged state - lhs.model().setState(*lhs.m_x0, *lhs.m_v0); + lhs.model().setState(lhs.m_x0, lhs.m_v0); // And propagate back the H*dx through mappings for (auto node : lhs.backwardSortedList()) { - const auto &simObjectV = lhs.graph()[node]; + const auto &simObjectV = lhs.model().graph()[node]; std::visit( - [&lhs](auto handle) { - const auto &simulationObject = lhs.model().simulationObject(handle); - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasMappings) { - mandos::core::utilities::static_for_each( - [&lhs](const auto &mappings) { - for (const auto &mapping : mappings) { - auto fromHandle = mapping.from(); - auto toHandle = mapping.to(); - - auto &from = lhs.model().simulationObject(fromHandle); - auto &to = lhs.model().simulationObject(toHandle); - - mapping.applyJT(from.mstate.m_grad, to.mstate.m_grad); - } - }, - simulationObject.m_mappings); - } + + [®istry](mandos::core::SimulationObject handle) { + const auto &mstate = registry.template get>(handle.entt); + mandos::core::utilities::static_for_each>( + [®istry, &mstate, handle]() { + using Mapping = MappingsVec::value_type; + const auto &mappings = registry.template get>(handle.entt); + for (const auto &mapping : mappings) { + auto fromHandle = mapping.from(); + auto toHandle = mapping.to(); + + auto &fromMstate = + registry.template get>( + fromHandle.entt); + auto &toMstate = + registry.template get>( + toHandle.entt); + + mapping.applyJT(fromMstate.m_grad, toMstate.m_grad); + } + }); }, simObjectV); } // Reset to the proper state - lhs.model().setState(*lhs.m_x, *lhs.m_v); + lhs.model().setState(lhs.m_x, lhs.m_v); // And finally, accumulate on the dst, taking into account the projections if needed offset = 0; for (auto node : lhs.freeSimulationObjects()) { - const auto &simObjectV = lhs.graph()[node]; + const auto &simObjectV = lhs.model().graph()[node]; std::visit( - [&lhs, &dst, &offset](auto handle) { - auto &simulationObject = lhs.model().simulationObject(handle); - const auto size = simulationObject.mstate.size(); - - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasProjections) { - mandos::core::utilities::static_for_each( - [&simulationObject](const auto &projection) { - projection.applyPT(simulationObject.mstate.gradientView()); - }, - simulationObject.projections); - } - simulationObject.mstate.gradient(dst.segment(offset, size)); + [®istry, &dst, &offset](mandos::core::SimulationObject handle) { + auto &mstate = registry.template get>(handle.entt); + const auto size = mstate.size(); + + mandos::core::utilities::static_for_each>( + [®istry, &mstate, handle]() { + registry.template get(handle.entt).applyPT(mstate.gradientView()); + }); + mstate.gradient(dst.segment(offset, size)); offset += size; }, simObjectV); diff --git a/src/Mandos/Core/Mappings.hpp b/src/Mandos/Core/Mappings.hpp index 43110d8f6205972ee5520e649e087e7663748952..c86afe48d6c93b4429b1b9f516799e64867769a3 100644 --- a/src/Mandos/Core/Mappings.hpp +++ b/src/Mandos/Core/Mappings.hpp @@ -1,12 +1,6 @@ #ifndef MANDOS_MAPPINGS_H_ #define MANDOS_MAPPINGS_H_ -// #include -// #include -// #include -// #include -// #include - #include #include @@ -16,11 +10,13 @@ #include #include #include +#include +#include +#include #include #include -#include -#include + #include namespace mandos::core @@ -34,6 +30,7 @@ namespace mandos::core */ template struct Mappings { + using type = std::tuple<>; }; /** @@ -43,31 +40,29 @@ struct Mappings { */ template <> struct Mappings { - static constexpr bool HasMappings = true; - using types = - utilities::typelist, - mandos::core::collisions::CollisionMapping>; - types::template map::template as m_mappings; + using type = std::tuple< + std::vector, + std::vector, + std::vector>, + std::vector< + mandos::core::collisions::CollisionMapping>>; }; template <> struct Mappings { - static constexpr bool HasMappings = true; - using types = - utilities::typelist>; - types::template map::template as m_mappings; + using type = std::tuple< + std::vector, + std::vector>>; }; template <> struct Mappings { - static constexpr bool HasMappings = true; - using types = utilities::typelist; - types::template map::template as m_mappings; + using type = std::tuple>; }; +template +using Mappings_t = Mappings::type; + } // namespace mandos::core #endif // MANDOS_MAPPINGS_H diff --git a/src/Mandos/Core/Mappings/BarycentricMapping.cpp b/src/Mandos/Core/Mappings/BarycentricMapping.cpp index c6f444dbcc883853546f996c4de16cd1878e894d..ccf2b8f465a74944dcb5b8b23b480c07f60be4ff 100644 --- a/src/Mandos/Core/Mappings/BarycentricMapping.cpp +++ b/src/Mandos/Core/Mappings/BarycentricMapping.cpp @@ -3,8 +3,7 @@ namespace mandos::core { -BarycentricMapping::BarycentricMapping(SimulationObjectHandle from, - SimulationObjectHandle to) +BarycentricMapping::BarycentricMapping(SimulationObject from, SimulationObject to) : m_from(from) , m_to(to) { @@ -28,12 +27,12 @@ void BarycentricMapping::apply(const std::vector &from, std::vector m_J * Vec::ConstMapType(from.data()->data(), static_cast(3 * from.size())); } -SimulationObjectHandle BarycentricMapping::to() const +SimulationObject BarycentricMapping::to() const { return m_to; } -SimulationObjectHandle BarycentricMapping::from() const +SimulationObject BarycentricMapping::from() const { return m_from; } diff --git a/src/Mandos/Core/Mappings/BarycentricMapping.hpp b/src/Mandos/Core/Mappings/BarycentricMapping.hpp index c7014227c0c64a003254efc9add6a100a871c1af..b0da11b32c2b4815f078070fd44216bb51a9bb77 100644 --- a/src/Mandos/Core/Mappings/BarycentricMapping.hpp +++ b/src/Mandos/Core/Mappings/BarycentricMapping.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include #include @@ -12,23 +12,20 @@ namespace mandos::core { -template -struct SimulationObjectHandle; - class MANDOS_CORE_EXPORT BarycentricMapping { public: - using From = SimulationObjectHandle; - using To = SimulationObjectHandle; + using From = Particle3DTag; + using To = Particle3DTag; - BarycentricMapping(SimulationObjectHandle from, SimulationObjectHandle to); + BarycentricMapping(SimulationObject from, SimulationObject to); void apply(const std::vector &from, std::vector &to) const; void applyJ(const std::vector &from, std::vector &to) const; void applyJT(std::vector &from, const std::vector &to) const; - SimulationObjectHandle from() const; - SimulationObjectHandle to() const; + SimulationObject from() const; + SimulationObject to() const; void setJ(const SparseMat &j); @@ -37,8 +34,8 @@ public: private: SparseMat m_J; - SimulationObjectHandle m_from; - SimulationObjectHandle m_to; + SimulationObject m_from; + SimulationObject m_to; }; } // namespace mandos::core diff --git a/src/Mandos/Core/Mappings/CollisionMapping.cpp b/src/Mandos/Core/Mappings/CollisionMapping.cpp index 384d5ac099046ea01449ae9559f6014546a35042..4639bdd2f5a464e0518d39cd758c1118bad39664 100644 --- a/src/Mandos/Core/Mappings/CollisionMapping.cpp +++ b/src/Mandos/Core/Mappings/CollisionMapping.cpp @@ -28,12 +28,12 @@ void updateMapping(const collisions::ContactEventSide &contact, { ZoneScopedN("updateMapping"); // Compute the current RB transform - const auto &mstate = mapping.from()->mstate; + const auto &mstate = mapping.from().mstate(); const Vec6 &x = mstate.m_x[static_cast(contact.rbIndex)]; const Vec3 r = mandos::core::rotationExpMap(x.segment<3>(3)).transpose() * (contact.contactPoint - x.segment<3>(0)); const MappingInfo mappingInfo{.m_r = r, - .mstate = mapping.from()->mstate, + .mstate = mapping.from().mstate(), .m_rbIndex = static_cast(contact.rbIndex), .m_toId = static_cast(collisionParticle)}; mapping.addMappingInfo(mappingInfo); diff --git a/src/Mandos/Core/Mappings/CollisionMapping.hpp b/src/Mandos/Core/Mappings/CollisionMapping.hpp index 6deff57570f48e4cdd2d8a56274c20f44af84601..8061470a3fe391e8fe0d5f2beae22d3b2a05239b 100644 --- a/src/Mandos/Core/Mappings/CollisionMapping.hpp +++ b/src/Mandos/Core/Mappings/CollisionMapping.hpp @@ -7,7 +7,6 @@ #include -#include #include #include @@ -15,7 +14,7 @@ #include #include -#include +#include #include @@ -35,7 +34,7 @@ struct MappingInfo { }; template <> -struct MappingInfo { +struct MANDOS_CORE_EXPORT MappingInfo { void apply(const std::vector &from, std::vector &to) const; void applyJ(const std::vector &from, std::vector &to) const; @@ -47,7 +46,7 @@ struct MappingInfo { }; template <> -struct MappingInfo { +struct MANDOS_CORE_EXPORT MappingInfo { void apply(const std::vector &from, std::vector &to) const; void applyJ(const std::vector &from, std::vector &to) const; @@ -61,7 +60,7 @@ struct MappingInfo { }; template <> -struct MappingInfo { +struct MANDOS_CORE_EXPORT MappingInfo { void apply(const std::vector & /*from*/, std::vector & /*to*/) const { } @@ -76,7 +75,7 @@ struct MappingInfo { }; template <> -struct MappingInfo { +struct MANDOS_CORE_EXPORT MappingInfo { void apply(const std::vector &from, std::vector &to) const; void applyJ(const std::vector &from, std::vector &to) const; @@ -89,14 +88,20 @@ struct MappingInfo { std::size_t m_toId; }; +template +struct CollisionParticlesTag +{ + using type = Particle3DTag; +}; + template class CollisionMapping { public: - using From = SimulationObject; - using To = SimulationObject; + using From = FromT; + using To = CollisionParticlesTag::type; - CollisionMapping(SimulationObjectHandle &from, SimulationObjectHandle &to) + CollisionMapping(SimulationObject from, SimulationObject to) : m_from(from) , m_to(to) { @@ -136,12 +141,12 @@ public: } } - SimulationObjectHandle from() const + SimulationObject from() const { return m_from; } - SimulationObjectHandle to() const + SimulationObject to() const { return m_to; } @@ -160,25 +165,25 @@ public: private: std::vector> m_mappingInfo; - SimulationObjectHandle m_from; - SimulationObjectHandle m_to; + SimulationObject m_from; + SimulationObject m_to; }; -void updateMapping(const collisions::ContactEventSide &contact, - CollisionMapping &mapping, - int CollisionParticle); +void MANDOS_CORE_EXPORT updateMapping(const collisions::ContactEventSide &contact, + CollisionMapping &mapping, + int CollisionParticle); -void updateMapping(const collisions::ContactEventSide &contact, - CollisionMapping &mapping, - int collisionParticle); +void MANDOS_CORE_EXPORT updateMapping(const collisions::ContactEventSide &contact, + CollisionMapping &mapping, + int collisionParticle); -void updateMapping(const collisions::ContactEventSide &contact, - CollisionMapping &mapping, - int collisionParticle); +void MANDOS_CORE_EXPORT updateMapping(const collisions::ContactEventSide &contact, + CollisionMapping &mapping, + int collisionParticle); -void updateMapping(const collisions::ContactEventSide &contact, - CollisionMapping &mapping, - int collisionParticle); +void MANDOS_CORE_EXPORT updateMapping(const collisions::ContactEventSide &contact, + CollisionMapping &mapping, + int collisionParticle); } // namespace collisions } // namespace mandos::core diff --git a/src/Mandos/Core/Mappings/IdentityMapping.cpp b/src/Mandos/Core/Mappings/IdentityMapping.cpp index 5d4eb4feaa9b4bf75180eb28899ddc4496b93570..603482f06398642a0505ebfa4f51d41b32885738 100644 --- a/src/Mandos/Core/Mappings/IdentityMapping.cpp +++ b/src/Mandos/Core/Mappings/IdentityMapping.cpp @@ -2,11 +2,12 @@ #include #include +#include namespace mandos::core { -IdentityMapping::IdentityMapping(SimulationObjectHandle from, SimulationObjectHandle to) +IdentityMapping::IdentityMapping(SimulationObject from, SimulationObject to) : m_from(from) , m_to(to) { @@ -40,12 +41,12 @@ void IdentityMapping::apply(const std::vector &from, std::vector &to }); } -SimulationObjectHandle IdentityMapping::to() const +SimulationObject IdentityMapping::to() const { return m_to; } -SimulationObjectHandle IdentityMapping::from() const +SimulationObject IdentityMapping::from() const { return m_from; } diff --git a/src/Mandos/Core/Mappings/IdentityMapping.hpp b/src/Mandos/Core/Mappings/IdentityMapping.hpp index d427c5cce4326874cb1f643d783b86c315e09601..1ba3190f202460f65e449b0764810ac2b4e99984 100644 --- a/src/Mandos/Core/Mappings/IdentityMapping.hpp +++ b/src/Mandos/Core/Mappings/IdentityMapping.hpp @@ -3,7 +3,7 @@ #include -#include +#include #include #include @@ -24,17 +24,17 @@ namespace mandos::core class MANDOS_CORE_EXPORT IdentityMapping { public: - using From = SimulationObjectHandle; - using To = SimulationObjectHandle; + using From = Particle3DTag; + using To = Particle3DTag; - IdentityMapping(SimulationObjectHandle from, SimulationObjectHandle to); + IdentityMapping(SimulationObject from, SimulationObject to); void apply(const std::vector &from, std::vector &to) const; void applyJ(const std::vector &from, std::vector &to) const; void applyJT(std::vector &from, const std::vector &to) const; - SimulationObjectHandle from() const; - SimulationObjectHandle to() const; + SimulationObject from() const; + SimulationObject to() const; void clear(); @@ -53,8 +53,8 @@ private: }; std::vector m_mapping; - SimulationObjectHandle m_from; - SimulationObjectHandle m_to; + SimulationObject m_from; + SimulationObject m_to; }; } // namespace mandos::core diff --git a/src/Mandos/Core/Mappings/RigidBodyGlobalPointMapping.cpp b/src/Mandos/Core/Mappings/RigidBodyGlobalPointMapping.cpp index 248b5dff967530761ace6b622f2016236c7d01ef..f48bbf30d4b5ed880dfcd09f54f89973a09ff1ef 100644 --- a/src/Mandos/Core/Mappings/RigidBodyGlobalPointMapping.cpp +++ b/src/Mandos/Core/Mappings/RigidBodyGlobalPointMapping.cpp @@ -5,8 +5,8 @@ namespace mandos::core { -RigidBodyGlobalPointMapping::RigidBodyGlobalPointMapping(SimulationObjectHandle from, - SimulationObjectHandle to) +RigidBodyGlobalPointMapping::RigidBodyGlobalPointMapping(SimulationObject from, + SimulationObject to) : m_from(from) , m_to(to) { @@ -22,7 +22,7 @@ void RigidBodyGlobalPointMapping::applyJT(std::vector &from, const std::ve assert(m_rigidBodyIndex.size() == to.size()); for (unsigned int i = 0; i < m_rigidBodyIndex.size(); ++i) { const auto iR = static_cast(m_rigidBodyIndex[i]); - const Vec3 &theta = m_from->mstate.m_x[iR].segment<3>(3); + const Vec3 &theta = m_from.mstate().m_x[iR].segment<3>(3); const Mat3 R = rotationExpMap(theta); const Mat3 thetaJT = computeLocalToGlobalAxisAngleJacobian(theta).transpose() * skew(R * m_localCoord[i]); @@ -36,7 +36,7 @@ void RigidBodyGlobalPointMapping::applyJ(const std::vector &from, std::vec assert(m_rigidBodyIndex.size() == to.size()); for (unsigned int i = 0; i < m_rigidBodyIndex.size(); ++i) { const auto iR = static_cast(m_rigidBodyIndex[i]); - const Vec3 &theta = m_from->mstate.m_x[iR].segment<3>(3); + const Vec3 &theta = m_from.mstate().m_x[iR].segment<3>(3); const Mat3 R = rotationExpMap(theta); const Mat3 thetaJ = -skew(R * m_localCoord[i]) * computeLocalToGlobalAxisAngleJacobian(theta); to[i] += from[iR].segment<3>(0) + thetaJ * from[iR].segment<3>(3); @@ -54,24 +54,24 @@ void RigidBodyGlobalPointMapping::apply(const std::vector &from, std::vect } } -SimulationObjectHandle RigidBodyGlobalPointMapping::to() const +SimulationObject RigidBodyGlobalPointMapping::to() const { return m_to; } -SimulationObjectHandle RigidBodyGlobalPointMapping::from() const +SimulationObject RigidBodyGlobalPointMapping::from() const { return m_from; } SparseMat RigidBodyGlobalPointMapping::J() const { - SparseMat J = SparseMat(m_to->mstate.size(), m_from->mstate.size()); + SparseMat J = SparseMat(m_to.mstate().size(), m_from.mstate().size()); std::vector triplets; for (unsigned int i = 0; i < m_rigidBodyIndex.size(); ++i) { const auto iR = static_cast(m_rigidBodyIndex[i]); - const Mat3 R = rotationExpMap(m_from->mstate.m_x[iR].segment<3>(3)); + const Mat3 R = rotationExpMap(m_from.mstate().m_x[iR].segment<3>(3)); const Mat3 thetaJ = -skew(R * m_localCoord[i]); for (unsigned int j = 0; j < 3; ++j) { triplets.emplace_back(3 * i + j, 6 * iR + j, 1.0); // Identity diff --git a/src/Mandos/Core/Mappings/RigidBodyGlobalPointMapping.hpp b/src/Mandos/Core/Mappings/RigidBodyGlobalPointMapping.hpp index fd0ab8bebe2dd7dba636a90ed703ac501084e50c..f08cc147d6df36a4da24b3e4b0d4e054ebe30ec3 100644 --- a/src/Mandos/Core/Mappings/RigidBodyGlobalPointMapping.hpp +++ b/src/Mandos/Core/Mappings/RigidBodyGlobalPointMapping.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include @@ -16,18 +16,17 @@ namespace mandos::core class MANDOS_CORE_EXPORT RigidBodyGlobalPointMapping { public: - using From = SimulationObjectHandle; - using To = SimulationObjectHandle; + using From = RigidBodyGlobalTag; + using To = Particle3DTag; - RigidBodyGlobalPointMapping(SimulationObjectHandle from, - SimulationObjectHandle to); + RigidBodyGlobalPointMapping(SimulationObject from, SimulationObject to); void apply(const std::vector &from, std::vector &to) const; void applyJ(const std::vector &from, std::vector &to) const; void applyJT(std::vector &from, const std::vector &to) const; - SimulationObjectHandle from() const; - SimulationObjectHandle to() const; + SimulationObject from() const; + SimulationObject to() const; void addLocalPoint(const Vec3 &localPoint, int rigidBodyIndex); @@ -35,8 +34,8 @@ public: SparseMat J() const; private: - SimulationObjectHandle m_from; - SimulationObjectHandle m_to; + SimulationObject m_from; + SimulationObject m_to; std::vector m_localCoord; std::vector m_rigidBodyIndex; diff --git a/src/Mandos/Core/Mappings/RigidBodyPointMapping.cpp b/src/Mandos/Core/Mappings/RigidBodyPointMapping.cpp index 77c9abd10b075101239ca83b75d1eb74c343d68f..cdec4dde57cad20c6511977c29ba3ec5d76496aa 100644 --- a/src/Mandos/Core/Mappings/RigidBodyPointMapping.cpp +++ b/src/Mandos/Core/Mappings/RigidBodyPointMapping.cpp @@ -5,8 +5,7 @@ namespace mandos::core { -RigidBodyPointMapping::RigidBodyPointMapping(SimulationObjectHandle from, - SimulationObjectHandle to) +RigidBodyPointMapping::RigidBodyPointMapping(SimulationObject from, SimulationObject to) : m_from(from) , m_to(to) { @@ -22,7 +21,7 @@ void RigidBodyPointMapping::applyJT(std::vector &from, const std::vector(m_rigidBodyIndex[i]); - const Mat3 R = rotationExpMap(m_from->mstate.m_x[iR].segment<3>(3)); + const Mat3 R = rotationExpMap(m_from.mstate().m_x[iR].segment<3>(3)); const Mat3 thetaJT = skew(R * m_localCoord[i]); from[iR].segment<3>(0) += to[i]; from[iR].segment<3>(3) += thetaJT * to[i]; @@ -34,7 +33,7 @@ void RigidBodyPointMapping::applyJ(const std::vector &from, std::vector(m_rigidBodyIndex[i]); - const Mat3 R = rotationExpMap(m_from->mstate.m_x[iR].segment<3>(3)); + const Mat3 R = rotationExpMap(m_from.mstate().m_x[iR].segment<3>(3)); const Mat3 thetaJ = -skew(R * m_localCoord[i]); to[i] += from[iR].segment<3>(0) + thetaJ * from[iR].segment<3>(3); } @@ -51,24 +50,24 @@ void RigidBodyPointMapping::apply(const std::vector &from, std::vector RigidBodyPointMapping::to() const +SimulationObject RigidBodyPointMapping::to() const { return m_to; } -SimulationObjectHandle RigidBodyPointMapping::from() const +SimulationObject RigidBodyPointMapping::from() const { return m_from; } SparseMat RigidBodyPointMapping::J() const { - SparseMat J = SparseMat(m_to->mstate.size(), m_from->mstate.size()); + SparseMat J = SparseMat(m_to.mstate().size(), m_from.mstate().size()); std::vector triplets; for (unsigned int i = 0; i < m_rigidBodyIndex.size(); ++i) { const auto iR = static_cast(m_rigidBodyIndex[i]); - const Mat3 R = rotationExpMap(m_from->mstate.m_x[iR].segment<3>(3)); + const Mat3 R = rotationExpMap(m_from.mstate().m_x[iR].segment<3>(3)); const Mat3 thetaJ = -skew(R * m_localCoord[i]); for (unsigned int j = 0; j < 3; ++j) { triplets.emplace_back((3 * i) + j, (6 * iR) + j, 1.0); // Identity diff --git a/src/Mandos/Core/Mappings/RigidBodyPointMapping.hpp b/src/Mandos/Core/Mappings/RigidBodyPointMapping.hpp index 8606e84027355179968c8f9687db27b9aefc1279..341793bbc2e8717e02616cde4ab2b44cdca9e3fc 100644 --- a/src/Mandos/Core/Mappings/RigidBodyPointMapping.hpp +++ b/src/Mandos/Core/Mappings/RigidBodyPointMapping.hpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include @@ -16,17 +16,17 @@ namespace mandos::core class MANDOS_CORE_EXPORT RigidBodyPointMapping { public: - using From = SimulationObjectHandle; - using To = SimulationObjectHandle; + using From = RigidBodyTag; + using To = Particle3DTag; - RigidBodyPointMapping(SimulationObjectHandle from, SimulationObjectHandle to); + RigidBodyPointMapping(SimulationObject from, SimulationObject to); void apply(const std::vector &from, std::vector &to) const; void applyJ(const std::vector &from, std::vector &to) const; void applyJT(std::vector &from, const std::vector &to) const; - SimulationObjectHandle from() const; - SimulationObjectHandle to() const; + SimulationObject from() const; + SimulationObject to() const; void addLocalPoint(const Vec3 &localPoint, int rigidBodyIndex); @@ -34,8 +34,8 @@ public: SparseMat J() const; private: - SimulationObjectHandle m_from; - SimulationObjectHandle m_to; + SimulationObject m_from; + SimulationObject m_to; std::vector m_localCoord; std::vector m_rigidBodyIndex; diff --git a/src/Mandos/Core/MechanicalStates/Particle3D.cpp b/src/Mandos/Core/MechanicalStates/Particle3D.cpp index 73e068a443dc291360ed32e5fe397658f8140442..7ff78eb10beb6e1be6e69c9f4d69129724714c26 100644 --- a/src/Mandos/Core/MechanicalStates/Particle3D.cpp +++ b/src/Mandos/Core/MechanicalStates/Particle3D.cpp @@ -4,7 +4,15 @@ #include -#include +#include +#include +#include +#include + +#include + +#include +#include namespace mandos::core { @@ -61,12 +69,14 @@ void mandos::core::MechanicalState::resize(int size) m_x0old.resize(static_cast(size)); m_v.resize(static_cast(size)); m_grad.resize(static_cast(size)); + m_buffer.resize(static_cast(size)); Vec::MapType(m_x.data()->data(), this->size()).setZero(); Vec::MapType(m_x0.data()->data(), this->size()).setZero(); Vec::MapType(m_x0old.data()->data(), this->size()).setZero(); Vec::MapType(m_v.data()->data(), this->size()).setZero(); Vec::MapType(m_grad.data()->data(), this->size()).setZero(); + Vec::MapType(m_buffer.data()->data(), this->size()).setZero(); m_hessian.resize(3 * static_cast(size), 3 * static_cast(size)); m_hessian.setZero(); @@ -88,11 +98,35 @@ void MechanicalState::setGradient(const Eigen::Ref &gr } void MechanicalState::scaleGradByHessian() { - Vec::MapType(this->m_grad.data()->data(), size()) = + Vec::MapType(this->m_buffer.data()->data(), size()).noalias() = m_hessian * Vec::ConstMapType(this->m_grad.data()->data(), size()); + swap(m_grad, m_buffer); } void MechanicalState::clearHessian() { m_hessian.setZero(); } + +void populateSimulationObject(SimulationObject handle, entt::registry ®istry) +{ + // Add potentials + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + // Add inertias + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); +} } // namespace mandos::core diff --git a/src/Mandos/Core/MechanicalStates/Particle3D.hpp b/src/Mandos/Core/MechanicalStates/Particle3D.hpp index d36cd995f9ed4874a52c0508d85377621e975c69..8bf9b7a3c28b4857b7de6430b123cf10ad406616 100644 --- a/src/Mandos/Core/MechanicalStates/Particle3D.hpp +++ b/src/Mandos/Core/MechanicalStates/Particle3D.hpp @@ -2,6 +2,9 @@ #define MANDOS_MECHANICALSTATES_PARTICLE3D_H #include +#include + +#include #include @@ -29,6 +32,8 @@ struct MANDOS_CORE_EXPORT MechanicalState { std::vector m_grad; SparseMat m_hessian; + mutable std::vector m_buffer; + /** * @brief The state size of the MechanicalState * @@ -96,6 +101,8 @@ struct MANDOS_CORE_EXPORT MechanicalState { void updateState(const Eigen::Ref &dx, const Eigen::Ref &x0, Scalar h); }; +void MANDOS_CORE_EXPORT populateSimulationObject(SimulationObject handle, entt::registry ®istry); + } // namespace mandos::core #endif // MANDOS_MECHANICALSTATES_PARTICLE3D_H diff --git a/src/Mandos/Core/MechanicalStates/RigidBody.cpp b/src/Mandos/Core/MechanicalStates/RigidBody.cpp index 84d3d95a69c773b3eb8f460a8a270aa0a1faf521..a59cca5eb8774762f9b400421db9fea25d16dd8f 100644 --- a/src/Mandos/Core/MechanicalStates/RigidBody.cpp +++ b/src/Mandos/Core/MechanicalStates/RigidBody.cpp @@ -1,6 +1,10 @@ #include #include +#include +#include +#include +#include #include namespace mandos::core @@ -40,4 +44,25 @@ void MechanicalState::advect(Scalar h) } } +void MANDOS_CORE_EXPORT populateSimulationObject(SimulationObject handle, entt::registry ®istry) +{ + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + // Add inertias + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); +} } // namespace mandos::core diff --git a/src/Mandos/Core/MechanicalStates/RigidBody.hpp b/src/Mandos/Core/MechanicalStates/RigidBody.hpp index 5c3c4e346fa7d2a7d07442df9104385ebff562d5..f5bc325ba0a865f1f1e3bd7dc5a5aef0378d03de 100644 --- a/src/Mandos/Core/MechanicalStates/RigidBody.hpp +++ b/src/Mandos/Core/MechanicalStates/RigidBody.hpp @@ -2,6 +2,7 @@ #define MANDOS_MECHANICALSTATES_RIGIDBODY_H #include +#include #include @@ -21,6 +22,8 @@ struct MANDOS_CORE_EXPORT MechanicalState : RigidBodyCommon { void advect(Scalar h); }; +void MANDOS_CORE_EXPORT populateSimulationObject(SimulationObject handle, entt::registry ®istry); + } // namespace mandos::core #endif // MANDOS_MECHANICALSTATES_RIGIDBODY_H diff --git a/src/Mandos/Core/MechanicalStates/RigidBodyGlobal.cpp b/src/Mandos/Core/MechanicalStates/RigidBodyGlobal.cpp index 2b854f961f4e44ed7a4796b0401ce11bd070b108..2c8d604cc11ca9014043ec7bac60b46c509653e6 100644 --- a/src/Mandos/Core/MechanicalStates/RigidBodyGlobal.cpp +++ b/src/Mandos/Core/MechanicalStates/RigidBodyGlobal.cpp @@ -1,6 +1,11 @@ #include #include +#include +#include +#include +#include + namespace mandos::core { @@ -37,4 +42,26 @@ void MechanicalState::advect(Scalar h) } } +void MANDOS_CORE_EXPORT populateSimulationObject(SimulationObject handle, entt::registry ®istry) +{ + // Add potentials + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + // Add inertias + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); + + utilities::static_for_each>( + [handle, ®istry]() { registry.emplace(handle.entt); }); +} } // namespace mandos::core diff --git a/src/Mandos/Core/MechanicalStates/RigidBodyGlobal.hpp b/src/Mandos/Core/MechanicalStates/RigidBodyGlobal.hpp index 44a2597788ffd6c7fc5b5cd3797eddc04dc8bfbb..dc725e271de12d2ad04ce17f6a74ae181cf5869d 100644 --- a/src/Mandos/Core/MechanicalStates/RigidBodyGlobal.hpp +++ b/src/Mandos/Core/MechanicalStates/RigidBodyGlobal.hpp @@ -2,6 +2,9 @@ #define MANDOS_MECHANICALSTATE_RIGIDBODYGLOBALCLOUD_H #include +#include + +#include #include @@ -21,6 +24,8 @@ struct MANDOS_CORE_EXPORT MechanicalState : RigidBodyCommon void advect(Scalar h); }; +void MANDOS_CORE_EXPORT populateSimulationObject(SimulationObject handle, entt::registry ®istry); + } // namespace mandos::core #endif // MANDOS_MECHANICALSTATE_RIGIDBODYGLOBALCLOUD_H diff --git a/src/Mandos/Core/Model.cpp b/src/Mandos/Core/Model.cpp index b88179016a7fce3977dc10a2b4d30db616e0e781..c927128cf5574d33567edc132eef944a970178e6 100644 --- a/src/Mandos/Core/Model.cpp +++ b/src/Mandos/Core/Model.cpp @@ -1,851 +1,5 @@ #include -#include +#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include "Mandos/Core/Collisions/CollisionDetection.hpp" -#include "Mandos/Core/Energies/CollisionSpring.hpp" -#include "Mandos/Core/Mappings/CollisionMapping.hpp" -#include "Mandos/Core/MechanicalState.hpp" -#include -#include - -#include - -namespace -{ - -template -void initialize(EnergyT & /*unused*/, const mandos::core::MechanicalState & /*unused*/) -{ -} - -template -mandos::core::Scalar computeEnergy(SimulationObjectT &simulationObject, mandos::core::Scalar h) -{ - mandos::core::Scalar accumulatedEnergy{0}; - if constexpr (SimulationObjectT::hasPotentials) { - mandos::core::utilities::static_for_each( - [&accumulatedEnergy, &simulationObject](const auto &energy) { - accumulatedEnergy += computeEnergy(energy, simulationObject.mstate); - }, - simulationObject.potentials()); - } - - if constexpr (SimulationObjectT::hasDissipativePotentials) { - mandos::core::utilities::static_for_each( - [&accumulatedEnergy, &simulationObject, h](const auto &energy) { - accumulatedEnergy += computeEnergy(energy, simulationObject.mstate, h); - }, - simulationObject.dissipativePotentials()); - } - - if (h != mandos::core::Scalar(0)) { - if constexpr (SimulationObjectT::hasInertias) { - mandos::core::utilities::static_for_each( - [&accumulatedEnergy, &simulationObject, h](const auto &energy) { - accumulatedEnergy += energy.computeEnergy(simulationObject.mstate, h); - }, - simulationObject.inertias()); - } - } - - return accumulatedEnergy; -} - -template -mandos::core::Scalar computeEnergyAndGradient(SimulationObjectT &simulationObject, mandos::core::Scalar h) -{ - mandos::core::Scalar accumulatedEnergy{0}; - if constexpr (SimulationObjectT::hasPotentials) { - mandos::core::utilities::static_for_each( - [&accumulatedEnergy, &simulationObject](const auto &energy) { - accumulatedEnergy += computeEnergyAndGradient(energy, simulationObject.mstate); - }, - simulationObject.potentials()); - } - - if constexpr (SimulationObjectT::hasDissipativePotentials) { - mandos::core::utilities::static_for_each( - [&accumulatedEnergy, &simulationObject, h](const auto &energy) { - accumulatedEnergy += computeEnergyAndGradient(energy, simulationObject.mstate, h); - }, - simulationObject.dissipativePotentials()); - } - - if (h != mandos::core::Scalar(0)) { - if constexpr (SimulationObjectT::hasInertias) { - mandos::core::utilities::static_for_each( - [&accumulatedEnergy, &simulationObject, h](const auto &energy) { - accumulatedEnergy += energy.computeEnergyAndGradient(simulationObject.mstate, h); - }, - simulationObject.inertias()); - } - } - - return accumulatedEnergy; -} - -template -mandos::core::Scalar computeEnergyGradientAndHessian(SimulationObjectT &simulationObject, mandos::core::Scalar h) -{ - mandos::core::Scalar accumulatedEnergy{0}; - if constexpr (SimulationObjectT::hasPotentials) { - mandos::core::utilities::static_for_each( - [&accumulatedEnergy, &simulationObject](const auto &energy) { - accumulatedEnergy += computeEnergyGradientAndHessian(energy, simulationObject.mstate); - }, - simulationObject.potentials()); - } - - if constexpr (SimulationObjectT::hasDissipativePotentials) { - mandos::core::utilities::static_for_each( - [&accumulatedEnergy, &simulationObject, h](const auto &energy) { - accumulatedEnergy += computeEnergyGradientAndHessian(energy, simulationObject.mstate, h); - }, - simulationObject.dissipativePotentials()); - } - - if (h != mandos::core::Scalar(0)) { - if constexpr (SimulationObjectT::hasInertias) { - mandos::core::utilities::static_for_each( - [&accumulatedEnergy, &simulationObject, h](const auto &energy) { - accumulatedEnergy += energy.computeEnergyGradientAndHessian(simulationObject.mstate, h); - }, - simulationObject.inertias()); - } - } - - return accumulatedEnergy; -} - -template -void computeLaggedPositionHessian(SimulationObjectT &simulationObject, mandos::core::Scalar h) -{ - if constexpr (SimulationObjectT::hasDissipativePotentials) { - mandos::core::utilities::static_for_each( - [&simulationObject, h](const auto &energy) { - energy.computeLaggedPositionHessian(simulationObject.mstate, h); - }, - simulationObject.dissipativePotentials()); - } - - if (h != mandos::core::Scalar(0)) { - if constexpr (SimulationObjectT::hasInertias) { - mandos::core::utilities::static_for_each( - [&simulationObject, h](const auto &energy) { - energy.computeLaggedPositionHessian(simulationObject.mstate, h); - }, - simulationObject.inertias()); - } - } -} - -template -void computeLaggedVelocityHessian(SimulationObjectT &simulationObject, mandos::core::Scalar h) -{ - if (h != mandos::core::Scalar(0)) { - if constexpr (SimulationObjectT::hasInertias) { - mandos::core::utilities::static_for_each( - [&simulationObject, h](const auto &energy) { - energy.computeLaggedVelocityHessian(simulationObject.mstate, h); - }, - simulationObject.inertias()); - } - } -} - -} // namespace - -namespace mandos::core -{ -void Model::populateGeneralizedGradient(Vec &gradient) -{ - // Propagate the forces back to the free simulation objects - for (auto node : m_backwardSortedList) { - auto &simObjectV = m_simulationObjectsGraph[node]; - std::visit( - [this](const auto handle) { - const auto &simulationObject = this->simulationObject(handle); - using SimulationObjectT = std::remove_pointer_t>; - if constexpr (SimulationObjectT::hasMappings) { - utilities::static_for_each( - [this](const auto &mappings) { - for (const auto &mapping : mappings) { - auto fromHandle = mapping.from(); - auto toHandle = mapping.to(); - - auto &from = this->simulationObject(fromHandle); - auto &to = this->simulationObject(toHandle); - - mapping.applyJT(from.mstate.m_grad, to.mstate.m_grad); - } - }, - simulationObject.m_mappings); - } - }, - simObjectV); - } - - // And accumulate on the generalized gradient - auto offset = 0; - for (auto node : m_freeSimulationObjects) { - const auto &simObjectV = m_simulationObjectsGraph[node]; - std::visit( - [this, &gradient, &offset](auto handle) { - auto &simulationObject = this->simulationObject(handle); - const auto size = simulationObject.mstate.size(); - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasProjections) { - mandos::core::utilities::static_for_each( - [&simulationObject](const auto &projection) { - projection.applyPT(simulationObject.mstate.gradientView()); - }, - simulationObject.projections); - } - simulationObject.mstate.gradient(gradient.segment(offset, size)); - offset += size; - }, - simObjectV); - } -} - -void Model::computeAdvection(Scalar h) -{ - ZoneScopedN("Model.computeAdvection"); - - // Clear x0 and x0old - utilities::static_for_each( - [](auto &simulationObjects) { - for (auto &simulationObject : simulationObjects) { - simulationObject.mstate.clearLaggedPositions(); - } - }, - m_simulationObjects); - // Compute Advection for free simulated objects - for (auto node : m_freeSimulationObjects) { - const auto &simObjectV = m_simulationObjectsGraph[node]; - std::visit( - [this, h](auto handle) { - auto &simulationObject = this->simulationObject(handle); - simulationObject.mstate.advect(h); - }, - simObjectV); - } - // And propagate the state from the free Simulation Objects to the mapped ones - for (auto node : m_forwardSortedList) { - const auto &simObjectV = m_simulationObjectsGraph[node]; - std::visit( - [this](const auto &handle) { - const auto &simulationObject = this->simulationObject(handle); - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasMappings) { - utilities::static_for_each( - [this, &simulationObject](const auto &mappings) { - for (const auto &mapping : mappings) { - auto toHandle = mapping.to(); - auto &to = this->simulationObject(toHandle); - - mapping.apply(simulationObject.mstate.m_x0, to.mstate.m_x0); - mapping.apply(simulationObject.mstate.m_x0old, to.mstate.m_x0old); - } - }, - simulationObject.m_mappings); - } - }, - simObjectV); - } -} - -Scalar Model::computeEnergy(Scalar h) const -{ - ZoneScopedN("Model.computeEnergy"); - // For computing the total energy of the system, we can just iterate on the simulation objects, there is no need - // to take into consideration mappings - auto accumulatedEnergy = Scalar{0}; - utilities::static_for_each( - [&accumulatedEnergy, h](const auto &simulationObjects) { - for (const auto &simulationObject : simulationObjects) { - accumulatedEnergy += ::computeEnergy(simulationObject, h); - } - }, - m_simulationObjects); - - return accumulatedEnergy; -} - -Scalar Model::computeEnergyAndGradient(Scalar h, Vec &gradient) -{ - ZoneScopedN("Model.computeEnergyAndGradient"); - // First we need to compute the gradient for each SimulationObject - auto accumulatedEnergy = Scalar{0}; - utilities::static_for_each( - [&accumulatedEnergy, h](auto &simulationObjects) { - for (auto &simulationObject : simulationObjects) { - using SimulationObjectT = std::remove_cvref_t; - if constexpr (SimulationObjectT::hasPotentials || SimulationObjectT::hasDissipativePotentials || - SimulationObjectT::hasInertias) { - simulationObject.mstate.clearGradient(); - } - - accumulatedEnergy += ::computeEnergyAndGradient(simulationObject, h); - } - }, - m_simulationObjects); - - populateGeneralizedGradient(gradient); - - return accumulatedEnergy; -} - -Scalar Model::computeEnergyGradientAndHessian(Scalar h, Vec &gradient, SystemMatrix &hessian) -{ - ZoneScopedN("Model.computeEnergyGradientAndHessian"); - // // First we need to compute the gradient and hessian for each SimulationObject - auto accumulatedEnergy{Scalar{0}}; - utilities::static_for_each( - [&accumulatedEnergy, h](auto &simulationObjects) { - for (auto &simulationObject : simulationObjects) { - using SimulationObjectT = std::remove_cvref_t; - if constexpr (SimulationObjectT::hasPotentials || SimulationObjectT::hasDissipativePotentials || - SimulationObjectT::hasInertias) { - simulationObject.mstate.clearGradient(); - simulationObject.mstate.clearHessian(); - } - - // This is a big hack because MSVC fails to compile if using a lambda here - accumulatedEnergy += ::computeEnergyGradientAndHessian(simulationObject, h); - } - }, - m_simulationObjects); - - populateGeneralizedGradient(gradient); - - hessian.setModel(*this); - hessian.setBackwardSortedList(m_backwardSortedList); - hessian.setForwardSortedList(m_forwardSortedList); - hessian.setFreeSimulationObject(m_freeSimulationObjects); - - return accumulatedEnergy; -} - -int Model::nDof() const -{ - ZoneScopedN("Model.nDof"); - // Only compute actual DoFs - int nDof = 0; - for (auto node : m_freeSimulationObjects) { - const auto &simObjectV = m_simulationObjectsGraph[node]; - std::visit( - [this, &nDof](const auto handle) { - const auto &simulationObject = this->simulationObject(handle); - nDof += simulationObject.mstate.size(); - }, - simObjectV); - } - - return nDof; -} - -void Model::state(Vec &x, Vec &v) const -{ - ZoneScopedN("Model.state"); - // Set the state of the free SimulationObjects - int offset = 0; - for (auto node : m_freeSimulationObjects) { - const auto &simObjectV = m_simulationObjectsGraph[node]; - std::visit( - [this, &offset, &x, &v](auto handle) { - auto &simulationObject = this->simulationObject(handle); - const auto size = simulationObject.mstate.size(); - simulationObject.mstate.state(x.segment(offset, size), v.segment(offset, size)); - offset += size; - }, - simObjectV); - } -} - -void Model::setState(const Vec &x, const Vec &v) -{ - ZoneScopedN("Model.setState"); - - { - ZoneScopedN("Model.setState.setZero"); - // First, set the state to 0 of all the objects, so we can later accumulate through mappings - utilities::static_for_each( - [](auto &simulationObjects) { - for (auto &simulationObject : simulationObjects) { - simulationObject.mstate.setZero(); - } - }, - m_simulationObjects); - } - - // Set the state of the free SimulationObjects - { - ZoneScopedN("Model.setState.setState"); - int offset = 0; - for (auto node : m_freeSimulationObjects) { - auto &simObjectV = m_simulationObjectsGraph[node]; - std::visit( - [this, &offset, &x, &v](auto handle) { - auto &simulationObject = this->simulationObject(handle); - const auto size = simulationObject.mstate.size(); - simulationObject.mstate.setState(x.segment(offset, size), v.segment(offset, size)); - offset += size; - }, - simObjectV); - } - } - - { - ZoneScopedN("Model.setState.apply/J"); - for (auto node : m_forwardSortedList) { - const auto &simObjectV = m_simulationObjectsGraph[node]; - std::visit( - [this](auto handle) { - const auto &simulationObject = this->simulationObject(handle); - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasMappings) { - utilities::static_for_each( - [this, &simulationObject](const auto &mappings) { - for (const auto &mapping : mappings) { - auto toHandle = mapping.to(); - auto &to = this->simulationObject(toHandle); - - mapping.apply(simulationObject.mstate.m_x, to.mstate.m_x); - mapping.applyJ(simulationObject.mstate.m_v, to.mstate.m_v); - } - }, - simulationObject.m_mappings); - } - }, - simObjectV); - } - } -} - -void Model::updateState(const Vec &dx, const Vec &x0, Scalar h) -{ - ZoneScopedN("Model.updateState"); - // First, reset the state of all the mapped objects, so we can later accumulate through mappings - for (auto node : m_forwardSortedList) { - if (std::ranges::find(m_freeSimulationObjects, node) == std::end(m_freeSimulationObjects)) { - std::visit( - [this](auto handle) { - auto &simulationObject = this->simulationObject(handle); - simulationObject.mstate.setZero(); - }, - m_simulationObjectsGraph[node]); - } - } - - // First, update the state of the free objects - int offset = 0; - for (auto node : m_freeSimulationObjects) { - const auto &simObjectV = m_simulationObjectsGraph[node]; - std::visit( - [this, &offset, &dx, &x0, h](auto handle) { - auto &simulationObject = this->simulationObject(handle); - const auto size = simulationObject.mstate.size(); - simulationObject.mstate.updateState(dx.segment(offset, size), x0.segment(offset, size), h); - offset += size; - }, - simObjectV); - } - - // And propagate the state from the free Simulation Objects to the mapped ones - for (auto node : m_forwardSortedList) { - const auto &simObjectV = m_simulationObjectsGraph[node]; - std::visit( - [this](const auto &handle) { - const auto &simulationObject = this->simulationObject(handle); - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasMappings) { - utilities::static_for_each( - [this, &simulationObject](const auto &mappings) { - for (const auto &mapping : mappings) { - auto toHandle = mapping.to(); - auto &to = this->simulationObject(toHandle); - - mapping.apply(simulationObject.mstate.m_x, to.mstate.m_x); - mapping.applyJ(simulationObject.mstate.m_v, to.mstate.m_v); - } - }, - simulationObject.m_mappings); - } - }, - simObjectV); - } -} - -const Model::KinematicGraph &Model::graph() const -{ - return m_simulationObjectsGraph; -} - -Model::KinematicGraph &Model::graph() -{ - return m_simulationObjectsGraph; -} - -void Model::updateColliders() -{ - utilities::static_for_each( - [](auto &simulationObjects) { - for (auto &simulationObject : simulationObjects) { - using SimulationObjectT = std::remove_cvref_t; - if constexpr (SimulationObjectT::hasColliders) { - utilities::static_for_each( - [&simulationObject](auto &colliders) { - for (auto &collider : colliders) { - collider.update(simulationObject.mstate); - } - }, - simulationObject.colliders()); - } - } - }, - m_simulationObjects); -} - -const std::vector &Model::freeSimulationObjects() const -{ - return m_freeSimulationObjects; -} - -void Model::detectCollisions() -{ - ZoneScopedN("Model.detectCollisions"); - utilities::static_for_each( - [](auto &collisionPairs) { - for (auto &collisionPair : collisionPairs) { - const auto &c0 = collisionPair.c0SimulationCollider; - const auto &c1 = collisionPair.c1SimulationCollider; - - const auto &c0Collider = c0.collider(); - const auto &c1Collider = c1.collider(); - - const auto &contactEvents = [&]() { - ZoneScopedN("CollisionDetection"); - return collisions::collisions(c0Collider, c1Collider); - }(); - TracyPlot("contactEvents", static_cast(contactEvents.size())); - - const auto nParticles = 2 * contactEvents.size(); - - auto &collisionState = collisionPair.collisionState().mstate; - collisionState.resize(static_cast(nParticles)); - - using SimulationObject0Tag = std::remove_cvref_t::SimulationObjectTag; - using SimulationObject1Tag = std::remove_cvref_t::SimulationObjectTag; - - using Collider0T = std::remove_cvref_t; - using Collider1T = std::remove_cvref_t; - - auto &mapping0 = - collisionPair.c0SimulationCollider.simulationObject() - .template mappings>() - [static_cast(collisionPair.mappingIndex0)]; - - auto &simObject1 = collisionPair.c1SimulationCollider.simulationObject(); - auto &mapping1 = - simObject1.template mappings>() - [static_cast(collisionPair.mappingIndex1)]; - - mapping0.resize(static_cast(contactEvents.size())); - mapping1.resize(static_cast(contactEvents.size())); - auto &collisionSpringEnergy = collisionPair.collisionState().template potential(); - collisionSpringEnergy.clear(); - for (auto cId = 0UL; cId < contactEvents.size(); ++cId) { - const auto &contact = contactEvents[cId]; - collisions::updateMapping(contact.c0Contact, mapping0, static_cast(cId)); - collisions::updateMapping( - contact.c1Contact, mapping1, static_cast(contactEvents.size() + cId)); - - collisionSpringEnergy.addElement(collisionPair.stiffness, collisionPair.threshold, contact.normal); - } - - collisionState.setZero(); - collisionState.clearLaggedPositions(); - mapping0.apply(c0.simulationObject().mstate.m_x, collisionState.m_x); - mapping1.apply(c1.simulationObject().mstate.m_x, collisionState.m_x); - mapping0.apply(c0.simulationObject().mstate.m_x0, collisionState.m_x0); - mapping1.apply(c1.simulationObject().mstate.m_x0, collisionState.m_x0); - - // Set up Friction - auto &frictionEnergy = collisionPair.collisionState().template dissipativePotential(); - frictionEnergy.clear(); - // Compute Lagged Collision Force - collisionState.clearGradient(); - mandos::core::computeEnergyAndGradient(collisionSpringEnergy, collisionState); - for (auto cId = 0UL; cId < contactEvents.size(); ++cId) { - const auto &contact = contactEvents[cId]; - const auto F = collisionState.m_grad[cId].norm(); - frictionEnergy.addElement(Friction::ParameterSet( - collisionPair.coulombConst, F, collisionPair.vMax, collisionPair.threshold, contact.normal)); - } - // Clear from lagged Collision Forces - collisionState.clearGradient(); - } - }, - m_collisionPairs); -} - -void Model::commit() -{ - ZoneScopedN("Model.commit"); - - // Clear edges - for (const auto &node : boost::make_iterator_range(boost::vertices(m_simulationObjectsGraph))) { - // The namemight be tricky, but this is removing all the edges of the node - boost::clear_vertex(node, m_simulationObjectsGraph); - } - - std::unordered_map vertexMap; - - // First create a map from the actual SimulationObject to the vertex_descriptor in the graph - for (const auto &node : boost::make_iterator_range(boost::vertices(m_simulationObjectsGraph))) { - std::visit( - [node, &vertexMap](auto handle) { - auto &simulationObject = handle.simulationObject(); - vertexMap.insert({&simulationObject, node}); - }, - m_simulationObjectsGraph[node]); - } - - utilities::static_for_each( - [this, &vertexMap](auto &simulationObjects) { - for (auto &simulationObject : simulationObjects) { - using SimulationObjectT = typename std::remove_cvref_t; - if constexpr (SimulationObjectT::hasMappings) { - utilities::static_for_each( - [this, &vertexMap](const auto &mappings) { - for (const auto &mapping : mappings) { - auto fromHandle = mapping.from(); - auto toHandle = mapping.to(); - - void *from = std::addressof(this->simulationObject(fromHandle)); - void *to = std::addressof(this->simulationObject(toHandle)); - - boost::add_edge(vertexMap.at(from), vertexMap.at(to), m_simulationObjectsGraph); - } - }, - simulationObject.m_mappings); - } - } - }, - m_simulationObjects); - - // Get the SimulationObject nodes without an input edge - m_freeSimulationObjects.clear(); - for (const auto &node : boost::make_iterator_range(boost::vertices(m_simulationObjectsGraph))) { - if (boost::in_degree(node, m_simulationObjectsGraph) == 0) { - m_freeSimulationObjects.push_back(node); - } - } - - m_forwardSortedList.clear(); - m_backwardSortedList.clear(); - - m_forwardSortedList.reserve(boost::num_vertices(m_simulationObjectsGraph)); - m_backwardSortedList.reserve(boost::num_vertices(m_simulationObjectsGraph)); - - auto reverseGraph = boost::make_reverse_graph(m_simulationObjectsGraph); - - // This might look strange. - // According to BGL documentation, they output the data in reverse order, so we need to reverse - std::vector sortedList; - sortedList.reserve(boost::num_vertices(m_simulationObjectsGraph)); - - boost::topological_sort(m_simulationObjectsGraph, std::back_inserter(sortedList)); - std::ranges::reverse_copy(sortedList, std::back_inserter(m_forwardSortedList)); - - sortedList.clear(); - boost::topological_sort(reverseGraph, std::back_inserter(sortedList)); - std::ranges::reverse_copy(sortedList, std::back_inserter(m_backwardSortedList)); - - // Initializing mappings this way might be a bit inneficient, but it avoid having to duplicate code, and its not a - // hot path - auto n = this->nDof(); - Vec x(n); - Vec v(n); - state(x, v); - setState(x, v); - - // Initialize energies if needed - utilities::static_for_each( - [](auto &simulationObjects) { - for (auto &simulationObject : simulationObjects) { - using SimulationObjectT = typename std::remove_cvref_t; - if constexpr (SimulationObjectT::hasPotentials) { - utilities::static_for_each( - [&simulationObject](auto &potential) { - using ::initialize; - - // Called by ADL if initialize function is found for the particular potential type - // Otherwise, noop - initialize(potential, simulationObject.mstate); - }, - simulationObject.m_potentials); - } - } - }, - m_simulationObjects); -} - -void Model::computeEnergyGradientParameterDerivative(const Vec &z, const DiffParameters &dp, Eigen::Ref result) -{ - int parameterIndex = 0; - - // Compute z_mapped, and place it in each SimulationObject gradient container - // Set z in the Free Simulation Objects Gradients - int offset = 0; - for (auto node : m_freeSimulationObjects) { - const auto &simObjectV = m_simulationObjectsGraph[node]; - std::visit( - [this, &offset, &z](auto handle) { - auto &simulationObject = this->simulationObject(handle); - const auto size = simulationObject.mstate.size(); - simulationObject.mstate.setGradient(z.segment(offset, size)); - - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasProjections) { - mandos::core::utilities::static_for_each( - [&simulationObject](const auto &projection) { - projection.applyP(simulationObject.mstate.gradientView()); - }, - simulationObject.projections); - } - - offset += size; - }, - simObjectV); - } - // Propagate z to the mapped objects - for (auto node : m_forwardSortedList) { - const auto &simObjectV = m_simulationObjectsGraph[node]; - std::visit( - [this](auto handle) { - const auto &simulationObject = this->simulationObject(handle); - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasMappings) { - mandos::core::utilities::static_for_each( - [this, &simulationObject](const auto &mappings) { - for (const auto &mapping : mappings) { - auto to = mapping.to(); - - mapping.applyJ(simulationObject.mstate.m_grad, - this->simulationObject(to).mstate.m_grad); - } - }, - simulationObject.m_mappings); - } - }, - simObjectV); - } - - for (const auto ¶meter : dp.parameters) { - std::visit( - [&](const auto ¶m) { - using Parameter_t = std::remove_cvref_t; - using E = Parameter_t::ParamEnergy; - constexpr auto P = Parameter_t::Param; - constexpr auto parameterShape = Parameter_t::parameterShape; - auto &simObj = param.m_simObj.simulationObject(); - - for (const auto index : param.m_indices) { - // Compute the parameter derivative - const auto zTdEdp = - simObj.template potential().template computeEnergyGradientParameterDerivative

( - simObj.mstate, index); - - constexpr auto size = parameterShape[1] * parameterShape[0]; - if constexpr (parameterShape[0] == 1 && parameterShape[1] == 1) { // Scalar parameter - result(parameterIndex) += zTdEdp; - } else if (parameterShape[1] == 1) { // Vector parameter - result.segment(parameterIndex) += zTdEdp; - } else { // Matrix Parameter - result.segment(parameterIndex) += zTdEdp.reshaped(); - } - parameterIndex += size; - } - }, - parameter); - } -} - -void Model::computeLaggedPositionHessian(Scalar h, - const Vec &x, - const Vec &v, - const Vec &x0, - const Vec &v0, - LaggedSystemMatrix &hessian) -{ - hessian.setModel(*this); - hessian.setBackwardSortedList(m_backwardSortedList); - hessian.setForwardSortedList(m_forwardSortedList); - hessian.setFreeSimulationObject(m_freeSimulationObjects); - hessian.m_x = &x; - hessian.m_v = &v; - hessian.m_x0 = &x0; - hessian.m_v0 = &v0; - - utilities::static_for_each( - [h](auto &simulationObjects) { - for (auto &simulationObject : simulationObjects) { - using SimulationObjectT = std::remove_cvref_t; - if constexpr (SimulationObjectT::hasPotentials || SimulationObjectT::hasDissipativePotentials || - SimulationObjectT::hasInertias) { - simulationObject.mstate.clearGradient(); - simulationObject.mstate.clearHessian(); - } - - ::computeLaggedPositionHessian(simulationObject, h); - } - }, - m_simulationObjects); -} -void Model::computeLaggedVelocityHessian(Scalar h, - const Vec &x, - const Vec &v, - const Vec &x0, - const Vec &v0, - LaggedSystemMatrix &hessian) -{ - hessian.setModel(*this); - hessian.setBackwardSortedList(m_backwardSortedList); - hessian.setForwardSortedList(m_forwardSortedList); - hessian.setFreeSimulationObject(m_freeSimulationObjects); - hessian.m_x = &x; - hessian.m_v = &v; - hessian.m_x0 = &x0; - hessian.m_v0 = &v0; - - utilities::static_for_each( - [h](auto &simulationObjects) { - for (auto &simulationObject : simulationObjects) { - using SimulationObjectT = std::remove_cvref_t; - if constexpr (SimulationObjectT::hasPotentials || SimulationObjectT::hasDissipativePotentials || - SimulationObjectT::hasInertias) { - simulationObject.mstate.clearGradient(); - simulationObject.mstate.clearHessian(); - } - - ::computeLaggedVelocityHessian(simulationObject, h); - } - }, - m_simulationObjects); -} - -} // namespace mandos::core +template class mandos::core::Model; diff --git a/src/Mandos/Core/Model.hpp b/src/Mandos/Core/Model.hpp index 740d8cd873e4cbf2d71ade673f22229236d9e28c..f3b703db9cd3220d17cc76324e140f1f83a38861 100644 --- a/src/Mandos/Core/Model.hpp +++ b/src/Mandos/Core/Model.hpp @@ -1,31 +1,41 @@ #ifndef MANDOS_MODEL_HPP #define MANDOS_MODEL_HPP -#include -#include #include -#include -#include -#include +#include -#include +#include +#include +#include -#include "Mandos/Core/Collisions/CollisionMesh.hpp" -#include "Mandos/Core/Collisions/CollisionPair.hpp" -#include "Mandos/Core/Collisions/SimulationCollider.hpp" -#include "Mandos/Core/Mappings/BarycentricMapping.hpp" -#include "Mandos/Core/MechanicalStates/Particle3D.hpp" -#include "Mandos/Core/MechanicalStates/RigidBody.hpp" -#include -#include +#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Mandos/Core/Energies.hpp" namespace mandos::core { +template +void initialize(Energy & /**/, const MechanicalState &mstate) +{ +} + +template struct SystemMatrix; + +template struct LaggedSystemMatrix; /** @@ -40,160 +50,29 @@ struct LaggedSystemMatrix; * undefined behavior. * */ -class MANDOS_CORE_EXPORT Model +template +class Model { public: - /** - * @brief A compile time list of supported SimulationObjects - * - */ - - using SimulationObjects = mandos::core::SimulationObjects; + using Kernel = KernelT; + using SimulationObjects = Kernel::SimulationObjects; + using KinematicGraph = Kernel::KinematicGraph; + using CollisionPairs = Kernel::CollisionPairs; - using KinematicGraph = mandos::core::KinematicGraph; - - Model() = default; + Model(); Model(const Model &other) = delete; Model(Model &&other) = delete; Model &operator=(const Model &other) = delete; - /** - * @brief A compile time list of supported Projections - * - */ - using Projections = utilities::typelist; - - /** - * @brief A compile time list of supported Mappings - * - */ - using Mappings = utilities::typelist; + entt::registry ®istry(); - /** - * @brief Accessor to the simulation objects of type SimulationObjectT. - * Note that taking a reference to current simulation objects is not valid, as adding new object may reallocate and - * invalid any current reference - * - * @tparam SimulationObjectT - * @return const std::vector& - */ - template - std::vector> &simulationObjects() - { - return std::get>>(m_simulationObjects); - } + const entt::registry ®istry() const; - /** - * @brief Accessor to the simulation objects of type SimulationObjectT. - * Note that taking a reference to current simulation objects is not valid, as adding new object may reallocate and - * invalid any current reference - * - * @tparam SimulationObjectT - * @return const std::vector& - */ - template - const std::vector> &simulationObjects() const - { - return std::get>>(m_simulationObjects); - } - - template - SimulationObject &simulationObject(SimulationObjectHandle handle) - { - return handle.simulationObject(); - } - - template - const SimulationObject &simulationObject(SimulationObjectHandle handle) const - { - return handle.simulationObject(); - } - - /** - * @brief Add a simulation object of type SimulationObject to the model - * - * @tparam Tag - */ - template - SimulationObjectHandle add() - { - // get the vector holding this types of SimulationObject - auto &v = std::get>>(m_simulationObjects); - - // Emplace a new object - v.emplace_back(); - - // Create a handle for this object - SimulationObjectHandle handle(v.size() - 1, simulationObjects()); - - // Add the handle to the graph - boost::add_vertex(handle, m_simulationObjectsGraph); - - // return the handle - return handle; - } - - template - int addMapping(SimulationObjectHandle fromHandle, SimulationObjectHandle toHandle) - { - fromHandle.simulationObject().template mappings().emplace_back(fromHandle, toHandle); - return static_cast(fromHandle.simulationObject().template mappings().size()) - 1; - } - - template - SimulationObjectHandle addCollisionPair(SimulationColliderT0 c0, - SimulationColliderT1 c1, - Scalar stiffness, - Scalar coulombConst, - Scalar vMax, - Scalar threshold) - { - auto collisionParticlesHandle = this->add(); - auto &collisionPair = - std::get>>( - m_collisionPairs) - .emplace_back(collisionParticlesHandle, c0, c1); - - using SimulationObjectT0 = - std::remove_cvref_t::SimulationObjectTag; - using SimulationObjectT1 = - std::remove_cvref_t::SimulationObjectTag; - using ColliderT0 = std::remove_cvref_t; - using ColliderT1 = std::remove_cvref_t; - - const int indexMapping0 = this->addMapping, - SimulationObjectT0, - Particle3DTag>(collisionPair.c0SimulationCollider.handle(), - collisionPair.collisionParticlesHandle); - - const int indexMapping1 = this->addMapping, - SimulationObjectT1, - Particle3DTag>(collisionPair.c1SimulationCollider.handle(), - collisionPair.collisionParticlesHandle); - - collisionPair.mappingIndex0 = indexMapping0; - collisionPair.mappingIndex1 = indexMapping1; - collisionPair.stiffness = stiffness; - collisionPair.coulombConst = coulombConst; - collisionPair.vMax = vMax; - collisionPair.threshold = threshold; - - return collisionParticlesHandle; - } - - /** - * @brief Initializes the structures for simulating the current Model. + entt::entity handle() const; - * - Constructs a DAG using the SimulationObjects and the Mapping to ensure the correct iteration order - * - Initializes the mappings by calling apply and applyJ with the current state of the free simulation object - * - Initializes energies for proper parallelization if needed - * - * The function must be called if there is any modification to the simulation model that needs to reinitialize, like - for example changing the mapping graph or adding new elements to parallel energies - */ - void commit(); + KinematicGraph &graph(); - void computeAdvection(Scalar h); + const KinematicGraph &graph() const; /** * @brief Computes the amount of degrees of freedom the model has @@ -213,7 +92,7 @@ public: * @param v Where to store the v generalized state. * [!WARNING] We dont perform bound checks, so ensure the size of x matches the nDof of the Model */ - void state(Vec &x, Vec &v) const; + void state(Eigen::Ref x, Eigen::Ref v) const; /** * @brief Set the MechanicalState of the model. This triggers the update of all the mappings in the model, updating @@ -222,7 +101,7 @@ public: * @param x The x generalized state * @param v The v generalized state */ - void setState(const Vec &x, const Vec &v); + void setState(Eigen::Ref x, Eigen::Ref v); /** * @brief Given a finite perturbation dx, update the MechanicalStates of the model. This triggers the update of all @@ -231,7 +110,32 @@ public: * @param dx The generalized perturbation vector * @param x0 The generalized x vector from where to apply the perturbation */ - void updateState(const Vec &dx, const Vec &x0, Scalar h); + void updateState(Eigen::Ref dx, Eigen::Ref x0, Scalar h); + + /** + * @brief Add a simulation object of type SimulationObject to the model + * + * @tparam Tag + */ + template + SimulationObject add(); + + // template + // int addMapping(SimulationObject from, SimulationObject to); + + /** + * @brief Initializes the structures for simulating the current Model. + + * - Constructs a DAG using the SimulationObjects and the Mapping to ensure the correct iteration order + * - Initializes the mappings by calling apply and applyJ with the current state of the free simulation object + * - Initializes energies for proper parallelization if needed + * + * The function must be called if there is any modification to the simulation model that needs to reinitialize, like + for example changing the mapping graph or adding new elements to parallel energies + */ + void commit(); + + void computeAdvection(Scalar h); /** * @brief Computes the energy of the simulation model on a given time span @@ -254,7 +158,7 @@ public: * @param gradient The derivatives of the energy wrt x * @return Scalar The internal energy of the system */ - Scalar computeEnergyAndGradient(Scalar h, Vec &gradient); + Scalar computeEnergyAndGradient(Scalar h, Eigen::Ref gradient); /** * @brief Computes the energy, its derivatives and second order derivatives wrt x @@ -268,20 +172,20 @@ public: * @param hessian The second derivatives of the energy wrt x * @return Scalar The internal energy of the system */ - Scalar computeEnergyGradientAndHessian(Scalar h, Vec &gradient, SystemMatrix &hessian); + Scalar computeEnergyGradientAndHessian(Scalar h, Eigen::Ref gradient, SystemMatrix &hessian); void computeLaggedPositionHessian(Scalar h, - const Vec &x, - const Vec &v, - const Vec &x0, - const Vec &v0, - LaggedSystemMatrix &hessian); + Eigen::Ref x, + Eigen::Ref v, + Eigen::Ref x0, + Eigen::Ref v0, + LaggedSystemMatrix &hessian); void computeLaggedVelocityHessian(Scalar h, - const Vec &x, - const Vec &v, - const Vec &x0, - const Vec &v0, - LaggedSystemMatrix &hessian); + Eigen::Ref x, + Eigen::Ref v, + Eigen::Ref x0, + Eigen::Ref v0, + LaggedSystemMatrix &hessian); /** * @brief Computes the vector-matrix product of a given vector `z` with the derivative of the @@ -295,26 +199,26 @@ public: * @param dp The parameters with respect to which the derivatives are computed. * @param result Result of the vector-matrix product */ - void computeEnergyGradientParameterDerivative(const Vec &z, const DiffParameters &dp, Eigen::Ref result); + void computeEnergyGradientParameterDerivative(Eigen::Ref z, + const DiffParameters &dp, + Eigen::Ref result); void updateColliders(); void detectCollisions(); - void updateCollisionMappings(); - - KinematicGraph &graph(); - - const std::vector &freeSimulationObjects() const; - const KinematicGraph &graph() const; + const std::vector &freeSimulationObjects() const; + const std::vector &forwardSortedList() const; + const std::vector &backwardSortedList() const; private: - void populateGeneralizedGradient(Vec &gradient); + void populateGeneralizedGradient(Eigen::Ref gradient); /** * @brief The SimulationObjects that define the Model * * This is the direct accessor to the Simulation Objects in the model */ - SimulationObjects::map::as m_simulationObjects; + entt::registry m_registry; + entt::entity m_modelHandle; /** * @brief A graph containing references to the SimulationObject as nodes and edges representing the mappings @@ -322,31 +226,843 @@ private: */ KinematicGraph m_simulationObjectsGraph; - std::vector m_freeSimulationObjects; + std::vector m_freeSimulationObjects; /** * @brief A sorted list of the SimulationObject graph. It is used to propagate the state through the mappings in * order * */ - std::vector m_forwardSortedList; + std::vector m_forwardSortedList; /** * @brief A sorted list of the reversed SimulationObjects graph. Is is used to propagate forces through the mappings * in order * */ - std::vector m_backwardSortedList; - - std::tuple< - std::vector, - collisions::SimulationCollider>>, - std::vector< - collisions::CollisionPair, - collisions::SimulationCollider>>> - m_collisionPairs; + std::vector m_backwardSortedList; }; +template +template +inline SimulationObject Model::add() +{ + // get the vector holding this types of SimulationObject + auto entt = m_registry.create(); + m_registry.emplace>(entt); + + SimulationObject handle = SimulationObject{entt, &m_registry.storage>()}; + KernelT::populateSimulationObject(handle, m_registry); + + // Add the handle to the graph + boost::add_vertex(handle, m_simulationObjectsGraph); + + // return the handle + return handle; +} + +template +Model::Model() + : m_modelHandle(m_registry.create()) +{ +} + +template +entt::registry &Model::registry() +{ + return m_registry; +} + +template +const entt::registry &Model::registry() const +{ + return m_registry; +} + +template +inline entt::entity Model::handle() const +{ + return m_modelHandle; +} + +template +const KernelT::KinematicGraph &Model::graph() const +{ + return m_simulationObjectsGraph; +} + +template +KernelT::KinematicGraph &Model::graph() +{ + return m_simulationObjectsGraph; +} + +template +void Model::populateGeneralizedGradient(Eigen::Ref gradient) +{ + // Propagate the forces back to the free simulation objects + for (auto node : m_backwardSortedList) { + auto &simObjectV = m_simulationObjectsGraph[node]; + std::visit( + [this](mandos::core::SimulationObject handle) { + auto &mstate = m_registry.get>(handle.entt); + mandos::core::utilities::static_for_each>( + [this, &mstate, handle]() { + using Mapping = MappingVec::value_type; + const auto &mappings = m_registry.get(handle.entt); + for (const auto &mapping : mappings) { + auto to = mapping.to(); + const auto &toMstate = + m_registry.get>(to.entt); + + mapping.applyJT(mstate.m_grad, toMstate.m_grad); + } + }); + }, + simObjectV); + } + + // And accumulate on the generalized gradient + auto offset = 0; + for (auto node : m_freeSimulationObjects) { + const auto &simObjectV = m_simulationObjectsGraph[node]; + std::visit( + [this, &gradient, &offset](SimulationObject handle) { + auto &mstate = m_registry.get>(handle.entt); + const auto size = mstate.size(); + + // Apply projections if needed + mandos::core::utilities::static_for_each>( + [this, &mstate, handle]() { + const auto &projection = m_registry.get(handle.entt); + projection.applyPT(mstate.gradientView()); + }); + + mstate.gradient(gradient.segment(offset, size)); + offset += size; + }, + simObjectV); + } +} + +template +int Model::nDof() const +{ + ZoneScopedN("Model.nDof"); + // Only compute actual DoFs + int nDof = 0; + for (auto node : m_freeSimulationObjects) { + const auto &simObjectV = m_simulationObjectsGraph[node]; + std::visit( + [this, &nDof](SimulationObject handle) { + nDof += m_registry.get>(handle.entt).size(); + }, + simObjectV); + } + + return nDof; +} + +template +void Model::state(Eigen::Ref x, Eigen::Ref v) const +{ + ZoneScopedN("Model.state"); + // Set the state of the free SimulationObjects + int offset = 0; + for (auto node : m_freeSimulationObjects) { + const auto &simObjectV = m_simulationObjectsGraph[node]; + std::visit( + [this, &offset, &x, &v](SimulationObject handle) { + const auto &mstate = m_registry.get>(handle.entt); + const auto size = mstate.size(); + mstate.state(x.segment(offset, size), v.segment(offset, size)); + offset += size; + }, + simObjectV); + } +} + +template +void Model::setState(Eigen::Ref x, Eigen::Ref v) +{ + ZoneScopedN("Model.setState"); + + { + ZoneScopedN("Model.setState.setZero"); + // First, set the state to 0 of all the objects, so we can later accumulate through mappings + utilities::static_for_each([this]() { + using Tag = SimulationObject::Tag; + m_registry.view>().each([](auto &mstate) { mstate.setZero(); }); + }); + } + + // Set the state of the free SimulationObjects + { + ZoneScopedN("Model.setState.setState"); + int offset = 0; + for (auto node : m_freeSimulationObjects) { + auto &simObjectV = m_simulationObjectsGraph[node]; + std::visit( + [this, &offset, &x, &v](SimulationObject handle) { + auto &mstate = m_registry.get>(handle.entt); + const auto size = mstate.size(); + mstate.setState(x.segment(offset, size), v.segment(offset, size)); + offset += size; + }, + simObjectV); + } + } + + { + ZoneScopedN("Model.setState.apply/J"); + for (auto node : m_forwardSortedList) { + const auto &simObjectV = m_simulationObjectsGraph[node]; + std::visit( + [this](SimulationObject handle) { + const auto &mstate = m_registry.get>(handle.entt); + mandos::core::utilities::static_for_each>( + [this, &mstate, handle]() { + using Mapping = MappingsVec::value_type; + const auto &mappings = m_registry.get>(handle.entt); + for (const auto &mapping : mappings) { + auto to = mapping.to(); + auto &toMstate = + m_registry.get>(to.entt); + + mapping.apply(mstate.m_x, toMstate.m_x); + mapping.applyJ(mstate.m_v, toMstate.m_v); + } + }); + }, + simObjectV); + } + } +} + +template +void Model::updateState(Eigen::Ref dx, Eigen::Ref x0, Scalar h) +{ + ZoneScopedN("Model.updateState"); + // First, reset the state of all the mapped objects, so we can later accumulate through mappings + for (auto node : m_forwardSortedList) { + if (std::ranges::find(m_freeSimulationObjects, node) == std::end(m_freeSimulationObjects)) { + std::visit( + [this](SimulationObject handle) { + m_registry.get>(handle.entt).setZero(); + }, + m_simulationObjectsGraph[node]); + } + } + + // First, update the state of the free objects + int offset = 0; + for (auto node : m_freeSimulationObjects) { + const auto &simObjectV = m_simulationObjectsGraph[node]; + std::visit( + [this, &offset, &dx, &x0, h](SimulationObject handle) { + auto &mstate = m_registry.get>(handle.entt); + const auto size = mstate.size(); + mstate.updateState(dx.segment(offset, size), x0.segment(offset, size), h); + offset += size; + }, + simObjectV); + } + + // And propagate the state from the free Simulation Objects to the mapped ones + for (auto node : m_forwardSortedList) { + const auto &simObjectV = m_simulationObjectsGraph[node]; + std::visit( + [this](SimulationObject handle) { + const auto &mstate = m_registry.get>(handle.entt); + mandos::core::utilities::static_for_each>( + [this, &mstate, handle]() { + using Mapping = MappingsVec::value_type; + const auto &mappings = m_registry.get>(handle.entt); + for (const auto &mapping : mappings) { + auto to = mapping.to(); + auto &toMstate = + m_registry.get>(to.entt); + + mapping.apply(mstate.m_x, toMstate.m_x); + mapping.applyJ(mstate.m_v, toMstate.m_v); + } + }); + }, + simObjectV); + } +} + +template +void Model::computeAdvection(Scalar h) +{ + ZoneScopedN("Model.computeAdvection"); + + // Clear x0 and x0old + utilities::static_for_each([this]() { + using Tag = SimulationObject::Tag; + m_registry.view>().each( + [&](MechanicalState &mstate) { mstate.clearLaggedPositions(); }); + }); + + // Compute Advection for free simulated objects + for (auto node : m_freeSimulationObjects) { + const auto &simObjectV = m_simulationObjectsGraph[node]; + std::visit([this, h]( + SimulationObject handle) { m_registry.get>(handle.entt).advect(h); }, + simObjectV); + } + + // And propagate the state from the free Simulation Objects to the mapped ones + for (auto node : m_forwardSortedList) { + const auto &simObjectV = m_simulationObjectsGraph[node]; + std::visit( + [this](mandos::core::SimulationObject handle) { + const auto &mstate = m_registry.get>(handle.entt); + mandos::core::utilities::static_for_each>( + [this, &mstate, handle]() { + using Mapping = MappingsVec::value_type; + const auto &mappings = m_registry.get>(handle.entt); + for (const auto &mapping : mappings) { + auto to = mapping.to(); + auto &toMstate = + m_registry.get>(to.entt); + + mapping.apply(mstate.m_x0, toMstate.m_x0); + mapping.apply(mstate.m_x0old, toMstate.m_x0old); + } + }); + }, + + simObjectV); + } +} + +template +Scalar Model::computeEnergy(Scalar h) const +{ + ZoneScopedN("Model.computeEnergy"); + // For computing the total energy of the system, we can just iterate on the simulation objects, there is no need + // to take into consideration mappings + auto accumulatedEnergy = Scalar{0}; + utilities::static_for_each( + [this, &accumulatedEnergy, h]() { + using Tag = SimulationObject::Tag; + m_registry.view>().each([&](entt::entity handle, const MechanicalState &mstate) { + using mandos::core::computeEnergy; + utilities::static_for_each>([&]() { + const auto &potential = m_registry.get(handle); + accumulatedEnergy += computeEnergy(potential, mstate); + }); + + utilities::static_for_each>([&]() { + const auto &inertia = m_registry.get(handle); + accumulatedEnergy += inertia.computeEnergy(mstate, h); + }); + + utilities::static_for_each>( + [&]() { + const auto &potential = m_registry.get(handle); + accumulatedEnergy += computeEnergy(potential, mstate, h); + }); + }); + }); + + return accumulatedEnergy; +} + +template +Scalar Model::computeEnergyAndGradient(Scalar h, Eigen::Ref gradient) +{ + ZoneScopedN("Model.computeEnergyAndGradient"); + // First we need to compute the gradient for each SimulationObject + auto accumulatedEnergy = Scalar{0}; + utilities::static_for_each( + [this, &accumulatedEnergy, h]() { + using Tag = SimulationObject::Tag; + m_registry.view>().each([&](entt::entity handle, MechanicalState &mstate) { + mstate.clearGradient(); + + using mandos::core::computeEnergyAndGradient; + utilities::static_for_each>([&]() { + const auto &potential = m_registry.get(handle); + accumulatedEnergy += computeEnergyAndGradient(potential, mstate); + }); + + utilities::static_for_each>([&]() { + const auto &inertia = m_registry.get(handle); + accumulatedEnergy += inertia.computeEnergyAndGradient(mstate, h); + }); + + utilities::static_for_each>( + [&]() { + const auto &potential = m_registry.get(handle); + accumulatedEnergy += computeEnergyAndGradient(potential, mstate, h); + }); + }); + }); + + populateGeneralizedGradient(gradient); + + return accumulatedEnergy; +} + +template +Scalar Model::computeEnergyGradientAndHessian(Scalar h, + Eigen::Ref gradient, + SystemMatrix &hessian) +{ + ZoneScopedN("Model.computeEnergyGradientAndHessian"); + // // First we need to compute the gradient and hessian for each SimulationObject + auto accumulatedEnergy = Scalar{0}; + utilities::static_for_each( + [this, &accumulatedEnergy, h]() { + using Tag = SimulationObject::Tag; + m_registry.view>().each([&](entt::entity handle, MechanicalState &mstate) { + mstate.clearGradient(); + mstate.clearHessian(); + + using mandos::core::computeEnergyGradientAndHessian; + utilities::static_for_each>([&]() { + const auto &potential = m_registry.get(handle); + accumulatedEnergy += computeEnergyGradientAndHessian(potential, mstate); + }); + + utilities::static_for_each>([&]() { + const auto &inertia = m_registry.get(handle); + accumulatedEnergy += inertia.computeEnergyGradientAndHessian(mstate, h); + }); + + utilities::static_for_each>( + [&]() { + const auto &potential = m_registry.get(handle); + accumulatedEnergy += computeEnergyGradientAndHessian(potential, mstate, h); + }); + }); + }); + + populateGeneralizedGradient(gradient); + + hessian.setModel(*this); + hessian.setBackwardSortedList(m_backwardSortedList); + hessian.setForwardSortedList(m_forwardSortedList); + hessian.setFreeSimulationObject(m_freeSimulationObjects); + + return accumulatedEnergy; +} + +template +void Model::updateColliders() +{ + utilities::static_for_each([this]() { + using Tag = SimulationObject::Tag; + m_registry.view>().each([this](entt::entity handle, const MechanicalState &mstate) { + utilities::static_for_each>([&]() { + auto &colliders = m_registry.get(handle); + for (auto &collider : colliders) { + collider.update(mstate); + } + }); + }); + }); +} + +template +void Model::detectCollisions() +{ + ZoneScopedN("Model.detectCollisions"); + utilities::static_for_each([&]() { + using CollisionPair = CollisionPairs::value_type; + using SimulationColliderT0 = CollisionPair::SimulationColliderT0; + using SimulationColliderT1 = CollisionPair::SimulationColliderT1; + using Tag0 = SimulationColliderT0::Tag; + using Tag1 = SimulationColliderT1::Tag; + using Collider0 = SimulationColliderT0::Collider; + using Collider1 = SimulationColliderT1::Collider; + using ToT0 = typename collisions::CollisionParticlesTag::type; + using ToT1 = typename collisions::CollisionParticlesTag::type; + + auto *collisionPairs = m_registry.try_get(m_modelHandle); + + // Ensure both mappings have the same To type + static_assert(std::is_same_v, + "CollisionParticles type is different for each side of the collision"); + + if (!collisionPairs) { + return; + } + for (auto &collisionPair : *collisionPairs) { + const auto &c0 = collisionPair.c0SimulationCollider; + const auto &c1 = collisionPair.c1SimulationCollider; + + const auto &c0Collider = c0.collider(m_registry); + const auto &c1Collider = c1.collider(m_registry); + + const auto &contactEvents = [&]() { + ZoneScopedN("CollisionDetection"); + using mandos::core::collisions::collisions; + return collisions(c0Collider, c1Collider); + }(); + TracyPlot("contactEvents", static_cast(contactEvents.size())); + + const auto nParticles = 2 * contactEvents.size(); + + auto &collisionState = m_registry.get>(collisionPair.collisionParticles.entt); + collisionState.resize(static_cast(nParticles)); + + auto &mapping0 = m_registry.get>>( + c0.handle().entt)[collisionPair.mappingIndex0]; + + auto &mapping1 = m_registry.get>>( + c1.handle().entt)[collisionPair.mappingIndex1]; + + mapping0.resize(static_cast(contactEvents.size())); + mapping1.resize(static_cast(contactEvents.size())); + auto &collisionSpringEnergy = m_registry.get(collisionPair.collisionParticles.entt); + + collisionSpringEnergy.clear(); + for (auto cId = 0UL; cId < contactEvents.size(); ++cId) { + const auto &contact = contactEvents[cId]; + collisions::updateMapping(contact.c0Contact, mapping0, static_cast(cId)); + collisions::updateMapping(contact.c1Contact, mapping1, static_cast(contactEvents.size() + cId)); + + collisionSpringEnergy.addElement(collisionPair.stiffness, collisionPair.threshold, contact.normal); + } + + const auto &mstate0 = m_registry.get>(c0.handle().entt); + const auto &mstate1 = m_registry.get>(c1.handle().entt); + collisionState.setZero(); + collisionState.clearLaggedPositions(); + mapping0.apply(mstate0.m_x, collisionState.m_x); + mapping1.apply(mstate1.m_x, collisionState.m_x); + mapping0.apply(mstate0.m_x0, collisionState.m_x0); + mapping1.apply(mstate1.m_x0, collisionState.m_x0); + + // // Set up Friction + auto &frictionEnergy = m_registry.get(collisionPair.collisionParticles.entt); + frictionEnergy.clear(); + // // Compute Lagged Collision Force + collisionState.clearGradient(); + mandos::core::computeEnergyAndGradient(collisionSpringEnergy, collisionState); + for (auto cId = 0UL; cId < contactEvents.size(); ++cId) { + const auto &contact = contactEvents[cId]; + const auto F = collisionState.m_grad[cId].norm(); + frictionEnergy.addElement(Friction::ParameterSet( + collisionPair.coulombConst, F, collisionPair.vMax, collisionPair.threshold, contact.normal)); + } + // // Clear from lagged Collision Forces + collisionState.clearGradient(); + } + }); +} + +template +void Model::commit() +{ + ZoneScopedN("Model.commit"); + + // Clear edges + for (const auto &node : boost::make_iterator_range(boost::vertices(m_simulationObjectsGraph))) { + // The namemight be tricky, but this is removing all the edges of the node + boost::clear_vertex(node, m_simulationObjectsGraph); + } + + std::unordered_map vertexMap; + + // First create a map from the actual SimulationObject to the vertex_descriptor in the graph + for (const auto &node : boost::make_iterator_range(boost::vertices(m_simulationObjectsGraph))) { + std::visit( + [node, &vertexMap](SimulationObject handle) { vertexMap.insert({handle.entt, node}); }, + m_simulationObjectsGraph[node]); + } + + utilities::static_for_each([this, &vertexMap]() { + using Tag = SimulationObject::Tag; + m_registry.view>().each([&](entt::entity handle, const MechanicalState &mstate) { + utilities::static_for_each>([&]() { + using Mapping = MappingsVec::value_type; + const auto &mappings = m_registry.get>(handle); + for (const auto &mapping : mappings) { + auto from = mapping.from(); + auto to = mapping.to(); + + boost::add_edge(vertexMap[from.entt], vertexMap[to.entt], m_simulationObjectsGraph); + } + }); + }); + }); + + // Get the SimulationObject nodes without an input edge + m_freeSimulationObjects.clear(); + for (const auto &node : boost::make_iterator_range(boost::vertices(m_simulationObjectsGraph))) { + if (boost::in_degree(node, m_simulationObjectsGraph) == 0) { + m_freeSimulationObjects.push_back(node); + } + } + + m_forwardSortedList.clear(); + m_backwardSortedList.clear(); + + m_forwardSortedList.reserve(boost::num_vertices(m_simulationObjectsGraph)); + m_backwardSortedList.reserve(boost::num_vertices(m_simulationObjectsGraph)); + + auto reverseGraph = boost::make_reverse_graph(m_simulationObjectsGraph); + + // This might look strange. + // According to BGL documentation, they output the data in reverse order, so we need to reverse + std::vector sortedList; + sortedList.reserve(boost::num_vertices(m_simulationObjectsGraph)); + + boost::topological_sort(m_simulationObjectsGraph, std::back_inserter(sortedList)); + std::ranges::reverse_copy(sortedList, std::back_inserter(m_forwardSortedList)); + + sortedList.clear(); + boost::topological_sort(reverseGraph, std::back_inserter(sortedList)); + std::ranges::reverse_copy(sortedList, std::back_inserter(m_backwardSortedList)); + + // Initializing mappings this way might be a bit inneficient, but it avoid having to duplicate code, and its not a + // hot path + auto n = this->nDof(); + Vec x(n); + Vec v(n); + state(x, v); + setState(x, v); + + // Initialize energies if needed + utilities::static_for_each([this]() { + using Tag = SimulationObject::Tag; + m_registry.view>().each([&](entt::entity handle, const MechanicalState &mstate) { + utilities::static_for_each>([&]() { + auto &potential = m_registry.get(handle); + using mandos::core::initialize; + initialize(potential, mstate); + }); + }); + }); +} + +template +const std::vector &Model::freeSimulationObjects() const +{ + return m_freeSimulationObjects; +} + +template +const std::vector &Model::forwardSortedList() const +{ + return m_forwardSortedList; +} + +template +const std::vector &Model::backwardSortedList() const +{ + return m_backwardSortedList; +} + +template +void Model::computeLaggedPositionHessian(Scalar h, + Eigen::Ref x, + Eigen::Ref v, + Eigen::Ref x0, + Eigen::Ref v0, + LaggedSystemMatrix &hessian) +{ + hessian.setModel(*this); + hessian.setBackwardSortedList(m_backwardSortedList); + hessian.setForwardSortedList(m_forwardSortedList); + hessian.setFreeSimulationObject(m_freeSimulationObjects); + new (&hessian.m_x) Eigen::Map(x.data(), x.size()); + new (&hessian.m_v) Eigen::Map(v.data(), v.size()); + new (&hessian.m_x0) Eigen::Map(x0.data(), x0.size()); + new (&hessian.m_v0) Eigen::Map(v0.data(), v0.size()); + + utilities::static_for_each([this, h]() { + using Tag = SimulationObject::Tag; + m_registry.view>().each([&](entt::entity handle, MechanicalState &mstate) { + mstate.clearGradient(); + mstate.clearHessian(); + + utilities::static_for_each>([&]() { + auto &potential = m_registry.get(handle); + potential.computeLaggedPositionHessian(mstate, h); + }); + + utilities::static_for_each>([&]() { + auto &inertia = m_registry.get(handle); + inertia.computeLaggedPositionHessian(mstate, h); + }); + }); + }); +} + +template +void Model::computeLaggedVelocityHessian(Scalar h, + Eigen::Ref x, + Eigen::Ref v, + Eigen::Ref x0, + Eigen::Ref v0, + LaggedSystemMatrix &hessian) +{ + hessian.setModel(*this); + hessian.setBackwardSortedList(m_backwardSortedList); + hessian.setForwardSortedList(m_forwardSortedList); + hessian.setFreeSimulationObject(m_freeSimulationObjects); + new (&hessian.m_x) Eigen::Map(x.data(), x.size()); + new (&hessian.m_v) Eigen::Map(v.data(), v.size()); + new (&hessian.m_x0) Eigen::Map(x0.data(), x0.size()); + new (&hessian.m_v0) Eigen::Map(v0.data(), v0.size()); + + utilities::static_for_each([this, h]() { + using Tag = SimulationObject::Tag; + m_registry.view>().each([&](entt::entity handle, MechanicalState &mstate) { + mstate.clearGradient(); + mstate.clearHessian(); + + utilities::static_for_each>([&]() { + auto &inertia = m_registry.get(handle); + inertia.computeLaggedVelocityHessian(mstate, h); + }); + }); + }); +} + +template +void Model::computeEnergyGradientParameterDerivative(Eigen::Ref z, + const DiffParameters &dp, + Eigen::Ref result) +{ + int parameterIndex = 0; + + // Compute z_mapped, and place it in each SimulationObject gradient container + // Set z in the Free Simulation Objects Gradients + int offset = 0; + for (auto node : m_freeSimulationObjects) { + const auto &simObjectV = m_simulationObjectsGraph[node]; + std::visit( + [this, &offset, &z](SimulationObject handle) { + auto &mstate = m_registry.template get>(handle.entt); + const auto size = mstate.size(); + mstate.setGradient(z.segment(offset, size)); + + // Apply projections if needed + mandos::core::utilities::static_for_each>( + [this, &mstate, handle]() { + const auto &projection = m_registry.template get(handle.entt); + projection.applyPT(mstate.gradientView()); + }); + + offset += size; + }, + simObjectV); + } + + // Propagate z to the mapped objects + for (auto node : m_forwardSortedList) { + const auto &simObjectV = m_simulationObjectsGraph[node]; + std::visit( + [this](SimulationObject handle) { + const auto &mstate = m_registry.get>(handle.entt); + mandos::core::utilities::static_for_each>( + [this, &mstate, handle]() { + using Mapping = MappingsVec::value_type; + const auto &mappings = m_registry.template get>(handle.entt); + for (const auto &mapping : mappings) { + auto to = mapping.to(); + auto &toMstate = + m_registry.template get>(to.entt); + + mapping.applyJ(mstate.m_grad, toMstate.m_grad); + } + }); + }, + + simObjectV); + } + + for (const auto ¶meter : dp.parameters) { + std::visit( + [&](const auto ¶m) { + using Parameter_t = std::remove_cvref_t; + using E = Parameter_t::ParamEnergy; + constexpr auto P = Parameter_t::Param; + constexpr auto parameterShape = Parameter_t::parameterShape; + const auto simObj = param.m_simObj; + auto &mstate = m_registry.get>(simObj.entt); + + for (const auto index : param.m_indices) { + // Compute the parameter derivative + const auto &potential = m_registry.get(simObj.entt); + const auto zTdEdp = potential.template computeEnergyGradientParameterDerivative

(mstate, index); + + constexpr auto size = parameterShape[1] * parameterShape[0]; + if constexpr (parameterShape[0] == 1 && parameterShape[1] == 1) { // Scalar parameter + result(parameterIndex) += zTdEdp; + } else if (parameterShape[1] == 1) { // Vector parameter + result.segment(parameterIndex) += zTdEdp; + } else { // Matrix Parameter + result.segment(parameterIndex) += zTdEdp.reshaped(); + } + parameterIndex += size; + } + }, + parameter); + } +} + +template +auto addCollisionPair(Model &model, + SimulationColliderT0 c0, + SimulationColliderT1 c1, + Scalar stiffness, + Scalar coulombConst, + Scalar vMax, + Scalar threshold) +{ + using CollisionPairT = mandos::core::collisions::CollisionPair; + using CollisionPairsT = std::vector; + + auto *collisionPairs = model.registry().template try_get(model.handle()); + if (!collisionPairs) { + collisionPairs = &model.registry().template emplace(model.handle()); + } + auto &collisionPair = collisionPairs->emplace_back(); + + using SimulationObjectT0 = SimulationColliderT0::Tag; + using SimulationObjectT1 = SimulationColliderT1::Tag; + using ColliderT0 = SimulationColliderT0::Collider; // std::remove_cvref_t; + using ColliderT1 = SimulationColliderT1::Collider; // std::remove_cvref_t; + using ToT0 = typename collisions::CollisionParticlesTag::type; + using ToT1 = typename collisions::CollisionParticlesTag::type; + // Ensure both mappings have the same To type + static_assert(std::is_same_v, "CollisionParticles type is different for each side of the collision"); + auto collisionParticlesHandle = model.template add(); + + auto &mappings0 = + model.registry() + .template get>>( + c0.handle().entt); + mappings0.emplace_back(c0.handle(), collisionParticlesHandle); + const auto indexMapping0 = mappings0.size() - 1; + + auto &mappings1 = + model.registry() + .template get>>( + c1.handle().entt); + mappings1.emplace_back(c1.handle(), collisionParticlesHandle); + const auto indexMapping1 = mappings1.size() - 1; + + collisionPair.collisionParticles = collisionParticlesHandle; + collisionPair.c0SimulationCollider = c0; + collisionPair.c1SimulationCollider = c1; + collisionPair.mappingIndex0 = indexMapping0; + collisionPair.mappingIndex1 = indexMapping1; + collisionPair.stiffness = stiffness; + collisionPair.coulombConst = coulombConst; + collisionPair.vMax = vMax; + collisionPair.threshold = threshold; + + return collisionParticlesHandle; +} + } // namespace mandos::core #endif // MANDOS_MODEL_HPP diff --git a/src/Mandos/Core/Projections.hpp b/src/Mandos/Core/Projections.hpp index 5c7dfc6f726e4bf3f0a00ce444c549bf1277758c..c02a03b4d8502d0256c578d85ac103d7c3a3d6f1 100644 --- a/src/Mandos/Core/Projections.hpp +++ b/src/Mandos/Core/Projections.hpp @@ -1,13 +1,13 @@ #ifndef MANDOS_PROJECTIONS_H #define MANDOS_PROJECTIONS_H -#include +#include + #include #include #include #include -#include namespace mandos::core { @@ -31,6 +31,7 @@ namespace mandos::core */ template struct Projections { + using type = std::tuple<>; }; /** @@ -40,25 +41,23 @@ struct Projections { */ template <> struct Projections { - static constexpr bool HasProjections = true; - using types = utilities::typelist; - types::template as projections; + using type = std::tuple; }; template <> struct Projections { - static constexpr bool HasProjections = true; - using types = utilities::typelist; - types::template as projections; + using type = std::tuple; }; template <> struct Projections { - static constexpr bool HasProjections = true; - using types = utilities::typelist; - types::template as projections; + using type = std::tuple; }; +template +using Projections_t = Projections::type; + + } // namespace mandos::core #endif // MANDOS_PROJECTIONS_H diff --git a/src/Mandos/Core/Simulation.cpp b/src/Mandos/Core/Simulation.cpp index 2ff9250e3c62b5820626e111322c6cf5281bfb6a..862a8344a6b7f72679842ff9283b1efe1d97f353 100644 --- a/src/Mandos/Core/Simulation.cpp +++ b/src/Mandos/Core/Simulation.cpp @@ -1,120 +1,8 @@ #include +#include #include -#include -#include -#include - -namespace mandos::core -{ - -// TODO move this into its own class, so we can provide a configuration scheme (iterations, line search, cache...) -SimulationStepResult step(Model &model, const StepParameters &stepParameters) -{ - FrameMark; - FrameMarkNamed("Forward"); - // Get the size of the DoF of the model - auto nDof = model.nDof(); - - // Get the current generalized state of the simulation - Vec x0{Vec::Zero(nDof)}; - Vec v0{Vec::Zero(nDof)}; - model.state(x0, v0); - - model.updateColliders(); - model.detectCollisions(); - - // Store x0 and x0old - model.computeAdvection(stepParameters.h); - - // Set the warm start for Newton - model.updateState(stepParameters.h * v0, x0, stepParameters.h); - - Vec grad{Vec::Zero(nDof)}; - SystemMatrix hessian(nDof, nDof); - - // TODO Make these variables - const int maxNewtonIterations = stepParameters.newtonIterations; - - Vec linX{Vec::Zero(nDof)}; - Vec linV{Vec::Zero(nDof)}; - auto currentNewtonIteration{0}; - while (currentNewtonIteration < maxNewtonIterations) { - ZoneScopedN("Simulation.step.NewtonIteration"); - // Get state on current linearization point - model.state(linX, linV); - - // Compute energy and derivatives - // ----------------------------------------------------------------------------------------- - grad.setZero(); - // There is no need to clear the SystemMatrix; it only contains references to the actual SimulationObject - // hessians, which are going to be cleared - - const Scalar linEnergy{model.computeEnergyGradientAndHessian(stepParameters.h, grad, hessian)}; - TracyPlot("Energy", linEnergy); - TracyPlot("Newton iteration", static_cast(currentNewtonIteration)); - - // If the grad is small, consider the step a success - const auto gradNorm = grad.norm(); - TracyPlot("Gradient norm", gradNorm); - if (gradNorm < stepParameters.gradNorm) { - return SimulationStepResult::Success; - } - - // Integration step - // ---------------------------------------------------------------------------------------- - const auto dx = [&stepParameters, &hessian, &grad]() { - if (stepParameters.cgIterations != 0) { - Eigen::ConjugateGradient cg; - cg.setTolerance(stepParameters.cgError); - cg.setMaxIterations(stepParameters.cgIterations); - cg.compute(hessian); - return [&cg, &grad]() { - ZoneScopedN("Simulation.step.cgSolve"); - return cg.solve(-grad).eval(); - }(); - } - return (-grad).eval(); - }(); - - // Update state - // ----------------------------------------------------------------------------------------- - model.updateState(dx, x0, stepParameters.h); - - // Line search - // ----------------------------------------------------------------------------------------- - Scalar lineSearchEnergy{model.computeEnergy(stepParameters.h)}; - Scalar alpha{1.0}; - const int maxLSIterations = stepParameters.lineSearchIterations; - int lsIterations{0}; - if (maxLSIterations != 0) { - while (lineSearchEnergy > linEnergy) { - ZoneScopedN("Simulation.step.lineSearch"); - if (lsIterations >= maxLSIterations) { - // If we dont converge, set the original state and return - if (!stepParameters.acceptFailedSolution) { - model.setState(x0, v0); - } - return SimulationStepResult::LineSearchFailed; - } - - alpha *= 0.5; - model.setState(linX, linV); - model.updateState(alpha * dx, x0, stepParameters.h); - lineSearchEnergy = model.computeEnergy(stepParameters.h); - lsIterations++; - } - } - TracyPlot("Line Search Iterations", static_cast(lsIterations)); - currentNewtonIteration++; - } - - // If we dont converge, set the original state and return - if (!stepParameters.acceptFailedSolution) { - model.setState(x0, v0); - } - return SimulationStepResult::NewtonFailed; -} - -} // namespace mandos::core +template mandos::core::SimulationStepResult mandos::core::step( + mandos::core::Model &model, + const mandos::core::StepParameters &stepParameters); diff --git a/src/Mandos/Core/Simulation.hpp b/src/Mandos/Core/Simulation.hpp index 63619b1d92587ee2b9eb01198afede1925865faa..955dd3a3d6cdc4ff639ad62575149a62a0bcce3a 100644 --- a/src/Mandos/Core/Simulation.hpp +++ b/src/Mandos/Core/Simulation.hpp @@ -2,17 +2,14 @@ #define MANDOS_SIMULATION_H_ #include +#include +#include #include namespace mandos::core { -class Model; - -struct SystemMatrix; -struct LaggedSystemMatrix; - /** * @brief Parameters for configuring the integration step * @@ -71,7 +68,115 @@ enum class SimulationStepResult { Success = 0, LineSearchFailed = 1, NewtonFaile * @param model The simulation model * @param stepParameters A StepParameters object to specify the step thresholds and iterations */ -SimulationStepResult MANDOS_CORE_EXPORT step(Model &model, const StepParameters &stepParameters); +template +SimulationStepResult step(Model &model, const StepParameters &stepParameters) +{ + FrameMark; + FrameMarkNamed("Forward"); + // Get the size of the DoF of the model + auto nDof = model.nDof(); + + // Get the current generalized state of the simulation + Vec x0{Vec::Zero(nDof)}; + Vec v0{Vec::Zero(nDof)}; + model.state(x0, v0); + + model.updateColliders(); + model.detectCollisions(); + + // Store x0 and x0old + model.computeAdvection(stepParameters.h); + + // Set the warm start for Newton + model.updateState(stepParameters.h * v0, x0, stepParameters.h); + + Vec grad{Vec::Zero(nDof)}; + SystemMatrix hessian(nDof, nDof); + + // TODO Make these variables + const int maxNewtonIterations = stepParameters.newtonIterations; + + Vec linX{Vec::Zero(nDof)}; + Vec linV{Vec::Zero(nDof)}; + auto currentNewtonIteration{0}; + while (currentNewtonIteration < maxNewtonIterations) { + ZoneScopedN("Simulation.step.NewtonIteration"); + // Get state on current linearization point + model.state(linX, linV); + + // Compute energy and derivatives + // ----------------------------------------------------------------------------------------- + grad.setZero(); + // There is no need to clear the SystemMatrix; it only contains references to the actual SimulationObject + // hessians, which are going to be cleared + + const Scalar linEnergy{model.computeEnergyGradientAndHessian(stepParameters.h, grad, hessian)}; + TracyPlot("Energy", linEnergy); + TracyPlot("Newton iteration", static_cast(currentNewtonIteration)); + + // If the grad is small, consider the step a success + const auto gradNorm = grad.norm(); + TracyPlot("Gradient norm", gradNorm); + if (gradNorm < stepParameters.gradNorm) { + return SimulationStepResult::Success; + } + + // Integration step + // ---------------------------------------------------------------------------------------- + const auto dx = [&stepParameters, &hessian, &grad]() { + if (stepParameters.cgIterations != 0) { + Eigen:: + ConjugateGradient, Eigen::Upper | Eigen::Lower, Eigen::IdentityPreconditioner> + cg; + cg.setTolerance(stepParameters.cgError); + cg.setMaxIterations(stepParameters.cgIterations); + cg.compute(hessian); + return [&cg, &grad]() { + ZoneScopedN("Simulation.step.cgSolve"); + return cg.solve(-grad).eval(); + }(); + } + return (-grad).eval(); + }(); + + // Update state + // ----------------------------------------------------------------------------------------- + model.updateState(dx, x0, stepParameters.h); + + // Line search + // ----------------------------------------------------------------------------------------- + Scalar lineSearchEnergy{model.computeEnergy(stepParameters.h)}; + Scalar alpha{1.0}; + const int maxLSIterations = stepParameters.lineSearchIterations; + int lsIterations{0}; + if (maxLSIterations != 0) { + while (lineSearchEnergy > linEnergy) { + ZoneScopedN("Simulation.step.lineSearch"); + if (lsIterations >= maxLSIterations) { + // If we dont converge, set the original state and return + if (!stepParameters.acceptFailedSolution) { + model.setState(x0, v0); + } + return SimulationStepResult::LineSearchFailed; + } + + alpha *= 0.5; + model.setState(linX, linV); + model.updateState(alpha * dx, x0, stepParameters.h); + lineSearchEnergy = model.computeEnergy(stepParameters.h); + lsIterations++; + } + } + TracyPlot("Line Search Iterations", static_cast(lsIterations)); + currentNewtonIteration++; + } + + // If we dont converge, set the original state and return + if (!stepParameters.acceptFailedSolution) { + model.setState(x0, v0); + } + return SimulationStepResult::NewtonFailed; +} } // namespace mandos::core diff --git a/src/Mandos/Core/SimulationObject.hpp b/src/Mandos/Core/SimulationObject.hpp index dd6443883867bd8db5f5b2e68ec1f53619440679..3f41990c6ac5841281224c0cbfbf59a1e56677bd 100644 --- a/src/Mandos/Core/SimulationObject.hpp +++ b/src/Mandos/Core/SimulationObject.hpp @@ -1,13 +1,10 @@ -#ifndef EA5CC9BC_9B26_421C_B4CB_4E265608D7F2 -#define EA5CC9BC_9B26_421C_B4CB_4E265608D7F2 +#ifndef MANDOS_CORE_SIMULATIONOBJECT_HPP +#define MANDOS_CORE_SIMULATIONOBJECT_HPP -#include -#include -#include -#include -#include +#include +#include -#include +#include namespace mandos::core { @@ -20,154 +17,31 @@ namespace mandos::core * Specifies the type of this SimulationObject and therefore, the type of mechanical state, energies, mappings and * projections it has. */ -template -struct SimulationObject : Inertias, - Potentials, - DissipativePotentials, - Projections, - Mappings, - Colliders { - using SimulationObjectTag = Tag; - - static constexpr bool hasInertias = !std::is_empty_v>; - static constexpr bool hasPotentials = !std::is_empty_v>; - static constexpr bool hasDissipativePotentials = !std::is_empty_v>; - static constexpr bool hasProjections = !std::is_empty_v>; - static constexpr bool hasMappings = !std::is_empty_v>; - static constexpr bool hasColliders = !std::is_empty_v>; - - template - [[nodiscard]] EnergyT &potential() - { - static_assert(hasPotentials, - "Trying to get a potential energy from a SimulationObject without potential energies"); - return std::get(this->m_potentials); - } - - template - [[nodiscard]] const EnergyT &potential() const - { - static_assert(hasPotentials, - "Trying to get a potential energy from a SimulationObject without potential energies"); - return std::get(this->m_potentials); - } - - template - [[nodiscard]] EnergyT &dissipativePotential() - { - static_assert(hasDissipativePotentials, - "Trying to get a dissipative potential energy from a SimulationObject without dissipative " - "potential energies"); - return std::get(this->m_dissipativePotentials); - } - - template - [[nodiscard]] const EnergyT &dissipativePotential() const - { - static_assert(hasDissipativePotentials, - "Trying to get a dissipative potential energy from a SimulationObject without dissipative " - "potential energies"); - return std::get(this->m_dissipativePotentials); - } - - template - [[nodiscard]] EnergyT &inertia() - { - static_assert(hasInertias, - "Trying to get an inertial energy from a SimulationObject without inertial energies"); - return std::get(this->inertias()); - } - - template - [[nodiscard]] const EnergyT &inertia() const - { - static_assert(hasInertias, - "Trying to get an inertial energy from a SimulationObject without inertial energies"); - return std::get(this->inertias()); - } - - template - int addMapping(typename MappingT::To &to) - { - static_assert(hasMappings, "Trying to add a Mapping to a SimulationObject without mappings"); - std::get>(this->m_mappings).emplace_back(*this, to); - return static_cast(std::get>(this->m_mappings).size()) - 1; - } +template +struct SimulationObject { + using Tag = TagT; - template - std::vector &mappings() - { - static_assert(hasMappings, "Trying to get a Mapping from a SimulationObject without mappings"); - return std::get>(this->m_mappings); - } + entt::entity entt; + entt::storage> *mstates; - template - const std::vector &mappings() const + const auto &mstate() const { - static_assert(hasMappings, "Trying to get a Mapping from a SimulationObject without mappings"); - return std::get>(this->m_mappings); + return (*mstates).get(entt); } - auto &potentials() - { - static_assert(hasPotentials, - "Trying to get potential energies from a SimulationObject without potential energies"); - return this->m_potentials; - } - - const auto &potentials() const - { - static_assert(hasPotentials, - "Trying to get potential energies from a SimulationObject without potential energies"); - return this->m_potentials; - } - - auto &dissipativePotentials() - { - static_assert(hasDissipativePotentials, - "Trying to get dissipative potential energies from a SimulationObject without dissipative " - "potential energies"); - return this->m_dissipativePotentials; - } - - const auto &dissipativePotentials() const - { - static_assert(hasDissipativePotentials, - "Trying to get dissipative potential energies from a SimulationObject without dissipative " - "potential energies"); - return this->m_dissipativePotentials; - } - auto &inertias() + auto &mstate() { - static_assert(hasInertias, "Trying to get inertial energies from a SimulationObject without inertial energies"); - return this->m_inertias; + return (*mstates).get(entt); } - const auto &inertias() const - { - static_assert(hasInertias, "Trying to get inertial energies from a SimulationObject without inertial energies"); - return this->m_inertias; - } - - auto &colliders() - { - static_assert(hasColliders, "Trying to get colliders from a SimulationObject without colliders"); - return this->m_colliders; - } - - const auto &colliders() const - { - static_assert(hasColliders, "Trying to get colliders from a SimulationObject without colliders"); - return this->m_colliders; - } - - /** - * @brief The current mechanical state of the SimulationObject - * - */ - MechanicalState mstate; }; +template +void populateSimulationObject(SimulationObject /*simObject*/, entt::registry & /*registry*/) +{ + static_assert(std::is_void_v, "populateSimulationObject not found"); +} + } // namespace mandos::core -#endif /* EA5CC9BC_9B26_421C_B4CB_4E265608D7F2 */ +#endif // MANDOS_CORE_SIMULATIONOBJECT_HPP diff --git a/src/Mandos/Core/SimulationObjectHandle.hpp b/src/Mandos/Core/SimulationObjectHandle.hpp deleted file mode 100644 index 7a6ed65c7d3eb431697814fc0b47687cd144219f..0000000000000000000000000000000000000000 --- a/src/Mandos/Core/SimulationObjectHandle.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef MANDOS_CORE_SIMULATIONOBJECTHANDLE_HPP -#define MANDOS_CORE_SIMULATIONOBJECTHANDLE_HPP - -#include -#include -#include - -#include - -namespace mandos::core -{ - -class Model; - -template -struct SimulationObject; - -template -struct SimulationObjectHandle { - SimulationObjectHandle() = default; - - SimulationObjectHandle(std::size_t index, std::vector> &simulationObjects) - : m_index(index) - , m_simulationObjects(&simulationObjects) - { - } - - MANDOS_CORE_EXPORT SimulationObject *operator->() const - { - return &(*m_simulationObjects)[m_index]; - } - - MANDOS_CORE_EXPORT SimulationObject &simulationObject() const - { - return (*m_simulationObjects)[m_index]; - } - -private: - std::size_t m_index = std::numeric_limits::max(); - std::vector> *m_simulationObjects{nullptr}; -}; - -template -using SimulationObjectHandle_t = SimulationObjectHandle; - -} // namespace mandos::core - -#endif // MANDOS_CORE_SIMULATIONOBJECTHANDLE_HPP \ No newline at end of file diff --git a/src/Mandos/Core/SystemMatrix.cpp b/src/Mandos/Core/SystemMatrix.cpp index 6aa2beb163d6db22a635526cc3c1d90ca0fd7e9f..5f0a0548bce190dd4ced42429cc51af8a62d31c7 100644 --- a/src/Mandos/Core/SystemMatrix.cpp +++ b/src/Mandos/Core/SystemMatrix.cpp @@ -1,73 +1,5 @@ #include -namespace mandos::core -{ +#include -SystemMatrix::SystemMatrix(int rows, int cols) - : m_rows(rows) - , m_cols(cols) -{ -} - -int SystemMatrix::rows() const -{ - return m_rows; -} - -int SystemMatrix::cols() const -{ - return m_cols; -} - -void SystemMatrix::clear() -{ -} - -void SystemMatrix::setForwardSortedList(const std::vector &forwardSortedList) -{ - m_forwardSortedList = std::addressof(forwardSortedList); -} - -const std::vector &SystemMatrix::forwardSortedList() const -{ - return *m_forwardSortedList; -} - -void SystemMatrix::setBackwardSortedList( - const std::vector &backwardSortedList) -{ - m_backwardSortedList = std::addressof(backwardSortedList); -} - -const std::vector &SystemMatrix::backwardSortedList() const -{ - return *m_backwardSortedList; -} - -void SystemMatrix::setFreeSimulationObject( - const std::vector &freeSimulationObjects) -{ - m_freeSimulationObjects = std::addressof(freeSimulationObjects); -} - -const std::vector &SystemMatrix::freeSimulationObjects() const -{ - return *m_freeSimulationObjects; -} - -const Model::KinematicGraph &SystemMatrix::graph() const -{ - return m_model->graph(); -} - -Model &SystemMatrix::model() const -{ - return *m_model; -} - -void SystemMatrix::setModel(Model &model) -{ - m_model = std::addressof(model); -} - -} // namespace mandos::core \ No newline at end of file +template class mandos::core::SystemMatrix; \ No newline at end of file diff --git a/src/Mandos/Core/SystemMatrix.hpp b/src/Mandos/Core/SystemMatrix.hpp index c8ac89d2a81bfe62924fade0f8697e2d2663f88a..25339ef009b4063de6180e69d7f6ed23ad5ecab8 100644 --- a/src/Mandos/Core/SystemMatrix.hpp +++ b/src/Mandos/Core/SystemMatrix.hpp @@ -1,28 +1,32 @@ #ifndef MANDOS_SYSTEMMATRIX_H #define MANDOS_SYSTEMMATRIX_H -#include - -#include -#include -#include +#include #include -#include #include #include +#include +#include + +#include + namespace mandos::core { +template struct SystemMatrix; -} + +template +class Model; +} // namespace mandos::core namespace Eigen::internal { // MatrixReplacement looks-like a SparseMatrix, so let's inherits its traits: -template <> -struct traits : public traits { +template +struct traits> : public traits { }; } // namespace Eigen::internal @@ -41,7 +45,8 @@ namespace mandos::core * */ -struct SystemMatrix : public Eigen::EigenBase { +template +struct SystemMatrix : public Eigen::EigenBase> { public: using Scalar = mandos::core::Scalar; using RealScalar = mandos::core::Scalar; @@ -65,166 +70,269 @@ public: return ::Eigen::Product(*this, x.derived()); } - void setModel(Model &model); - Model &model() const; - const Model::KinematicGraph &graph() const; + void setModel(Model &model); + Model &model() const; - void setFreeSimulationObject(const std::vector &freeSimulationObjects); - const std::vector &freeSimulationObjects() const; + void setFreeSimulationObject( + const std::vector &freeSimulationObjects); + const std::vector &freeSimulationObjects() const; - void setForwardSortedList(const std::vector &forwardSortedList); - const std::vector &forwardSortedList() const; + void setForwardSortedList( + const std::vector &forwardSortedList); + const std::vector &forwardSortedList() const; - void setBackwardSortedList(const std::vector &backwardSortedList); - const std::vector &backwardSortedList() const; + void setBackwardSortedList( + const std::vector &backwardSortedList); + const std::vector &backwardSortedList() const; private: int m_rows; int m_cols; - Model *m_model{nullptr}; - const std::vector *m_freeSimulationObjects{nullptr}; - const std::vector *m_forwardSortedList{nullptr}; - const std::vector *m_backwardSortedList{nullptr}; + Model *m_model{nullptr}; + const std::vector *m_freeSimulationObjects{nullptr}; + const std::vector *m_forwardSortedList{nullptr}; + const std::vector *m_backwardSortedList{nullptr}; }; +template +SystemMatrix::SystemMatrix(int rows, int cols) + : m_rows(rows) + , m_cols(cols) +{ +} + +template +int SystemMatrix::rows() const +{ + return m_rows; +} + +template +int SystemMatrix::cols() const +{ + return m_cols; +} + +template +void SystemMatrix::clear() +{ +} + +template +void SystemMatrix::setForwardSortedList( + const std::vector &forwardSortedList) +{ + m_forwardSortedList = std::addressof(forwardSortedList); +} + +template +const std::vector &SystemMatrix::forwardSortedList() const +{ + return *m_forwardSortedList; +} + +template +void SystemMatrix::setBackwardSortedList( + const std::vector &backwardSortedList) +{ + m_backwardSortedList = std::addressof(backwardSortedList); +} + +template +const std::vector &SystemMatrix::backwardSortedList() + const +{ + return *m_backwardSortedList; +} + +template +void SystemMatrix::setFreeSimulationObject( + const std::vector &freeSimulationObjects) +{ + m_freeSimulationObjects = std::addressof(freeSimulationObjects); +} + +template +const std::vector &SystemMatrix::freeSimulationObjects() + const +{ + return *m_freeSimulationObjects; +} + +template +Model &SystemMatrix::model() const +{ + return *m_model; +} + +template +void SystemMatrix::setModel(Model &model) +{ + m_model = std::addressof(model); +} + } // namespace mandos::core // Implementation of MatrixReplacement * Eigen::DenseVector though a specialization of internal::generic_product_impl: namespace Eigen::internal { -template -struct generic_product_impl // GEMV stands for - // matrix-vector - : generic_product_impl_base +struct generic_product_impl, + Rhs, + SparseShape, + DenseShape, + GemvProduct> // GEMV stands for + // matrix-vector + : generic_product_impl_base, Rhs, - generic_product_impl> { - typedef typename Product::Scalar Scalar; + generic_product_impl, Rhs>> { + typedef typename Product, Rhs>::Scalar Scalar; template - static void scaleAndAddTo(Dest &dst, const mandos::core::SystemMatrix &lhs, const Rhs &rhs, const Scalar &alpha) + static void scaleAndAddTo(Dest &dst, + const mandos::core::SystemMatrix &lhs, + const Rhs &rhs, + const Scalar &alpha) { ZoneScopedN("H*dx"); + + auto ®istry = lhs.model().registry(); // This method should implement "dst += alpha * lhs * rhs" inplace, // however, for iterative solvers, alpha is always equal to 1, so let's not bother about it. assert(alpha == Scalar(1) && "scaling is not implemented"); EIGEN_ONLY_USED_FOR_DEBUG(alpha); // Clear the gradient so we can store there the dx - for (auto node : lhs.forwardSortedList()) { - std::visit( - [&lhs](auto handle) { - auto &simulationObject = lhs.model().simulationObject(handle); - simulationObject.mstate.clearGradient(); - }, - lhs.graph()[node]); + { + ZoneScopedN("Clear"); + for (auto node : lhs.forwardSortedList()) { + std::visit( + [®istry](mandos::core::SimulationObject handle) { + registry.template get>(handle.entt).clearGradient(); + }, + lhs.model().graph()[node]); + } } // Set the dx for the free SimulationObjects, applying projections if needed - int offset = 0; - for (auto node : lhs.freeSimulationObjects()) { - const auto &simObjectV = lhs.graph()[node]; - std::visit( - [&lhs, &offset, &rhs](auto handle) { - auto &simulationObject = lhs.model().simulationObject(handle); - const auto size = simulationObject.mstate.size(); - simulationObject.mstate.setGradient(rhs.segment(offset, size)); - - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasProjections) { - mandos::core::utilities::static_for_each( - [&simulationObject](const auto &projection) { - projection.applyP(simulationObject.mstate.gradientView()); - }, - simulationObject.projections); - } - - offset += size; - }, - simObjectV); + { + ZoneScopedN("P * dx"); + int offset = 0; + for (auto node : lhs.freeSimulationObjects()) { + const auto &simObjectV = lhs.model().graph()[node]; + std::visit( + [®istry, &offset, &rhs](mandos::core::SimulationObject handle) { + auto &mstate = registry.template get>(handle.entt); + const auto size = mstate.size(); + mstate.setGradient(rhs.segment(offset, size)); + + mandos::core::utilities::static_for_each>( + [®istry, &mstate, handle]() { + const auto &projection = registry.template get(handle.entt); + projection.applyP(mstate.gradientView()); + }); + + offset += size; + }, + simObjectV); + } } // Propagate the dx to mapped objects - for (auto node : lhs.forwardSortedList()) { - const auto &simObjectV = lhs.graph()[node]; - std::visit( - [&lhs](auto handle) { - const auto &simulationObject = lhs.model().simulationObject(handle); - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasMappings) { - mandos::core::utilities::static_for_each( - [&lhs, &simulationObject](const auto &mappings) { + { + ZoneScopedN("J * P * dx"); + for (auto node : lhs.forwardSortedList()) { + const auto &simObjectV = lhs.model().graph()[node]; + std::visit( + [®istry](mandos::core::SimulationObject handle) { + const auto &mstate = registry.template get>(handle.entt); + + mandos::core::utilities::static_for_each>( + [®istry, &mstate, handle]() { + using Mapping = MappingsVec::value_type; + const auto &mappings = registry.template get>(handle.entt); for (const auto &mapping : mappings) { auto to = mapping.to(); + auto &toMstate = + registry.template get>( + to.entt); - mapping.applyJ(simulationObject.mstate.m_grad, - lhs.model().simulationObject(to).mstate.m_grad); + mapping.applyJ(mstate.m_grad, toMstate.m_grad); } - }, - simulationObject.m_mappings); - } - }, - simObjectV); + }); + }, + simObjectV); + } } - // For each object, multiply by its local hessian - for (auto node : lhs.forwardSortedList()) { - std::visit( - [&lhs](auto handle) { - auto &simulationObject = lhs.model().simulationObject(handle); - simulationObject.mstate.scaleGradByHessian(); - }, - lhs.graph()[node]); + { + ZoneScopedN("H * J * P * dx"); + // For each object, multiply by its local hessian + for (auto node : lhs.forwardSortedList()) { + std::visit( + [®istry](mandos::core::SimulationObject handle) { + registry.template get>(handle.entt).scaleGradByHessian(); + }, + lhs.model().graph()[node]); + } } // And propagate back the H*dx through mappings - for (auto node : lhs.backwardSortedList()) { - const auto &simObjectV = lhs.graph()[node]; - std::visit( - [&lhs](auto handle) { - const auto &simulationObject = lhs.model().simulationObject(handle); - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasMappings) { - mandos::core::utilities::static_for_each( - [&lhs](const auto &mappings) { + { + ZoneScopedN("J.T * H * J * P * dx"); + for (auto node : lhs.backwardSortedList()) { + const auto &simObjectV = lhs.model().graph()[node]; + std::visit( + [®istry](mandos::core::SimulationObject handle) { + const auto &mstate = registry.template get>(handle.entt); + + mandos::core::utilities::static_for_each>( + [®istry, &mstate, handle]() { + using Mapping = MappingsVec::value_type; + const auto &mappings = registry.template get>(handle.entt); for (const auto &mapping : mappings) { - auto fromHandle = mapping.from(); - auto toHandle = mapping.to(); + auto from = mapping.from(); + auto to = mapping.to(); - auto &from = lhs.model().simulationObject(fromHandle); - auto &to = lhs.model().simulationObject(toHandle); + auto &fromMstate = + registry.template get>( + from.entt); + auto &toMstate = + registry.template get>( + to.entt); - mapping.applyJT(from.mstate.m_grad, to.mstate.m_grad); + mapping.applyJT(fromMstate.m_grad, toMstate.m_grad); } - }, - simulationObject.m_mappings); - } - }, - simObjectV); + }); + }, + simObjectV); + } } // And finally, accumulate on the dst, taking into account the projections if needed - offset = 0; - for (auto node : lhs.freeSimulationObjects()) { - const auto &simObjectV = lhs.graph()[node]; - std::visit( - [&lhs, &dst, &offset](auto handle) { - auto &simulationObject = lhs.model().simulationObject(handle); - const auto size = simulationObject.mstate.size(); - - using SimulationObjectT = std::remove_reference_t>; - if constexpr (SimulationObjectT::hasProjections) { - mandos::core::utilities::static_for_each( - [&simulationObject](const auto &projection) { - projection.applyPT(simulationObject.mstate.gradientView()); - }, - simulationObject.projections); - } - simulationObject.mstate.gradient(dst.segment(offset, size)); - offset += size; - }, - simObjectV); + { + ZoneScopedN("P.T * J.T * H * J * P * dx"); + int offset = 0; + for (auto node : lhs.freeSimulationObjects()) { + const auto &simObjectV = lhs.model().graph()[node]; + std::visit( + [®istry, &dst, &offset](mandos::core::SimulationObject handle) { + auto &mstate = registry.template get>(handle.entt); + const auto size = mstate.size(); + + mandos::core::utilities::static_for_each>( + [®istry, &mstate, handle]() { + const auto &projection = registry.template get(handle.entt); + projection.applyPT(mstate.gradientView()); + }); + + mstate.gradient(dst.segment(offset, size)); + offset += size; + }, + simObjectV); + } } } }; diff --git a/src/Mandos/Core/utility_functions.hpp b/src/Mandos/Core/utility_functions.hpp index d12163947f5a30cfcae112d18ea7f49779126718..7a0b7c54aad1a2f38e4022ad928211267b7f4dd3 100644 --- a/src/Mandos/Core/utility_functions.hpp +++ b/src/Mandos/Core/utility_functions.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include namespace mandos::core::utilities @@ -41,9 +41,25 @@ overloaded(Fs...) -> overloaded; template void static_for_each(F &&f, Tup &&tup) { - std::apply([f = std::forward(f)](auto &&...v) { return (f(std::forward(v)), ...); }, - std::forward(tup)); + std::apply( + [f = std::forward(f)](auto &&...v) { + if constexpr (std::tuple_size_v> != 0) { + return (f(std::forward(v)), ...); + } + }, + std::forward(tup)); } + +template +void static_for_each(F &&f) +{ + [f = std::forward(f)](std::tuple *) { + if constexpr (std::tuple_size_v != 0) { + (f.template operator()(), ...); + } + }(static_cast(nullptr)); +} + } // namespace mandos::core::utilities // /** diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 44088a89e9e82e9c0eaf9fafa111a80919dbb842..99b5415badd2b59f1529176e210913b356cc1dc1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory(MassSpring) add_subdirectory(CosseratBendingRod) add_subdirectory(CosseratRod) add_subdirectory(RotationMatrixDerivatives) +add_subdirectory(Model) diff --git a/tests/Model/CMakeLists.txt b/tests/Model/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..68b87d7606aebb15073ac61eda1f32ca7b2b4fe7 --- /dev/null +++ b/tests/Model/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(tst_Model tst_Model.cpp) +target_link_libraries( + tst_Model + PUBLIC Mandos::Core + Catch2::Catch2WithMain +) + +add_test(NAME tst_Model COMMAND tst_Model) diff --git a/tests/Model/tst_Model.cpp b/tests/Model/tst_Model.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e839dc58d5d904f0c87e8d25844747011ca4bc30 --- /dev/null +++ b/tests/Model/tst_Model.cpp @@ -0,0 +1,226 @@ +#include + +#include + +#include +#include + +#include "Mandos/Core/utility_functions.hpp" +#include +#include +#include +#include +#include +#include +#include + +TEST_CASE("Model") +{ + mandos::core::Model model; + + SECTION("Should be empty") + { + mandos::core::utilities::static_for_each( + [&](mandos::core::SimulationObject) { + auto view = model.registry().view>(); + REQUIRE(view.size() == 0); + }, + mandos::core::Kernel::SimulationObjects{}); + } + + SECTION("Add one object of each type") + { + mandos::core::Kernel::SimulationObjects simObjects; + mandos::core::utilities::static_for_each( + [&](mandos::core::SimulationObject) { + std::get>(simObjects) = model.add(); + }, + mandos::core::Kernel::SimulationObjects{}); + + mandos::core::utilities::static_for_each( + [&](mandos::core::SimulationObject) { + auto view = model.registry().view>(); + REQUIRE(view.size() == 1); + + mandos::core::SimulationObject simObject = + std::get>(simObjects); + view.each([&](auto entt, auto &mstate) { + REQUIRE(simObject.entt == entt); + REQUIRE(&simObject.mstate() == &mstate); + }); + }, + mandos::core::Kernel::SimulationObjects{}); + + SECTION("Shouldn't have a random type") + { + struct S { + int i; + }; + mandos::core::utilities::static_for_each( + [&](mandos::core::SimulationObject) { + auto view = model.registry().view>(); + view.each([&](auto handle, mandos::core::MechanicalState & /*mstate*/) { + REQUIRE(model.registry().try_get(handle) == nullptr); + }); + + model.add(); + }, + mandos::core::Kernel::SimulationObjects{}); + } + + SECTION("Should have potentials") + { + mandos::core::utilities::static_for_each( + [&](mandos::core::SimulationObject) { + auto view = model.registry().view>(); + view.each([&](auto handle, mandos::core::MechanicalState & /*mstate*/) { + mandos::core::utilities::static_for_each( + [&](C) { REQUIRE_NOTHROW(model.registry().get(handle)); }, + mandos::core::Kernel::Potentials_t{}); + }); + + model.add(); + }, + mandos::core::Kernel::SimulationObjects{}); + } + + SECTION("Should have inertias") + { + mandos::core::utilities::static_for_each( + [&](mandos::core::SimulationObject) { + auto view = model.registry().view>(); + view.each([&](auto handle, mandos::core::MechanicalState & /*mstate*/) { + mandos::core::utilities::static_for_each( + [&](C) { REQUIRE_NOTHROW(model.registry().try_get(handle)); }, + mandos::core::Kernel::Inertias_t{}); + }); + + model.add(); + }, + mandos::core::Kernel::SimulationObjects{}); + } + + SECTION("Should have colliders") + { + mandos::core::utilities::static_for_each( + [&](mandos::core::SimulationObject) { + auto view = model.registry().view>(); + view.each([&](auto handle, mandos::core::MechanicalState & /*mstate*/) { + mandos::core::utilities::static_for_each( + [&](C) { REQUIRE_NOTHROW(model.registry().get(handle)); }, + mandos::core::Kernel::Colliders_t{}); + }); + + model.add(); + }, + mandos::core::Kernel::SimulationObjects{}); + } + + SECTION("Should have projections") + { + mandos::core::utilities::static_for_each( + [&](mandos::core::SimulationObject) { + auto view = model.registry().view>(); + view.each([&](auto handle, mandos::core::MechanicalState & /*mstate*/) { + mandos::core::utilities::static_for_each( + [&](C) { REQUIRE_NOTHROW(model.registry().get(handle)); }, + mandos::core::Kernel::Projections_t{}); + }); + + model.add(); + }, + mandos::core::Kernel::SimulationObjects{}); + } + + SECTION("Should have mappings") + { + mandos::core::utilities::static_for_each( + [&](mandos::core::SimulationObject) { + auto view = model.registry().view>(); + view.each([&](auto handle, mandos::core::MechanicalState & /*mstate*/) { + mandos::core::utilities::static_for_each( + [&](C) { REQUIRE_NOTHROW(model.registry().get(handle)); }, + mandos::core::Kernel::Mappings_t{}); + }); + + model.add(); + }, + mandos::core::Kernel::SimulationObjects{}); + } + + SECTION("Should allow mappings") + { + mandos::core::utilities::static_for_each( + [&](mandos::core::SimulationObject) { + auto view = model.registry().view>(); + view.each([&](auto handle, mandos::core::MechanicalState & /*mstate*/) { + mandos::core::utilities::static_for_each( + [&](std::vector) { + auto &mappings = model.registry().get>(handle); + REQUIRE(mappings.size() == 0); + + using From = C::From; + using To = C::To; + + static_assert(std::is_same_v); + + auto toObject = model.add(); + + mandos::core::SimulationObject fromObject{handle, nullptr}; + mappings.emplace_back(fromObject, toObject); + REQUIRE(mappings.size() == 1); + }, + mandos::core::Kernel::Mappings_t{}); + }); + }, + mandos::core::Kernel::SimulationObjects{}); + } + } + + SECTION("Should allow collisions") + { + mandos::core::utilities::static_for_each( + [&]( + std::vector>) { + // Create the colliding objects + + using Tag0 = SimulationCollider0::Tag; + using Tag1 = SimulationCollider1::Tag; + using Collider0 = SimulationCollider0::Collider; + using Collider1 = SimulationCollider1::Collider; + + const auto &simObject0 = model.add(); + const auto &simObject1 = model.add(); + + const auto &mappings0 = + model.registry().get>>( + simObject0.entt); + const auto &mappings1 = + model.registry().get>>( + simObject1.entt); + + REQUIRE(mappings0.size() == 0); + REQUIRE(mappings1.size() == 0); + + SimulationCollider0 c0{simObject0, 0}; + SimulationCollider1 c1{simObject1, 0}; + + auto collisionState = + mandos::core::addCollisionPair( + model, c0, c1, 0, 0, 0, 0); + REQUIRE(mappings0.size() == 1); + REQUIRE(mappings1.size() == 1); + + REQUIRE(mappings0[0].from().entt == simObject0.entt); + REQUIRE(mappings0[0].to().entt == collisionState.entt); + + REQUIRE(mappings1[0].from().entt == simObject1.entt); + REQUIRE(mappings1[0].to().entt == collisionState.entt); + + REQUIRE(model.registry() + .get>(collisionState.entt) + .size() == 0); + }, + mandos::core::Kernel::CollisionPairs{}); + } +} \ No newline at end of file diff --git a/tests/Simulation/tst_Simulation.cpp b/tests/Simulation/tst_Simulation.cpp index 2edf193b3b48a3a165033201a869d0b207028814..edbf00406a9d7f3b4cc0d15c1791128f18be0d98 100644 --- a/tests/Simulation/tst_Simulation.cpp +++ b/tests/Simulation/tst_Simulation.cpp @@ -1,47 +1,50 @@ #include #include +#include #include #include +#include #include #include TEST_CASE("Particle3D") { - mandos::core::Model model; + mandos::core::Model model; // Add a simulation object made of 3D particles to the model auto handle = model.add(); model.commit(); - auto &simObject{model.simulationObjects()[0]}; + auto &mstate{model.registry().get>(handle.entt)}; SECTION("handle should point to object") { - REQUIRE(&handle.simulationObject() == &simObject); + REQUIRE(&handle.mstate() == &mstate); } SECTION("Should have a single simulation object") { - REQUIRE(model.simulationObjects().size() == 1); + auto view = model.registry().view>(); + REQUIRE(view.size() == 1); } SECTION("Should be unintialized") { - REQUIRE(simObject.mstate.m_x.empty()); - REQUIRE(simObject.mstate.m_v.empty()); + REQUIRE(mstate.m_x.empty()); + REQUIRE(mstate.m_v.empty()); - REQUIRE(simObject.potential().size() == 0); + REQUIRE(model.registry().get(handle.entt).size() == 0); REQUIRE(model.nDof() == 0); } // Lets adds some particles to the state // Note that modifying directly the mstate may make the object behave incorrectly, so its an unsupported practice - simObject.mstate.m_x.emplace_back(0, 0, 0); - simObject.mstate.m_x.emplace_back(1, 0, 0); - simObject.mstate.m_x.emplace_back(0, 1, 0); - simObject.mstate.m_x.emplace_back(0, 0, 1); + mstate.m_x.emplace_back(0, 0, 0); + mstate.m_x.emplace_back(1, 0, 0); + mstate.m_x.emplace_back(0, 1, 0); + mstate.m_x.emplace_back(0, 0, 1); - simObject.mstate.m_v.resize(simObject.mstate.m_x.size()); + mstate.m_v.resize(mstate.m_x.size()); SECTION("Should have 4 positions") { @@ -50,7 +53,7 @@ TEST_CASE("Particle3D") SECTION("should have no energy elements") { - auto &snh{simObject.potential()}; + auto &snh{model.registry().get(handle.entt)}; REQUIRE(snh.size() == 0); } diff --git a/tests/Utilities/tst_Utilities.cpp b/tests/Utilities/tst_Utilities.cpp index f559f27994ae135c5ffde18adc1adddac2251cbd..ffae04583fdff0f20c8a3fe776359e58ce54e0d2 100644 --- a/tests/Utilities/tst_Utilities.cpp +++ b/tests/Utilities/tst_Utilities.cpp @@ -30,6 +30,23 @@ TEST_CASE("static_for_each") REQUIRE(acc == 15); } + + SECTION("tuple without creation") + { + struct A { + int v = 7; + }; + + struct B { + int v = 8; + }; + + std::tuple t{A{}, B{}}; + auto acc = 0; + mandos::core::utilities::static_for_each>([&acc]() { acc += T{}.v; }); + + REQUIRE(acc == 15); + } } TEST_CASE("skew")