diff --git a/CMakeLists.txt b/CMakeLists.txt index de2b96d..46c2b1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,9 +2,16 @@ cmake_minimum_required(VERSION 3.12) project(Shell VERSION 1.0 LANGUAGES CXX) # Find and load CMake configuration of packages containing this plugin's dependencies -find_package(Sofa.Component.Controller REQUIRED) +find_package(Sofa.Config REQUIRED) +sofa_find_package(Sofa.Component.Controller REQUIRED) +sofa_find_package(Sofa.Component.Topology.Container.Dynamic REQUIRED) sofa_find_package(Sofa.Component.StateContainer REQUIRED) +sofa_find_package(Sofa.Component.Mapping.Linear REQUIRED) +sofa_find_package(Sofa.GL REQUIRED) +set(README_FILE README.md) + +option(SOFA-PLUGIN_SHELLS_ADAPTIVITY "Enables shells adaptivity" OFF) # List all files set(SHELL_SRC_DIR src/Shell) @@ -13,32 +20,102 @@ set(HEADER_FILES ${SHELL_SRC_DIR}/controller/MeshChangedEvent.h ${SHELL_SRC_DIR}/controller/MeshInterpolator.h ${SHELL_SRC_DIR}/controller/MeshInterpolator.inl + ${SHELL_SRC_DIR}/controller/TriangleSwitchExample.h + ${SHELL_SRC_DIR}/controller/TriangleSwitchExample.inl ${SHELL_SRC_DIR}/engine/JoinMeshPoints.h ${SHELL_SRC_DIR}/engine/JoinMeshPoints.inl + ${SHELL_SRC_DIR}/engine/FindClosePoints.h + ${SHELL_SRC_DIR}/engine/FindClosePoints.inl + ${SHELL_SRC_DIR}/forcefield/BezierTriangularBendingFEMForceField.h + ${SHELL_SRC_DIR}/forcefield/BezierTriangularBendingFEMForceField.inl + ${SHELL_SRC_DIR}/forcefield/CstFEMForceField.h + ${SHELL_SRC_DIR}/forcefield/CstFEMForceField.inl ${SHELL_SRC_DIR}/forcefield/TriangularBendingFEMForceField.h ${SHELL_SRC_DIR}/forcefield/TriangularBendingFEMForceField.inl + ${SHELL_SRC_DIR}/forcefield/TriangularShellForceField.h + ${SHELL_SRC_DIR}/forcefield/TriangularShellForceField.inl + ${SHELL_SRC_DIR}/mapping/BendingPlateMechanicalMapping.h + ${SHELL_SRC_DIR}/mapping/BendingPlateMechanicalMapping.inl + ${SHELL_SRC_DIR}/mapping/BezierTriangleMechanicalMapping.h + ${SHELL_SRC_DIR}/mapping/BezierTriangleMechanicalMapping.inl + ${SHELL_SRC_DIR}/misc/PointProjection.h + ${SHELL_SRC_DIR}/misc/PointProjection.inl + ${SHELL_SRC_DIR}/shells2/fem/BezierShellInterpolation.h + ${SHELL_SRC_DIR}/shells2/fem/BezierShellInterpolation.inl + ${SHELL_SRC_DIR}/shells2/fem/BezierShellInterpolationM.h + ${SHELL_SRC_DIR}/shells2/fem/BezierShellInterpolationM.inl + ${SHELL_SRC_DIR}/shells2/forcefield/BezierShellForceField.h + ${SHELL_SRC_DIR}/shells2/forcefield/BezierShellForceField.inl + ${SHELL_SRC_DIR}/shells2/mapping/BezierShellMechanicalMapping.h + ${SHELL_SRC_DIR}/shells2/mapping/BezierShellMechanicalMapping.inl ) + set(SOURCE_FILES ${SHELL_SRC_DIR}/initShell.cpp ${SHELL_SRC_DIR}/controller/MeshChangedEvent.cpp ${SHELL_SRC_DIR}/controller/MeshInterpolator.cpp + ${SHELL_SRC_DIR}/controller/TriangleSwitchExample.cpp ${SHELL_SRC_DIR}/engine/JoinMeshPoints.cpp + ${SHELL_SRC_DIR}/engine/FindClosePoints.cpp + ${SHELL_SRC_DIR}/forcefield/BezierTriangularBendingFEMForceField.cpp + ${SHELL_SRC_DIR}/forcefield/CstFEMForceField.cpp ${SHELL_SRC_DIR}/forcefield/TriangularBendingFEMForceField.cpp + ${SHELL_SRC_DIR}/forcefield/TriangularShellForceField.cpp + ${SHELL_SRC_DIR}/mapping/BendingPlateMechanicalMapping.cpp + ${SHELL_SRC_DIR}/mapping/BezierTriangleMechanicalMapping.cpp + ${SHELL_SRC_DIR}/misc/PointProjection.cpp + ${SHELL_SRC_DIR}/shells2/fem/BezierShellInterpolation.cpp + ${SHELL_SRC_DIR}/shells2/fem/BezierShellInterpolationM.cpp + ${SHELL_SRC_DIR}/shells2/forcefield/BezierShellForceField.cpp + ${SHELL_SRC_DIR}/shells2/mapping/BezierShellMechanicalMapping.cpp ) -set(README_FILES - README.md -) -# Create the plugin library. +if(SOFA-PLUGIN_SHELLS_ADAPTIVITY) + set(COMPILER_DEFINE "SOFA_BUILD_SHELLS_ADAPTIVITY") + + list(APPEND HEADER_FILES + ${SHELL_SRC_DIR}/controller/AdaptiveCuttingController.h + ${SHELL_SRC_DIR}/controller/AdaptiveCuttingController.inl + ${SHELL_SRC_DIR}/controller/Test2DAdapter.h + ${SHELL_SRC_DIR}/controller/Test2DAdapter.inl + ${SHELL_SRC_DIR}/misc/Optimize2DSurface.h + ${SHELL_SRC_DIR}/misc/Optimize2DSurface.inl + ${SHELL_SRC_DIR}/misc/SurfaceParametrization.h + ${SHELL_SRC_DIR}/misc/SurfaceParametrization.inl + ) + + list(APPEND SOURCE_FILES + ${SHELL_SRC_DIR}/controller/AdaptiveCuttingController.cpp + ${SHELL_SRC_DIR}/controller/Test2DAdapter.cpp + ${SHELL_SRC_DIR}/misc/Optimize2DSurface.cpp + ${SHELL_SRC_DIR}/misc/SurfaceParametrization.cpp + ) + + if(SofaGui_FOUND AND SofaOpenglVisual_FOUND) + list(APPEND HEADER_FILES + ${SHELL_SRC_DIR}/cutting/AdaptiveCutting.h + ) + + list(APPEND SOURCE_FILES + ${SHELL_SRC_DIR}/cutting/AdaptiveCutting.cpp + ) + endif() + +endif() + + +# Create the plugin library add_library(${PROJECT_NAME} SHARED ${HEADER_FILES} ${SOURCE_FILES} ${README_FILES}) # Link the plugin library to its dependency(ies). -target_link_libraries(${PROJECT_NAME} Sofa.Component.Controller Sofa.Component.StateContainer) +target_link_libraries(${PROJECT_NAME} + Sofa.Component.Controller + Sofa.Component.Topology.Container.Dynamic + Sofa.Component.StateContainer + Sofa.Component.Mapping.Linear + Sofa.GL +) -# Create package Config, Version & Target files. -# Deploy the headers, resources, scenes & examples. -# Set the plugin 'relocatable' if built within SOFA. -# --> see SofaMacros.cmake sofa_create_package_with_targets( PACKAGE_NAME ${PROJECT_NAME} PACKAGE_VERSION ${PROJECT_VERSION} diff --git a/examples/sofapython3/SceneCochlea/__pycache__/Scene_Cochlea.cpython-311.pyc b/examples/sofapython3/SceneCochlea/__pycache__/Scene_Cochlea.cpython-311.pyc new file mode 100644 index 0000000..3e07ce2 Binary files /dev/null and b/examples/sofapython3/SceneCochlea/__pycache__/Scene_Cochlea.cpython-311.pyc differ diff --git a/examples/xml/ShellTest.scn b/examples/xml/ShellTest.scn index 95a6ac0..9af6fcf 100644 --- a/examples/xml/ShellTest.scn +++ b/examples/xml/ShellTest.scn @@ -27,11 +27,12 @@ - + - + + diff --git a/src/Shell/controller/AdaptiveCuttingController.cpp b/src/Shell/controller/AdaptiveCuttingController.cpp new file mode 100644 index 0000000..229465f --- /dev/null +++ b/src/Shell/controller/AdaptiveCuttingController.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + + +namespace sofa +{ + +namespace component +{ + +namespace controller +{ + +using namespace sofa::defaulttype; + +SOFA_DECL_CLASS(AdaptiveCuttingController) + +// Register in the Factory +int AdaptiveCuttingControllerClass = core::RegisterObject( + "Controller that handles the cutting method based on mesh adaptivity.") +#ifdef SOFA_FLOAT +.add< AdaptiveCuttingController >(true) // default template +#else +.add< AdaptiveCuttingController >(true) // default template +# ifndef SOFA_DOUBLE +.add< AdaptiveCuttingController >() +# endif +#endif +; + +#ifndef SOFA_FLOAT +template class SOFA_SHELLS_API AdaptiveCuttingController; +#endif //SOFA_FLOAT +#ifndef SOFA_DOUBLE +template class SOFA_SHELLS_API AdaptiveCuttingController; +#endif //SOFA_DOUBLE + +} // namespace controller + +} // namespace component + +} // namespace sofa diff --git a/src/Shell/controller/AdaptiveCuttingController.h b/src/Shell/controller/AdaptiveCuttingController.h new file mode 100644 index 0000000..b1e19a9 --- /dev/null +++ b/src/Shell/controller/AdaptiveCuttingController.h @@ -0,0 +1,169 @@ +#ifndef SOFA_COMPONENT_CONTROLLER_ADAPTIVECUTTINGCONTROLLER_H +#define SOFA_COMPONENT_CONTROLLER_ADAPTIVECUTTINGCONTROLLER_H + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace sofa +{ + +namespace component +{ + +namespace controller +{ + +/// Class to shield the data type +class CuttingAdapter +{ +public: + virtual void setTrackedPoint(const collision::BodyPicked &picked) = 0; + virtual void freeTrackedPoint() = 0; + virtual void addCuttingPoint() = 0; +}; + +template +class AdaptiveCuttingController : public Controller, public CuttingAdapter +{ +public: + SOFA_CLASS(SOFA_TEMPLATE(AdaptiveCuttingController,DataTypes),Controller); + + typedef typename DataTypes::Coord Coord; + typedef typename DataTypes::VecCoord VecCoord; + //typedef typename DataTypes::Deriv Deriv; + //typedef typename DataTypes::VecDeriv VecDeriv; + typedef typename Coord::value_type Real; + + typedef sofa::type::Vec<2, Real> Vec2; + typedef sofa::type::Vec<3, Real> Vec3; + //typedef sofa::type::Mat<2,2,Real> Mat22; + //typedef sofa::type::Mat<3,3,Real> Mat33; + //typedef type::vector VecVec2; + //typedef type::vector VecVec3; + + + typedef sofa::core::topology::BaseMeshTopology::Edge Edge; + typedef sofa::core::topology::BaseMeshTopology::EdgesAroundVertex EdgesAroundVertex; + typedef sofa::component::topology::TriangleSetTopologyContainer::TriangleID Index; + typedef sofa::component::topology::TriangleSetTopologyContainer::Triangle Triangle; + typedef sofa::component::topology::TriangleSetTopologyContainer::TrianglesAroundVertex TrianglesAroundVertex; + typedef sofa::component::topology::TriangleSetTopologyContainer::TrianglesAroundEdge TrianglesAroundEdge; + typedef sofa::component::topology::TriangleSetTopologyContainer::EdgesInTriangle EdgesInTriangle; + typedef sofa::type::vector VecIndex; + + enum { InvalidID = sofa::core::topology::Topology::InvalidID }; + + /// @brief If geometric functinal drops below this value the attached node + /// is dropped. + Data m_affinity; + + virtual void init(); + virtual void reinit(); + + virtual std::string getTemplateName() const + { + return templateName(this); + } + + static std::string templateName(const AdaptiveCuttingController* = NULL) + { + return DataTypes::Name(); + } + + void onEndAnimationStep(const double dt); + //void onKeyPressedEvent(core::objectmodel::KeypressedEvent *key); + + void draw(const core::visual::VisualParams* vparams); + + + void setTrackedPoint(const collision::BodyPicked &picked); + void freeTrackedPoint() { + // Detach the point + m_pointId = InvalidID; + // Stop cutting + m_cutPoints = 0; + m_cutEdge = InvalidID; + } + void addCuttingPoint(); + + /// Whether the cutting is in progress or not. + bool cutting() { return m_cutPoints > 0; } + +protected: + + AdaptiveCuttingController(); + + +private: + + Test2DAdapter* m_adapter; + sofa::component::topology::TriangleSetTopologyContainer* m_container; + sofa::component::topology::TriangleSetGeometryAlgorithms *m_algoGeom; + sofa::component::topology::TriangleSetTopologyAlgorithms *m_algoTopo; + sofa::core::behavior::MechanicalState* m_state; + + // TODO: This should go to cutting config (maybe?) + bool autoCutting; + + + /// Closest point in the mstate. + Index m_pointId; + /// A point on a surface to attract to (valid only if m_pointId != InvalidID). + Vec3 m_point; + /// Position of m_point projected into rest shape. + Vec3 m_pointRest; + /// @brief Triangle ID inside which m_point is located (valid only if + ///m_pointId != InvalidID). + Index m_pointTriId; + /// @brief Number of iterations during which the attached node will not be + /// reattached. + unsigned int m_gracePeriod; + + /// @brief Stored index of the first edge to cut when the first cut has + /// been delayed. + Index m_cutEdge; + /// Last cutting point. + Index m_cutLastPoint; + /// Cutting operation to perform in this step. + VecIndex m_cutList; + /// Number of cut points defined. + int m_cutPoints; + + void switchPoint(const Vec3 &newPoint, const Index newPointTri, + const Index newID, const Index newCutEdge); + + /** + * Set edge planned for the next cut. + * + * @param newCutEdge Index of new edge to use. + * @param bKeepProtection Whether to keep edge protection for previous + * edge. + */ + void setCutEdge(const Index newCutEdge, const bool bKeepProtection=false); + +}; + + +} // namespace controller + +} // namespace component + +} // namespace sofa + +#endif // #ifndef SOFA_COMPONENT_CONTROLLER_ADAPTIVECUTTINGCONTROLLER_H diff --git a/src/Shell/controller/AdaptiveCuttingController.inl b/src/Shell/controller/AdaptiveCuttingController.inl new file mode 100644 index 0000000..671ad0a --- /dev/null +++ b/src/Shell/controller/AdaptiveCuttingController.inl @@ -0,0 +1,509 @@ +#ifndef SOFA_COMPONENT_CONTROLLER_ADAPTIVECUTTINGCONTROLLER_INL +#define SOFA_COMPONENT_CONTROLLER_ADAPTIVECUTTINGCONTROLLER_INL + +// TODO +// - protect/unprotect cut edges (m_cutEdge,m_cutList) + +#include + +#include +#include + +#include +#include +#include + +#define OTHER(x, a, b) ((x == a) ? b : a) + +// Return non-zero if triangle with points (a,b,c) is defined in +// counter-clockwise direction. +// NOTE: Constrained to 2D! +#define CCW(a,b,c) (\ + cross(Vec2(b[0]-a[0], b[1]-a[1]), \ + Vec2(c[0]-a[0], c[1]-a[1])) > 1e-15) + +// Test for intersection between two segments (a,b) and (c,d) +#define INTERSECT(a,b,c,d) (\ + (CCW(a,c,d) != CCW(b,c,d)) && (CCW(a,b,c) != CCW(a,b,d))) + +namespace sofa +{ + +namespace component +{ + +namespace controller +{ + +template +AdaptiveCuttingController::AdaptiveCuttingController() +: m_affinity(initData(&m_affinity, (Real)0.7, "affinity", "Threshold for point attachment (value betwen 0 and 1).")) +, autoCutting(false) +, m_pointId(InvalidID) +, m_pointTriId(InvalidID) +, m_gracePeriod(0) +, m_cutEdge(InvalidID) +, m_cutLastPoint(InvalidID) +, m_cutPoints(0) +{ +} + +template +void AdaptiveCuttingController::init() +{ + this->getContext()->get(m_adapter); + if (m_adapter == NULL) { + msg_error() << "Unable to find Test2DAdapter component"; + return; + } + + m_state = dynamic_cast*> (this->getContext()->getMechanicalState()); + if (!m_state) { + msg_error() << "Unable to find MechanicalState"; + return; + } + + this->getContext()->get(m_container); + if (m_container == NULL) { + msg_error() << "Unable to find triangular topology"; + return; + } + + this->getContext()->get(m_algoGeom); + if (m_algoGeom == NULL) { + msg_error() << "Unable to find TriangleSetGeometryAlgorithms"; + return; + } + + this->getContext()->get(m_algoTopo); + if (m_algoTopo == NULL) { + msg_error() << "Unable to find TriangleSetTopologyAlgorithms"; + return; + } + + reinit(); +} + +template +void AdaptiveCuttingController::reinit() +{ + *this->f_listening.beginEdit() = true; + this->f_listening.endEdit(); + + if (m_affinity.getValue() < 0.0 || m_affinity.getValue() > 1.0) { + msg_error() << "Affinity must be between 0 and 1."; + *m_affinity.beginEdit() = 0.7; + m_affinity.endEdit(); + } + +} + + +template +void AdaptiveCuttingController::onEndAnimationStep(const double /*dt*/) +{ + if (m_gracePeriod > 0) m_gracePeriod--; + + const VecCoord& x0 = m_state->read( + sofa::core::ConstVecCoordId::restPosition())->getValue(); + + // Perform the (delayed) cutting + if ((m_cutList.size() > 0) && (m_cutPoints > 2) ) { + VecIndex newList, endList; + // First remove protection + for (VecIndex::const_iterator i=m_cutList.begin(); + i != m_cutList.end(); i++) { + m_adapter->unprotectEdge(*i); + } + // Then perform the incision + bool bReachedBorder; + m_algoTopo->InciseAlongEdgeList(m_cutList, newList, endList, + bReachedBorder); + //m_algoTopo->InciseAlongEdge(m_cutList[0], NULL); + m_cutList.clear(); + } + + // Check the values around attached point and reattach if necessary + if ((m_pointId != InvalidID) && !m_gracePeriod && !cutting()) { + TrianglesAroundVertex N1 = + m_container->getTrianglesAroundVertex(m_pointId); + Real min = 1.0; + for (unsigned int it=0; itmetricGeom( + m_container->getTriangle(N1[it]), N1[it]); + if (f < min) min = f; + } + if (min < m_affinity.getValue()) { + // Value too low, try reattaching the node + m_gracePeriod = 20; + Triangle t = m_container->getTriangle(m_pointTriId); + VecIndex pts; + for (int i=0; i<3; i++) { + if (t[i] != m_pointId) pts.push_back(t[i]); + } + if ((x0[m_pointId] - x0[ pts[0] ]).norm2() < + (x0[m_pointId] - x0[ pts[1] ]).norm2()) { + m_pointId = pts[0]; + } else { + m_pointId = pts[1]; + } + } + } + + +} + +template +void AdaptiveCuttingController::draw( + const core::visual::VisualParams* vparams) +{ + //if ((!vparams->displayFlags().getShowBehaviorModels())) + return; + + if (!m_state) return; + const VecCoord& x = m_state->read(sofa::core::ConstVecCoordId::position())->getValue(); + + if (m_cutList.size() > 0) { + type::vector points; + for (VecIndex::const_iterator i=m_cutList.begin(); + i != m_cutList.end(); i++) { + const Edge &e = m_container->getEdge(*i); + points.push_back(x[ e[0] ]); + points.push_back(x[ e[1] ]); + } + vparams->drawTool()->drawLines(points, 4, + type::RGBAColor(1.0, 0.0, 0.0, 1.0)); + } + + if (m_cutEdge != InvalidID) { + const Edge &e = m_container->getEdge(m_cutEdge); + type::vector points; + points.push_back(x[ e[0] ]); + points.push_back(x[ e[1] ]); + vparams->drawTool()->drawLines(points, 4, + type::RGBAColor(1.0, 0.0, 0.0, 1.0)); + } +} + + +template +void AdaptiveCuttingController::setTrackedPoint( + const collision::BodyPicked &picked) +{ + if (!m_adapter) return; + + using namespace sofa::component::collision; + + //const VecCoord& x0 = m_state->read( + // sofa::core::ConstVecCoordId::restPosition())->getValue(); + const VecCoord& x = m_state->read( + sofa::core::ConstVecCoordId::position())->getValue(); + + + // Support only trianglular model! The others don't give any added value. + /*if(dynamic_cast(picked.body)) { + // Point + m_pointId = picked.indexCollisionElement; + } else if(dynamic_cast(picked.body)) { + // Edge + Edge e = m_container->getEdge(picked.indexCollisionElement); + Real d1 = (x[ e[0] ] - m_point).norm2(); + Real d2 = (x[ e[1] ] - m_point).norm2(); + m_pointId = (d1 < d2 ? e[0] : e[1]); + } else*/ + if(dynamic_cast(picked.body)) { + + Index newId = InvalidID; + Index newCutEdge = InvalidID; + + if (!cutting()) { + Triangle t = m_container->getTriangle( + picked.indexCollisionElement); + Real d1 = (x[ t[0] ] - picked.point).norm2(); + Real d2 = (x[ t[1] ] - picked.point).norm2(); + Real d3 = (x[ t[2] ] - picked.point).norm2(); + newId = (d1 < d2) ? + (d1 < d3 ? t[0] : t[2]) : + (d2 < d3 ? t[1] : t[2]); + } else { + // During cutting we should allow change only to point directly + // connected with last cut point. + // We pick a point from N1 shell which is closest to the tracked point. + + // NOTE: We don't allow fixed/boundary nodes (now) + + + Triangle t = m_container->getTriangle( + picked.indexCollisionElement); + if ((t[0] == m_cutLastPoint) || (t[1] == m_cutLastPoint) || + (t[2] == m_cutLastPoint)) { + // The point is inside a triangle in N1-ring, take one of it's + // corner nodes + Index pt1 = OTHER(m_cutLastPoint, t[0], t[1]), + pt2 = OTHER(m_cutLastPoint, t[2], t[1]); + + + if (!m_adapter->isPointNormal(pt1)) { + if (!m_adapter->isPointNormal(pt2)) { + // No point available! + newId = InvalidID; + } else { + newId = pt2; + } + } else if (!m_adapter->isPointNormal(pt2)) { + newId = pt1; + } else if ( + (x[pt1] - picked.point).norm2() < + (x[pt2] - picked.point).norm2()) { + newId = pt1; + } else { + newId = pt2; + } + + } else { + + // Tracked position is outside the N1-ring. Pick closest point + // while checking for crossing segments -- between last cut + // edge and (seleted-picked). Idealy we should check with all + // cut edges, but that's too much work. + + EdgesAroundVertex N1e = + m_container->getEdgesAroundVertex(m_cutLastPoint); + + Index lastCut = InvalidID; + Edge ce(InvalidID, InvalidID); + if (m_cutList.size() > 0) { + lastCut = m_cutList.back(); + ce = m_container->getEdge(lastCut); + } + + Real minDist = DBL_MAX; + + for (Index ie=0; iegetEdge(N1e[ie]); + Index otherPt = OTHER(m_cutLastPoint, e[0], e[1]); + + if (!m_adapter->isPointNormal(otherPt)) + continue; + + Real dist = (picked.point - x[otherPt]).norm2(); + bool inter = false; + if (lastCut != InvalidID) { + inter = INTERSECT(x[ce[0]], x[ce[1]], + x[otherPt], picked.point); + } + if ((dist < minDist) && !inter) { + minDist = dist; + newId = otherPt; + newCutEdge = N1e[ie]; + } + } + + } + if (newId != InvalidID) { + newCutEdge = m_container->getEdgeIndex(m_cutLastPoint, newId); + } + } + + if (newId == InvalidID) { + msg_error() << "Failed to pick a point!"; + } else { + switchPoint(picked.point, picked.indexCollisionElement, newId, + newCutEdge); + } + } else { + freeTrackedPoint(); + } + + + // Add new cut point during automated cutting + if (cutting() && autoCutting) { + // Compare edge lengths in triangles sharing the cut edge. + + TrianglesAroundEdge tris = + m_container->getTrianglesAroundEdge(m_cutEdge); + Real edgeSum = 0.0; + int edgeCount = 0; + + for (int i=0; i<2; i++) { + EdgesInTriangle elist = m_container->getEdgesInTriangle(tris[i]); + for (int j=0; j<3; j++) { + if (elist[j] == m_cutEdge) continue; + Edge e = m_container->getEdge(elist[j]); + edgeSum += (x[e[0]] - x[e[1]]).norm2(); + edgeCount++; + } + } + + // Compute mean of (squared) edge lengths + if (edgeCount > 0) { + edgeSum /= edgeCount; + + if (edgeSum < (x[m_cutLastPoint] - x[m_pointId]).norm2()) { + addCuttingPoint(); + } + } + } +} + +template +void AdaptiveCuttingController::addCuttingPoint() +{ + if (!m_adapter) return; + if (!m_algoTopo || !m_algoGeom) return; + + if (m_pointId == InvalidID) { + msg_error() << "BUG! Attempted cutting with no point tracked."; + return; + } + + const VecCoord& x = m_state->read( + sofa::core::ConstVecCoordId::position())->getValue(); + //const VecCoord& xrest = m_state->read( + // sofa::core::ConstVecCoordId::restPosition())->getValue(); + Coord oldpos = x[m_pointId]; + + bool bFirst = !cutting(); + + // Check if the target location is in one of the triangles connected to + // poin m_pointId. + bool bConnected = false; + Triangle t = m_container->getTriangle(m_pointTriId); + for (int i=0; i<3; i++) { + if (t[i] == m_pointId) { + bConnected = true; + break; + } + } + + // Make sure the tracked point is at the target location. + if (!bConnected) { + // NOTE: We can try adding the cut point as far as possible, then + // reattach and repeat. But we risk "eating" up all available + // points quitckly. Another possibility is waiting a few iterations + // before adding another cut point, but this will only work if the + // cut point is not last (end of the cut). + msg_error() << "Failed to insert cut point! Tracking too slow."; + return; + } else if (m_adapter->isPointNormal(m_pointId)) { + m_adapter->relocatePoint(m_pointId, m_point, m_pointTriId, false); + } else { + // TODO: We need a parameter controling how close one has to be to the + // boundary/fixed node to attach to it. For boundary nodes we have to + // project the target position onto the boundary nodes. Fixed points, + // of course, have to be kept intact. Or alternatively we may prevent + // attachment to fixed nodes completely. + msg_error() << "Handling of boundary/fixed nodes is not implemented!"; + } + + + // Chose another point in the direction of movement. + // TODO: the following is not good + Vec3 dir = m_point - oldpos; + dir.normalize(); + // END_TODO + + // Get another point in that direction. + Index tId = m_algoGeom->getTriangleInDirection(m_pointId, dir); + if (tId == InvalidID) { + msg_error() << "BUG! Nothing in cutting direction!"; + return; + } + + EdgesInTriangle elist = m_container->getEdgesInTriangle(tId); + Real alpha[2]; + Index otherPt[2], otherEdge[2]; + for (int i=0,j=0; i<3; i++) { + Edge e = m_container->getEdge(elist[i]); + if ((e[0] != m_pointId) && (e[1] != m_pointId)) continue; + Vec3 edgeDir; + if (e[0] == m_pointId) { + otherPt[j] = e[1]; + edgeDir = x[ e[1] ] - x[ e[0] ]; + } else { + otherPt[j] = e[0]; + edgeDir = x[ e[0] ] - x[ e[1] ]; + } + otherEdge[j] = elist[i]; + edgeDir.normalize(); + alpha[j] = dir * edgeDir; + j++; + } + + Index newPt = (alpha[0] < alpha[1]) ? otherPt[1] : otherPt[0]; + Index oldPt = m_pointId; + Index edge = (alpha[0] < alpha[1]) ? otherEdge[1] : otherEdge[0]; + + m_cutPoints++; + + // Attach the new point and move it to be near the cursor. + m_pointId = newPt; + m_pointTriId = tId; + m_gracePeriod = 20; + m_adapter->relocatePoint(newPt, + x[oldPt] + (dir*m_adapter->getPrecision()/2.0), + tId, false); + + m_cutLastPoint = oldPt; + if (!bFirst && (m_cutEdge != InvalidID)) { + m_cutList.push_back(m_cutEdge); + } + setCutEdge(edge, true); + // Fix the point so nothing happens to it before the topological change + // occurs. + m_adapter->setPointFixed(oldPt); +} + +template +void AdaptiveCuttingController::switchPoint(const Vec3 &newPoint, + const Index newPointTri, const Index newId, const Index newCutEdge) +{ + if (newId == InvalidID) { + freeTrackedPoint(); + return; + } + + if (!m_gracePeriod && (newId != m_pointId)) { + m_gracePeriod = 5; // NOTE: Don't put too large value here, or we + // will fail to follow quick changes. + m_pointId = newId; + if (newCutEdge != InvalidID) { + setCutEdge(newCutEdge); + } + } + //if (newId == m_pointId) { + m_point = newPoint; + m_pointTriId = newPointTri; + //} + + m_adapter->setPointAttraction(m_pointId, m_point, m_pointTriId); + +} + +template +void AdaptiveCuttingController::setCutEdge(const Index newCutEdge, + const bool bKeepProtection) +{ + if (!m_adapter) return; + + if (!bKeepProtection) { + m_adapter->unprotectEdge(m_cutEdge); + } + m_adapter->protectEdge(newCutEdge); + + m_cutEdge = newCutEdge; +} + +} // namespace controller + +} // namespace component + +} // namespace sofa + + +#undef OTHER +#undef CCW +#undef INTERSECT + + +#endif // #ifndef SOFA_COMPONENT_CONTROLLER_ADAPTIVECUTTINGCONTROLLER_INL diff --git a/src/Shell/controller/CudaTest2DAdapter.cpp b/src/Shell/controller/CudaTest2DAdapter.cpp new file mode 100644 index 0000000..26fce42 --- /dev/null +++ b/src/Shell/controller/CudaTest2DAdapter.cpp @@ -0,0 +1,46 @@ + +#include +#include +#include +#include + + +namespace sofa +{ + +namespace component +{ + +namespace controller +{ + +template class Test2DAdapter; +//#ifdef SOFA_GPU_CUDA_DOUBLE +//template class Test2DAdapter; +//#endif // SOFA_GPU_CUDA_DOUBLE + +} // namespace controller + +} // namespace component + +namespace gpu +{ + +namespace cuda +{ + +SOFA_DECL_CLASS(CudaTest2DAdapter) + +// Register in the Factory +int CudaTest2DAdapterClass = core::RegisterObject("Adaptive mesh improvement component for 2D triangular meshes (for testing) -- the CUDA version") +.add< component::controller::Test2DAdapter >() +//#ifdef SOFA_GPU_CUDA_DOUBLE +//.add< component::controller::Test2DAdapter >() +//#endif // SOFA_GPU_CUDA_DOUBLE +; + +} // namespace cuda + +} // namespace gpu + +} // namespace sofa diff --git a/src/Shell/controller/CudaTest2DAdapter.cu b/src/Shell/controller/CudaTest2DAdapter.cu new file mode 100644 index 0000000..3852824 --- /dev/null +++ b/src/Shell/controller/CudaTest2DAdapter.cu @@ -0,0 +1,613 @@ +#include +#include +#include +//#include + +#if defined(__cplusplus) && CUDA_VERSION < 2000 +namespace sofa +{ +namespace gpu +{ +namespace cuda +{ +#endif + +extern "C" +{ +void Test2DAdapterCuda3f_computeTriangleNormal(unsigned int size, const void* x, void* tri); +void Test2DAdapterCuda3f_functionalGeom(unsigned int size, const void* x, void* tri); +void Test2DAdapterCuda3f_reduceStep(unsigned int size, void* x, void* pt, void* indices); +void Test2DAdapterCuda3f_restoreUnchanged(unsigned int size, void* x, void* pt, void* indices); +void Test2DAdapterCuda3f_smooth(unsigned int size, void* x, void* tri, void* pt, void* indices); +void Test2DAdapterCuda3f_testAcceptable(unsigned int size, void* x, const void* tri, void* pt, void* indices, float tolerance); +///// parallel +void Test2DAdapterCuda3f_prepareGradients(unsigned int size, const void* x, void* tri); +void Test2DAdapterCuda3f_smoothParallel(unsigned int size, void* x, const void* tri, void* pt); +void Test2DAdapterCuda3f_reduceStepP(unsigned int size, void* x, void* pt); +void Test2DAdapterCuda3f_testAcceptableP(unsigned int size, void* x, const void* tri, void* pt, float tolerance); +void Test2DAdapterCuda3f_restoreUnchangedP(unsigned int size, void* x, void* pt); +//#ifdef SOFA_GPU_CUDA_DOUBLE +//void Test2DAdapterCuda3d_computeTriangleNormal(const void* x, const void* n); +//#endif +}// extern "C" + +typedef unsigned int Index; + +// NOTE: must be equivalent to the Test2DAdapterData::TriangleData +template +struct TriangleData { + Index nodes[3]; + CudaVec3 normal; + Real functional; + CudaVec3 gradient[3]; +}; + +// NOTE: must be equivalent to the Test2DAdapterData::PointData +template +struct PointData { + bool bBoundary; + + unsigned int nNeighboursPt; + const Index *neighboursPt; + + unsigned int nNeighboursTri; + const Index *neighboursTri; + + bool bAccepted; /// New position has been accepted in current step. + CudaVec3 oldpos; + Real oldworst; + Real newworst; + + Index mintri; + CudaVec3 grad; +}; + +////////////////////// +// GPU-side methods // +////////////////////// + +template +__device__ CudaVec3 computeTriangleNormal(const CudaVec3* x, + const Index nodes[3]) +{ + CudaVec3 A, B; + A = x[ nodes[1] ] - x[ nodes[0] ]; + B = x[ nodes[2] ] - x[ nodes[0] ]; + + CudaVec3 normal = CudaVec3::make(0.0, 0.0, 0.0); + + Real An = invnorm(A), Bn = invnorm(B); + if (An > 1e-20 && Bn > 1e-20) { + A = A*An; + B = B*Bn; + normal = cross(A, B); + normal = normal * invnorm(normal); + } + + return normal; +} + +template +__device__ Real getMinFunc(Index v, const TriangleData* tri, + const PointData* pt) +{ + unsigned int nElem = pt[v].nNeighboursTri; + Real value = 1.0; + // TODO: do some unrolling? + for (Index it=0; it tri[ pt[v].neighboursTri[it] ].functional) { + value = tri[ pt[v].neighboursTri[it] ].functional; + } + } + + return value; +} + +template +__device__ Real functionalGeom(const Index t, + const CudaVec3* x, const TriangleData* tri) +{ + // TODO: move outside, pass nodes as arguments + + // TODO: we can precompute these, is it worth it? + CudaVec3 ab = x[ tri[t].nodes[1] ] - x[ tri[t].nodes[0] ]; + CudaVec3 ca = x[ tri[t].nodes[0] ] - x[ tri[t].nodes[2] ]; + CudaVec3 cb = x[ tri[t].nodes[1] ] - x[ tri[t].nodes[2] ]; + + // Normalizing factor so that the value is 1 in maximum + // TODO: does compiler precompute this? NOTE: is float + Real m = 2 * sqrt(3.0f); + + m *= norm(cross(ca,cb)); // || CA × CB || + m /= norm2(ca) + norm2(ab) + norm2(cb); + + // Is triangle inverted? + CudaVec3 nnew = computeTriangleNormal(x, tri[t].nodes); + if (dot(nnew, tri[t].normal) < 0.0) { + m *= -1.0; + } + + return m; +} + +template +__device__ __inline__ Index translateIndexInTriangle(Index index, + const TriangleData &tri) +{ + if (tri.nodes[0] == index) return 0; + if (tri.nodes[1] == index) return 1; + return 2; +} + +////////////////////// +// Kernels // +////////////////////// + +template +__global__ void Test2DAdapterCuda3t_computeTriangleNormal_kernel(unsigned int size, + const CudaVec3* x, TriangleData* tri) +{ + int index = umul24(blockIdx.x,BSIZE)+threadIdx.x; + if (index < size) { + tri[index].normal = computeTriangleNormal(x, tri[index].nodes); + } +} + +template +__global__ void Test2DAdapterCuda3t_functionalGeom_kernel(unsigned int size, + const CudaVec3* x, TriangleData* tri) +{ + int index = umul24(blockIdx.x,BSIZE)+threadIdx.x; + if (index < size) { + tri[index].functional = functionalGeom(index, x, tri); + } +} + + +// Laplacian smoothing +template +__global__ void Test2DAdapterCuda3t_smoothLaplacian_kernel(unsigned int size, + CudaVec3* x, const TriangleData* tri, PointData* pt, + const Index *indices) +{ + typedef CudaVec3 Vec3; + + int index = umul24(blockIdx.x,BSIZE)+threadIdx.x; + if (index < size) { + + Index v = indices[index]; + + pt[v].oldpos = x[v]; + pt[v].oldworst = getMinFunc(v, tri, pt); + pt[v].bAccepted = false; + + // Compute centroid of polygon from 1-ring around the vertex + Vec3 xnew = Vec3::make(0.0, 0.0, 0.0); + for (Index ie=0; ie +__global__ void Test2DAdapterCuda3t_smoothOptimize_kernel(unsigned int size, + CudaVec3* x, const TriangleData* tri, PointData* pt, + const Index *indices) +{ + typedef CudaVec3 Vec3; + + int index = umul24(blockIdx.x,BSIZE)+threadIdx.x; + if (index < size) { + + Index v = indices[index]; + Vec3 xold = x[v]; + + pt[v].oldpos = x[v]; + pt[v].oldworst = getMinFunc(v, tri, pt); + pt[v].bAccepted = false; + + unsigned int nElem = pt[v].nNeighboursTri; + + // Compute gradients + // TODO: do it once for all elements! + Vec3 grad[10]; // TODO: Vec3 grad[nElem]; + if (nElem > 10) nElem = 10; // XXX + Real delta = 1e-5; + + // NOTE: Constrained to 2D! + // TODO: can we use shared memory here? + // -- X + x[v].x += delta; + for (Index it=0; it(pt[v].neighboursTri[it], x, tri); + grad[it].x = (m - tri[ pt[v].neighboursTri[it] ].functional)/delta; + } + // -- Y + x[v].x = xold.x; + x[v].y += delta; + for (Index it=0; it(pt[v].neighboursTri[it], x, tri); + grad[it].y = (m - tri[ pt[v].neighboursTri[it] ].functional)/delta; + } + + // Find smallest functional with non-zero gradient + Index imin = 0; + Real fmin = 1.0; + for (Index it=0; it 1e-15)) { + fmin = tri[ pt[v].neighboursTri[it] ].functional; + imin = it; + } + } + + Vec3 step = grad[imin]; + // Find out step size + Real gamma = 0.05; + //gamma *= step.norm(); + step = step * invnorm(step); + + x[v] = xold + gamma*step; + } +} + +template +__global__ void Test2DAdapterCuda3t_reduceStep_kernel(unsigned int size, + CudaVec3* x, const PointData* pt, const Index *indices) +{ + typedef CudaVec3 Vec3; + + int index = umul24(blockIdx.x,BSIZE)+threadIdx.x; + if (index < size) { + + Index v = indices[index]; + if (!pt[v].bAccepted) { + // The correct step size is best found empiricaly + x[v] = pt[v].oldpos + (x[v] - pt[v].oldpos) * Real(2.0/3.0); + //x[v] = (x[v] + pt[v].oldpos)/2.0; + } + } +} + +template +__global__ void Test2DAdapterCuda3t_restoreUnchanged_kernel(unsigned int size, + CudaVec3* x, const PointData* pt, const Index *indices) +{ + int index = umul24(blockIdx.x,BSIZE)+threadIdx.x; + if (index < size) { + Index v = indices[index]; + if (!pt[v].bAccepted) { + x[v] = pt[v].oldpos; + } + } +} + +template +__global__ void Test2DAdapterCuda3t_testAcceptable_kernel(unsigned int size, + CudaVec3* x, const TriangleData* tri, PointData* pt, const Index *indices, float tolerance) +{ + int index = umul24(blockIdx.x,BSIZE)+threadIdx.x; + if (index < size) { + + //// This check is not worth the effort + //if ((xold - x[v]).norm2() < 1e-8) { + // // No change in position + // //std::cout << "No change in position for " << v << "\n"; + // break; + //} + + Index v = indices[index]; + //if (!pt[v].bAccepted) { // TODO + + // We accept any change that doesn't decrease worst metric for the + // triangle set. + Real newworst = getMinFunc(v, tri, pt); + if (newworst >= (pt[v].oldworst + tolerance)) { + pt[v].bAccepted = true; + } + pt[v].newworst = newworst; + + //} + } +} + +///// parallel + +template +__global__ void Test2DAdapterCuda3t_prepareGradients_kernel(unsigned int size, + CudaVec3* x, TriangleData* tri) +{ + typedef CudaVec3 Vec3; + + int index = umul24(blockIdx.x,BSIZE)+threadIdx.x; + if (index < size) { + // TODO: handle boundary vertices + + for (int i=0; i<3; i++) { + // For each corner + + Real delta = 1e-5; + + Index v = tri[index].nodes[i]; + Vec3 xold = x[v]; + + // NOTE: Constrained to 2D! + // TODO: can we use shared memory here? + // -- X + x[v].x += delta; + Real m = functionalGeom(index, x, tri); + tri[index].gradient[i].x = (m - tri[index].functional)/delta; + // -- Y + x[v].x = xold.x; + x[v].y += delta; + m = functionalGeom(index, x, tri); + tri[index].gradient[i].y = (m - tri[index].functional)/delta; + + x[v].y = xold.y; + } + } +} + +template +__global__ void Test2DAdapterCuda3t_smoothParallel_kernel(unsigned int size, + CudaVec3* x, const TriangleData* tri, PointData* pt) +{ + typedef CudaVec3 Vec3; + + int index = umul24(blockIdx.x,BSIZE)+threadIdx.x; + if (index < size) { + + pt[index].oldpos = x[index]; + pt[index].oldworst = getMinFunc(index, tri, pt); + pt[index].bAccepted = false; + + unsigned int nElem = pt[index].nNeighboursTri; + + // Find smallest functional with non-zero gradient + Index tmin = Index(-1); + Real fmin = 1.0; + Vec3 step = Vec3::make(0.0, 0.0, 0.0); + if (!pt[index].bBoundary) { + for (Index it=0; it 1e-15)) { + fmin = tri[t].functional; + tmin = t; + step = tg; + } + } + } + pt[index].mintri = tmin; + pt[index].grad = step; + + // Sync -- we need mintri to be available for all points + __syncthreads(); + + // Consult neighbourhood and make an estimate + Vec3 ns = Vec3::make(0.0, 0.0, 0.0); + if (!pt[index].bBoundary) { + + for (Index it=0; it +__global__ void Test2DAdapterCuda3t_reduceStepP_kernel(unsigned int size, + CudaVec3* x, const PointData* pt) +{ + typedef CudaVec3 Vec3; + + int index = umul24(blockIdx.x,BSIZE)+threadIdx.x; + if (index < size) { + + if (!pt[index].bAccepted) { + // The correct step size is best found empiricaly + x[index] = pt[index].oldpos + + (x[index] - pt[index].oldpos) * Real(2.0/3.0); + //x[index] = (x[v] + pt[index].oldpos)/2.0; + } + } +} + +template +__global__ void Test2DAdapterCuda3t_testAcceptableP_kernel(unsigned int size, + CudaVec3* x, const TriangleData* tri, PointData* pt, + float tolerance) +{ + int index = umul24(blockIdx.x,BSIZE)+threadIdx.x; + if (index < size) { + + //// This check is not worth the effort + //if ((xold - x[index]).norm2() < 1e-8) { + // // No change in position + // //std::cout << "No change in position for " << index << "\n"; + // break; + //} + + //if (!pt[index].bAccepted) { // TODO + + // We accept any change that doesn't decrease worst metric for the + // triangle set. + Real newworst = getMinFunc(index, tri, pt); + if (newworst >= (pt[index].oldworst + tolerance)) { + pt[index].bAccepted = true; + } + pt[index].newworst = newworst; + + //} + } +} + +template +__global__ void Test2DAdapterCuda3t_restoreUnchangedP_kernel(unsigned int size, + CudaVec3* x, const PointData* pt) +{ + int index = umul24(blockIdx.x,BSIZE)+threadIdx.x; + if (index < size) { + if (!pt[index].bAccepted) { + x[index] = pt[index].oldpos; + } + } +} + + + +////////////////////// +// CPU-side methods // +////////////////////// + + +void Test2DAdapterCuda3f_computeTriangleNormal(unsigned int size, const void* x, void* tri) +{ + dim3 threads(BSIZE,1); + dim3 grid((size+BSIZE-1)/BSIZE,1); + Test2DAdapterCuda3t_computeTriangleNormal_kernel<<< grid, threads >>>(size, (const CudaVec3*)x, (TriangleData*)tri); + mycudaDebugError("Test2DAdapterCuda3t_computeTriangleNormal_kernel"); +} + +void Test2DAdapterCuda3f_functionalGeom(unsigned int size, const void* x, void* tri) +{ + dim3 threads(BSIZE,1); + dim3 grid((size+BSIZE-1)/BSIZE,1); + Test2DAdapterCuda3t_functionalGeom_kernel<<< grid, threads >>>(size, (const CudaVec3*)x, (TriangleData*)tri); + mycudaDebugError("Test2DAdapterCuda3t_functionalGeom_kernel"); +} + +void Test2DAdapterCuda3f_reduceStep(unsigned int size, void* x, void* pt, void* indices) +{ + dim3 threads(BSIZE,1); + dim3 grid((size+BSIZE-1)/BSIZE,1); + Test2DAdapterCuda3t_reduceStep_kernel<<< grid, threads >>>(size, (CudaVec3*)x, (const PointData*)pt, (const Index*) indices); + mycudaDebugError("Test2DAdapterCuda3t_reduceStep_kernel"); +} + +void Test2DAdapterCuda3f_restoreUnchanged(unsigned int size, void* x, void* pt, void* indices) +{ + dim3 threads(BSIZE,1); + dim3 grid((size+BSIZE-1)/BSIZE,1); + Test2DAdapterCuda3t_restoreUnchanged_kernel<<< grid, threads >>>(size, (CudaVec3*)x, (const PointData*)pt, (const Index*) indices); + mycudaDebugError("Test2DAdapterCuda3t_restoreUnchanged_kernel"); +} + +void Test2DAdapterCuda3f_smooth(unsigned int size, void* x, void* tri, void* pt, void* indices) +{ + dim3 threads(BSIZE,1); + dim3 grid((size+BSIZE-1)/BSIZE,1); + //Test2DAdapterCuda3t_smoothLaplacian_kernel<<< grid, threads >>>( + // size, (CudaVec3*)x, (const TriangleData*)tri, + // (PointData*)pt, (const Index*) indices); + //mycudaDebugError("Test2DAdapterCuda3t_smoothLaplacian_kernel"); + Test2DAdapterCuda3t_smoothOptimize_kernel<<< grid, threads >>>( + size, (CudaVec3*)x, (const TriangleData*)tri, + (PointData*)pt, (const Index*) indices); + mycudaDebugError("Test2DAdapterCuda3t_smoothOptimize_kernel"); +} + +void Test2DAdapterCuda3f_testAcceptable(unsigned int size, void* x, const void* tri, void* pt, void* indices, float tolerance) +{ + dim3 threads(BSIZE,1); + dim3 grid((size+BSIZE-1)/BSIZE,1); + Test2DAdapterCuda3t_testAcceptable_kernel<<< grid, threads >>>(size, (CudaVec3*)x, (const TriangleData*)tri, (PointData*)pt, (const Index*) indices, tolerance); + mycudaDebugError("Test2DAdapterCuda3t_testAcceptable_kernel"); +} + + +/// Specific to parallel version + +void Test2DAdapterCuda3f_prepareGradients(unsigned int size, const void* x, + void* tri) +{ + dim3 threads(BSIZE,1); + dim3 grid((size+BSIZE-1)/BSIZE,1); + Test2DAdapterCuda3t_prepareGradients_kernel + <<< grid, threads >>>( + size, (CudaVec3*)x, (TriangleData*)tri); + mycudaDebugError("Test2DAdapterCuda3t_prepareGradients_kernel"); +} + +void Test2DAdapterCuda3f_smoothParallel(unsigned int size, void* x, + const void* tri, void* pt) +{ + dim3 threads(BSIZE,1); + dim3 grid((size+BSIZE-1)/BSIZE,1); + Test2DAdapterCuda3t_smoothParallel_kernel + <<< grid, threads >>>( + size, (CudaVec3*)x, (const TriangleData*)tri, + (PointData*)pt); + mycudaDebugError("Test2DAdapterCuda3t_smoothParallel_kernel"); +} + +void Test2DAdapterCuda3f_reduceStepP(unsigned int size, void* x, void* pt) +{ + dim3 threads(BSIZE,1); + dim3 grid((size+BSIZE-1)/BSIZE,1); + Test2DAdapterCuda3t_reduceStepP_kernel + <<< grid, threads >>>( + size, (CudaVec3*)x, (const PointData*)pt); + mycudaDebugError("Test2DAdapterCuda3t_reduceStepP_kernel"); +} + +void Test2DAdapterCuda3f_testAcceptableP(unsigned int size, void* x, + const void* tri, void* pt, float tolerance) +{ + dim3 threads(BSIZE,1); + dim3 grid((size+BSIZE-1)/BSIZE,1); + Test2DAdapterCuda3t_testAcceptableP_kernel + <<< grid, threads >>>( + size, (CudaVec3*)x, (const TriangleData*)tri, + (PointData*)pt, tolerance); + mycudaDebugError("Test2DAdapterCuda3t_testAcceptableP_kernel"); +} + +void Test2DAdapterCuda3f_restoreUnchangedP(unsigned int size, void* x, + void* pt) +{ + dim3 threads(BSIZE,1); + dim3 grid((size+BSIZE-1)/BSIZE,1); + Test2DAdapterCuda3t_restoreUnchangedP_kernel + <<< grid, threads >>>( + size, (CudaVec3*)x, (const PointData*)pt); + mycudaDebugError("Test2DAdapterCuda3t_restoreUnchangedP_kernel"); +} + + +#if defined(__cplusplus) && CUDA_VERSION < 2000 +} // namespace cuda +} // namespace gpu +} // namespace sofa +#endif diff --git a/src/Shell/controller/CudaTest2DAdapter.h b/src/Shell/controller/CudaTest2DAdapter.h new file mode 100644 index 0000000..048c1e7 --- /dev/null +++ b/src/Shell/controller/CudaTest2DAdapter.h @@ -0,0 +1,110 @@ +#ifndef CUDA_TEST2DADAPTER_H +#define CUDA_TEST2DADAPTER_H + +#include +#include + +#include +#include + + +namespace sofa +{ + +namespace gpu +{ + +namespace cuda +{ + +template +class CudaKernelsTest2DAdapter; + +} // namespace cuda + +} // namespace gpu + +namespace component +{ + +namespace controller +{ + +/** + * @brief Internal data for Test2DAdapter specialized for Cuda version. + */ +template<> +class Test2DAdapterData< gpu::cuda::CudaVec3fTypes > +{ +public: + typedef gpu::cuda::CudaVec3fTypes DataTypes; + typedef Test2DAdapter Main; + typedef DataTypes::Coord Coord; + typedef DataTypes::Deriv Deriv; + typedef Coord::value_type Real; + + typedef sofa::component::topology::TriangleSetTopologyContainer::TriangleID Index; + + // Graph colours (of independent sets) + type::vector< sofa::gpu::cuda::CudaVector > colours; + + struct TriangleData { + type::fixed_array nodes; + Coord normal; + Real functional; + type::fixed_array gradient; + }; + + struct PointData { + + bool bBoundary; /// Is the pont on a border? + + unsigned int nNeighboursPt; /// Number of points in N1-ring. + const Index *neighboursPt; /// List of points in N1-ring. + + unsigned int nNeighboursTri; /// Number of triangles in N1-ring. + const Index *neighboursTri; /// List of triangles in N1-ring. + + bool bAccepted; /// New position has been accepted in current step. + Coord oldpos; /// Temporary holder for original point position. + Real oldworst; + Real newworst; + + Index mintri; // Triangle from N1-ring with smallest functional + Deriv grad; // Gradient for mintri + }; + + struct PointDataHost { + sofa::gpu::cuda::CudaVector neighboursPt; + sofa::gpu::cuda::CudaVector neighboursTri; + }; + + sofa::gpu::cuda::CudaVector triangles; + sofa::gpu::cuda::CudaVector points; + sofa::gpu::cuda::CudaVector pointsHost; + +}; + +template<> +class Test2DAdapterData< gpu::cuda::CudaVec3fTypes >; + +template<> +void Test2DAdapter< gpu::cuda::CudaVec3fTypes >::onEndAnimationStep(const double dt); + +template<> +void Test2DAdapter< gpu::cuda::CudaVec3fTypes >::smoothLinear(); + +template<> +void Test2DAdapter< gpu::cuda::CudaVec3fTypes >::smoothParallel(); + +template<> +void Test2DAdapter< gpu::cuda::CudaVec3fTypes >::colourGraph(); + + +} // namespace controller + +} // namespace component + +} // namespace sofa + +#endif // CUDA_TEST2DADAPTER_H diff --git a/src/Shell/controller/CudaTest2DAdapter.inl b/src/Shell/controller/CudaTest2DAdapter.inl new file mode 100644 index 0000000..05223ae --- /dev/null +++ b/src/Shell/controller/CudaTest2DAdapter.inl @@ -0,0 +1,390 @@ +#ifndef CUDA_TEST2DADAPTER_INL +#define CUDA_TEST2DADAPTER_INL + +#include +#include +#include +#include + +extern "C" +{ +void Test2DAdapterCuda3f_computeTriangleNormal(unsigned int size, const void* x, const void* tri); +void Test2DAdapterCuda3f_functionalGeom(unsigned int size, const void* x, void* tri); +void Test2DAdapterCuda3f_reduceStep(unsigned int size, void* x, void* pt, void* indices); +void Test2DAdapterCuda3f_restoreUnchanged(unsigned int size, void* x, void* pt, void* indices); +void Test2DAdapterCuda3f_smooth(unsigned int size, void* x, void* tri, void* pt, void* indices); +void Test2DAdapterCuda3f_testAcceptable(unsigned int size, void* x, const void* tri, void* pt, void* indices, float tolerance); +///// parallel +void Test2DAdapterCuda3f_prepareGradients(unsigned int size, const void* x, void* tri); +void Test2DAdapterCuda3f_smoothParallel(unsigned int size, void* x, const void* tri, void* pt); +void Test2DAdapterCuda3f_reduceStepP(unsigned int size, void* x, void* pt); +void Test2DAdapterCuda3f_testAcceptableP(unsigned int size, void* x, const void* tri, void* pt, float tolerance); +void Test2DAdapterCuda3f_restoreUnchangedP(unsigned int size, void* x, void* pt); +//#ifdef SOFA_GPU_CUDA_DOUBLE +//void Test2DAdapterCuda3d_computeTriangleNormal(const void* x, const void* n); +//#endif +}// extern "C" + + +namespace sofa +{ + +namespace component +{ + +namespace controller +{ + +template<> +void Test2DAdapter< gpu::cuda::CudaVec3fTypes >::onEndAnimationStep(const double /*dt*/) +{ + using namespace sofa::gpu::cuda; + + std::cout << "GPU step\n"; + + if ((m_container == NULL) || (m_state == NULL)) + return; + + Index nTriangles = m_container->getNbTriangles(); + if (nTriangles == 0) + return; + + Data* datax = m_state->write(sofa::core::VecCoordId::position()); + VecCoord& x = *datax->beginEdit(); + + ////////////////////////// + // Initialization + if (data.colours.size() == 0) { + // Do the initialization + // TODO: move this elsewhere + + // Colour the graph of vertices/edges to get the independent sets. + colourGraph(); + // TODO: use a better container for the sets? + + // Initialize triangle data + data.triangles.resize(nTriangles); + for (Index i=0; igetTriangle(i); + } + + // Initialize point data + Index nPoints = x.size(); + data.points.resize(nPoints); + data.pointsHost.resize(nPoints); + for (Index v=0; vgetEdgesAroundVertex(v); + data.pointsHost[v].neighboursPt.resize(N1e.size()); + + for (Index ie=0; iegetEdge(N1e[ie]); + for (int n=0; n<2; n++) { + if (e[n] != v) { + data.pointsHost[v].neighboursPt[ie] = e[n]; + } + } + } + + data.points[v].nNeighboursPt = N1e.size(); + data.points[v].neighboursPt = reinterpret_cast + (data.pointsHost[v].neighboursPt.deviceRead()); + + // List of triangle neighbours + TrianglesAroundVertex N1 = m_container->getTrianglesAroundVertex(v); + data.pointsHost[v].neighboursTri.resize(N1.size()); + + for (Index it=0; it + (data.pointsHost[v].neighboursTri.deviceRead()); + } + } + // End of Initialization + ////////////////////////// + + stepCounter++; + + // Array sizes + //unsigned int szNormals = nTriangles * 3 * sizeof(Real); + + // Compute initial metrics and normals + // TODO: kernel + //Real *normals_gpu; + //mycudaMalloc((void**)&normals_gpu, szNormals); + + //Real *normals_buf; + //normals_buf = (Real*) malloc(szNormals); + //mycudaMemcpyDeviceToHost(normals_buf, normals_gpu, szNormals); + + //vector normals(nTriangles); + //for (unsigned int i=0; i &functionals = *m_functionals.beginEdit(); + functionals.resize(nTriangles); + for (Index i=0; iendEdit(); +} + + +template<> +void Test2DAdapter< gpu::cuda::CudaVec3fTypes >::smoothLinear() +{ + Index nTriangles = m_container->getNbTriangles(); + if (nTriangles == 0) + return; + + Data* datax = m_state->write(sofa::core::VecCoordId::position()); + VecCoord& x = *datax->beginEdit(); + + // Persistent on gpu, we don't need to use this on host + void *triangles_gpu = data.triangles.deviceWrite(); + void *points_gpu = data.points.deviceWrite(); + + // Compute initial normals + Test2DAdapterCuda3f_computeTriangleNormal(nTriangles, x.deviceRead(), + triangles_gpu); + + // Compute initial values of the functional + Test2DAdapterCuda3f_functionalGeom(nTriangles, x.deviceRead(), + triangles_gpu); + + //data.triangles.hostRead(); + //std::cout << "m: "; + //for (unsigned int i=0; iendEdit(); +} + +template<> +void Test2DAdapter< gpu::cuda::CudaVec3fTypes >::smoothParallel() +{ + Index nTriangles = m_container->getNbTriangles(); + Index nPoints = m_container->getNbPoints(); + if (nTriangles == 0) + return; + + Data* datax = m_state->write(sofa::core::VecCoordId::position()); + VecCoord& x = *datax->beginEdit(); + + // Persistent on gpu, we don't need to use this on host + void *triangles_gpu = data.triangles.deviceWrite(); + void *points_gpu = data.points.deviceWrite(); + + // Compute initial normals + Test2DAdapterCuda3f_computeTriangleNormal(nTriangles, x.deviceRead(), + triangles_gpu); + + // Compute initial values of the functional + Test2DAdapterCuda3f_functionalGeom(nTriangles, x.deviceRead(), + triangles_gpu); + + // Prepare gradients + Test2DAdapterCuda3f_prepareGradients(nTriangles, x.deviceRead(), + triangles_gpu); + + // Per vertex operations: gradient, assumed step + Test2DAdapterCuda3f_smoothParallel(nPoints, x.deviceWrite(), + triangles_gpu, points_gpu); + + // Re-evaluate functional + Test2DAdapterCuda3f_functionalGeom(nTriangles, x.deviceRead(), + triangles_gpu); + + // Test for acceptance + Test2DAdapterCuda3f_testAcceptableP(nPoints, x.deviceWrite(), + triangles_gpu, points_gpu, m_sigma.getValue()); + + data.points.hostRead(); + for (unsigned int i=0; iendEdit(); +} + +template<> +void Test2DAdapter< gpu::cuda::CudaVec3fTypes >::colourGraph() +{ + data.colours.clear(); + + int ncolours = 0; + type::vector c(m_container->getNbPoints(), -1); + + for (Index v=0; (int)vgetNbPoints(); v++) { + + if (pointInfo.getValue()[v].isBoundary() || pointInfo.getValue()[v].isFixed()) continue; // Skip boundary vertices + + c[v] = 0; + + EdgesAroundVertex N1e = m_container->getEdgesAroundVertex(v); + Index ie = 0; + while (ie < N1e.size()) { + Edge e = m_container->getEdge(N1e[ie]); + Index other = (e[0] == v) ? e[1] : e[0]; + if (c[v] == c[other]) { + c[v]++; + ie = 0; + continue; + } + ie++; + } + + if (c[v] >= ncolours) { + ncolours = c[v]+1; + data.colours.resize(ncolours); + } + data.colours[ c[v] ].push_back(v); + } + + std::cout << "GC: " << data.colours.size() << " colours\n"; +} + +} // namespace controller + +} // namespace component + +} // namespace sofa + +#endif // SOFA_COMPONENT_CONTROLLER_TEST2DADAPTER_INL diff --git a/src/Shell/controller/MeshChangedEvent.h b/src/Shell/controller/MeshChangedEvent.h index ffdcf38..2a5aca5 100644 --- a/src/Shell/controller/MeshChangedEvent.h +++ b/src/Shell/controller/MeshChangedEvent.h @@ -32,7 +32,7 @@ namespace shell::objectmodel using namespace sofa::type; // Sent by MeshInterpolator when the mesh changes -class MeshChangedEvent : public sofa::core::objectmodel::Event +class SOFA_SHELL_API MeshChangedEvent : public sofa::core::objectmodel::Event { public: diff --git a/src/Shell/controller/Test2DAdapter.cpp b/src/Shell/controller/Test2DAdapter.cpp new file mode 100644 index 0000000..526c943 --- /dev/null +++ b/src/Shell/controller/Test2DAdapter.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + + +namespace sofa +{ + +namespace component +{ + +namespace controller +{ + +using namespace sofa::defaulttype; + +SOFA_DECL_CLASS(Test2DAdapter) + +// Register in the Factory +int Test2DAdapterClass = core::RegisterObject("Adaptive mesh improvement component for 2D triangular meshes (for testing)") +#ifdef SOFA_FLOAT +.add< Test2DAdapter >(true) // default template +#else +.add< Test2DAdapter >(true) // default template +# ifndef SOFA_DOUBLE +.add< Test2DAdapter >() +# endif +#endif +; + +#ifndef SOFA_FLOAT +template class SOFA_SHELLS_API Test2DAdapter; +#endif //SOFA_FLOAT +#ifndef SOFA_DOUBLE +template class SOFA_SHELLS_API Test2DAdapter; +#endif //SOFA_DOUBLE + +} // namespace controller + +} // namespace component + +} // namespace sofa diff --git a/src/Shell/controller/Test2DAdapter.h b/src/Shell/controller/Test2DAdapter.h new file mode 100644 index 0000000..28f98bc --- /dev/null +++ b/src/Shell/controller/Test2DAdapter.h @@ -0,0 +1,464 @@ +#ifndef SOFA_COMPONENT_CONTROLLER_TEST2DADAPTER_H +#define SOFA_COMPONENT_CONTROLLER_TEST2DADAPTER_H + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace sofa +{ + +namespace component +{ + +namespace controller +{ + +/** + * @brief Internal data for Test2DAdapter. + * + * Internal data for Test2DAdapter. Can be overriden in class specializations. + */ +template +class Test2DAdapterData +{ +public: +}; + +/** + * + * @brief Component for adaptivity/smoothing of 2D triangular meshes. + * + * References: + * + * [CTS98] Canann, S. A.; Tristano, J. R. & Staten, M. L. An Approach to + * Combined Laplacian and Optimization-Based Smoothing for Triangular, + * Quadrilateral, and Quad-Dominant Meshes International Meshing + * Roundtable, 1998, 479-494. + * + * [PUdOG01] Pain, C.; Umpleby, A.; de Oliveira, C. & Goddard, A. Tetrahedral + * mesh optimisation and adaptivity for steady-state and transient + * finite element calculations Computer Methods in Applied Mechanics and + * Engineering, 2001, 190, 3771-3796. + * + * [VL99] Y. Vasilevskii, K. Lipnikov, An adaptive algorithm for quasioptimal + * mesh generation, Computational mathematics and mathematical physics + * 39 (9) (1999) 1468–1486. + * + * + * 1) make initial projections, handle overlaping boundaries + * - for boundary points pick primary region that will be used for smoothing + * 2) smoothing step + * - if moved point not on overlapping boundary no problem + * - if point is on the boundary we need to recompute the projection and + * metrics in the other regions + * * computing new position using the barycentric coordinates may be + * enough and we don't need to rebuild whole region + * * recompute the metric tensors in N1-ring (or just for the point?) + * - compute new point position in 3D and update it + * 3) loop + * + * + <---------- x(u) ---------- + + U X + -----------> -------------> + global bezier + (barycentric) + ( in 2D ) + + ----------- u(x) ---------> + * + */ +template +class Test2DAdapter : public Controller +{ +public: + SOFA_CLASS(SOFA_TEMPLATE(Test2DAdapter,DataTypes),Controller); + + typedef typename DataTypes::VecCoord VecCoord; + typedef typename DataTypes::VecDeriv VecDeriv; + typedef typename DataTypes::Coord Coord; + typedef typename DataTypes::Deriv Deriv; + typedef typename Coord::value_type Real; + + typedef sofa::type::Vec<2, Real> Vec2; + typedef sofa::type::Vec<3, Real> Vec3; + typedef sofa::type::Mat<2,2,Real> Mat22; + typedef sofa::type::Mat<3,3,Real> Mat33; + //typedef type::vector VecVec2; + typedef type::vector VecVec3; + + + typedef sofa::core::topology::BaseMeshTopology::Edge Edge; + typedef sofa::core::topology::BaseMeshTopology::EdgesAroundVertex EdgesAroundVertex; + typedef sofa::component::topology::TriangleSetTopologyContainer::TriangleID Index; + typedef sofa::component::topology::TriangleSetTopologyContainer::Triangle Triangle; + typedef sofa::component::topology::TriangleSetTopologyContainer::TrianglesAroundVertex TrianglesAroundVertex; + typedef sofa::component::topology::TriangleSetTopologyContainer::TrianglesAroundEdge TrianglesAroundEdge; + typedef sofa::component::topology::TriangleSetTopologyContainer::EdgesInTriangle EdgesInTriangle; + typedef sofa::type::vector VecIndex; + + enum { InvalidID = sofa::core::topology::Topology::InvalidID }; + +protected: + + Test2DAdapter(); + + virtual ~Test2DAdapter(); + + Test2DAdapterData data; + +public: + + /// Holds information about each node. + class PointInformation { + public: + PointInformation() : type(NORMAL), forceFixed(false) {} + + /// Type of the node. + enum NodeType { NORMAL, FIXED, BOUNDARY }; + + /// Line this boundary node lies on. + Vec3 boundary; + + /// @brief Marks the type of the node (whether it lies on the + /// boundary or is fixed). + NodeType type; + + /// Force node to be fixed despite its type. + bool forceFixed; + + /// Returns true if node can be moved freely. + bool isNormal() const { return type == NORMAL && !forceFixed; } + /// Returns true if node lies on the boundary. + bool isBoundary() const { return (type == BOUNDARY) && + !forceFixed; } + /// Returns true if node is fixed and cannot be moved. + bool isFixed() const { return (type == FIXED) || forceFixed; } + + /// Output stream + inline friend std::ostream& operator<< ( std::ostream& os, const PointInformation& /*pi*/ ) { return os; } + /// Input stream + inline friend std::istream& operator>> ( std::istream& in, PointInformation& /*pi*/ ) { return in; } + }; + + class PointInfoHandler : public sofa::core::topology::TopologyDataHandler > + { + public: + typedef sofa::core::topology::TopologyDataHandler > Inherited; + PointInfoHandler(Test2DAdapter* _adapter, sofa::core::topology::PointData >* _data) : Inherited(_data), adapter(_adapter) {} + + void applyCreateFunction( + unsigned int pointIndex, + PointInformation &pInfo, + const sofa::type::vector< unsigned int > &ancestors, + const sofa::type::vector< double > &coeffs) + { applyCreateFunction(pointIndex, pInfo, sofa::core::topology::BaseMeshTopology::InvalidID, ancestors, coeffs); } + + void applyCreateFunction( + unsigned int pointIndex, + PointInformation &pInfo, + const sofa::core::topology::Point &elem, + const sofa::type::vector< unsigned int > &ancestors, + const sofa::type::vector< double > &coeffs); + + void applyDestroyFunction(unsigned int pointIndex, PointInformation &pInfo); + + void swap( unsigned int i1, unsigned int i2 ); + + protected: + Test2DAdapter *adapter; + }; + + class TriangleInformation { + public: + TriangleInformation() {} + + /// Initial normal of the triangle. + Vec3 normal; + + /// List of points projected onto this triangle. + sofa::type::vector attachedPoints; + + /// Output stream + inline friend std::ostream& operator<< ( std::ostream& os, const TriangleInformation& /*ti*/ ) { return os; } + /// Input stream + inline friend std::istream& operator>> ( std::istream& in, TriangleInformation& /*ti*/ ) { return in; } + }; + + class TriangleInfoHandler : public sofa::core::topology::TopologyDataHandler > + { + public: + typedef sofa::core::topology::TopologyDataHandler > Inherited; + + TriangleInfoHandler( + Test2DAdapter *_adapter, + sofa::core::topology::TriangleData >* _data) : Inherited(_data), adapter(_adapter) {} + + void applyCreateFunction( + unsigned int triangleIndex, + TriangleInformation &tInfo, + const sofa::core::topology::Triangle &elem, + const sofa::type::vector< unsigned int > &ancestors, + const sofa::type::vector< double > &coeffs); + + void applyDestroyFunction(unsigned int triangleIndex, TriangleInformation &tInfo); + + void swap( unsigned int i1, unsigned int i2 ); + + ///// Add Element after a displacement of vertices, ie. add element based on previous position topology revision. + //void addOnMovedPosition(const sofa::type::vector &indexList, + // const sofa::type::vector< topology::Triangle > &elems); + + ///// Remove Element after a displacement of vertices, ie. add element based on previous position topology revision. + //void removeOnMovedPosition(const sofa::type::vector &indices); + + protected: + Test2DAdapter *adapter; + }; + + + /// Minimal increase in functional to accept the change + Data m_sigma; + /// Current value of the functional for each triangle. + Data< type::vector > m_functionals; + + /// Points to project onto the topology. + //SingleLink, MechanicalState, + // BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> m_mappedState; + Data< sofa::type::vector > m_projectedPoints; + /// Interpolation indices for projected points. + Data< sofa::type::vector > > m_interpolationIndices; + /// Interpolation values for projected points. + Data< sofa::type::vector > > m_interpolationValues; + + virtual void init(); + virtual void reinit(); + + virtual std::string getTemplateName() const { + return templateName(this); + } + + static std::string templateName(const Test2DAdapter* = NULL) { + return DataTypes::Name(); + } + + void draw(const core::visual::VisualParams* vparams); + + void onEndAnimationStep(const double dt); + void onKeyPressedEvent(core::objectmodel::KeypressedEvent *key); + + /// Returnds whether node is considered normal (can be moved freely). + bool isPointNormal(Index pt) { + return pointInfo.getValue()[pt].isNormal(); + } + + /// Returnds whether node is constrained and cannot be moved. + bool isPointFixed(Index pt) { + return pointInfo.getValue()[pt].isFixed(); + } + + /// Returnds whether node is considered normal (can be moved freely). + bool isPointBoundary(Index pt) { + return pointInfo.getValue()[pt].isBoundary(); + } + + /// Returnds whether node is considered normal (can be moved freely). + Vec3 getPointBoundary(Index pt) { + return pointInfo.getValue()[pt].boundary; + } + + /// Returns whith which precision the optimizer operates. + Real getPrecision() { return m_precision; } + + /// Set specified point as fixed to prevent it's movement. + void setPointFixed(Index pt) { + (*pointInfo.beginEdit())[pt].forceFixed = true; + pointInfo.endEdit(); + } + + void setPointAttraction(Index pointID, Vec3 position, Index triangleID) { + m_pointId = pointID; + m_point = position; + m_pointTriId = triangleID; + } + + void protectEdge(Index edgeID) { + if (std::find(m_protectedEdges.begin(), m_protectedEdges.end(), + edgeID) == m_protectedEdges.end()) { + m_protectedEdges.push_back(edgeID); + } + } + + void unprotectEdge(Index edgeID) { + VecIndex::iterator i = std::find(m_protectedEdges.begin(), + m_protectedEdges.end(), edgeID); + if (i != m_protectedEdges.end()) { + m_protectedEdges.erase(i); + } + } + + /** + * @brief Move point to a new location. + * + * @param pt Index of the point to mvoe. + * @param target Target coordinates to move the point to. + * @param hint Optional index of the triangle in which the point is + * located. + * @param bInRest Whether to perform the operation on rest shape. + */ + void relocatePoint(Index pt, Coord target, Index hint=InvalidID, + bool bInRest=true); + + /** + * @brief Distortion metric for a triangle. + * + * @param t Triangle to compute the metric for. + * @param triID Triangle to relate computation to. + */ + Real metricGeom(const Triangle &t, const Index triID) const { + return m_opt.metricGeom(t, triID); + } + +private: + + unsigned int stepCounter; + sofa::core::topology::TriangleSetTopologyContainer* m_container; + sofa::core::topology::TriangleSetTopologyModifier* m_modifier; + sofa::core::topology::TriangleSetGeometryAlgorithms *m_algoGeom; + sofa::core::topology::TriangleSetTopologyAlgorithms *m_algoTopo; + sofa::core::behavior::MechanicalState* m_state; + + /// List of nodes that have to be rechecked if they are on the boundry. + std::map m_toUpdate; + + /// Amount of precision that is acceptable for us. + Real m_precision; + + // TODO: to be removed. + Real sumgamma, mingamma, maxgamma; + int ngamma; + + /// Point to attract to prespecified position. + Index m_pointId; + /// A point on a surface to attract to (in deformed shape). + Vec3 m_point; + /// Position of point projected into rest shape. + Vec3 m_pointRest; + /// @brief Triangle ID inside which m_point is located (valid only if + /// m_pointId != InvalidID). + Index m_pointTriId; + + /// Edges not eligible for edge swapping operation. + // TODO: We need to add edge EdgeInfoHandler::swap(). + VecIndex m_protectedEdges; + + SurfaceParametrization m_surf; + Optimize2DSurface m_opt; + + Real metricDistance(const Triangle &t, const VecVec3 &x, const Vec3 &/*normal*/) const { + if (m_pointId == InvalidID) return 1.0; + + Real d; + if (t[0] == m_pointId) d = (m_pointRest - x[ t[0] ]).norm2(); + else if (t[1] == m_pointId) d = (m_pointRest - x[ t[1] ]).norm2(); + else if (t[2] == m_pointId) d = (m_pointRest - x[ t[2] ]).norm2(); + else return 1.0; + + // Accept point if distance from target is less than this value. + if (d < m_precision) { + return 1.0; + } else if (d > 1.0) { + // Do NOT go into negative value! Negative is strictly for inverted + // elements. + return 0.0; + } else { + return (Real)1.0 - d; + } + } + + /** + * Attemt edge swapping operation to improve functional of triangle. + * + * @param triID Index of triangle to improve. + */ + void swapEdge(Index triID); + + /** + * @brief Inspect the nodes to detect boundary/fixed nodes. + */ + void recheckBoundary(); + + /** + * @brief Detect if node can be moved or not. + * + * Detects if node lies on a boundary and if it can be moved or not. + * + * @param pt Index of the point to inspect. + * @param boundary Direction of the boundary (set only if the type is BOUNDARY). + * + * @returns Type of the node. + * + */ + typename PointInformation::NodeType detectNodeType(Index pt, Vec3 &boundaryDirection); + + /** + * @brief Compute triangle normal. + * + * @param a First vertex. + * @param b Second vertex. + * @param c Third vertex. + * @param normal The computed normal. + * + */ + // TODO: this is geometry algorithms + void computeTriangleNormal(const Triangle &t, const VecCoord &x, Vec3 &normal) const; + + // GPU-specific methods + void colourGraph(); + void smoothLinear(); + void smoothParallel(); + + /// Initialize all projected points. + void projectionInit(); + /** + * Update projection of points affected by rellocation of the specified point. + * @param pt Index of a point that was relocated. + */ + void projectionUpdate(Index pt); + +protected: + + topology::PointData< sofa::type::vector > pointInfo; + PointInfoHandler* pointHandler; + + topology::TriangleData< sofa::type::vector > triInfo; + TriangleInfoHandler* triHandler; + +}; + + +} // namespace controller + +} // namespace component + +} // namespace sofa + +#endif //SOFA_COMPONENT_CONTROLLER_TEST2DADAPTER_H diff --git a/src/Shell/controller/Test2DAdapter.inl b/src/Shell/controller/Test2DAdapter.inl new file mode 100644 index 0000000..ef2e7da --- /dev/null +++ b/src/Shell/controller/Test2DAdapter.inl @@ -0,0 +1,1048 @@ +// +// Component for dynamic remeshing. +// +// Few notes for all the brave adventurers. +// * There is a thin line between operations performed in rest shape and in +// deformed shape. Specificaly the element geometry is analysed in rest +// shape, but point tracking for cutting support is (mostly) done in deformed +// shape. +// * The point tracking is not ideal because the optimizion is slow (read: +// makes small updates). Idealy we would want to run several optimization +// steps per simulation step. Unfortunately the work related to relocation of +// points (updating mechanical state, propagation of topololgical changes, +// ...) is really big so we cannot do that. +// +// TODO: +// - Bugs +// * triangles get inverted +// * attaching to fixed points and then relocating them +// +// - reattach points in N1-ring during cutting to avoid degeneration +// - refactor into several components +// +// Edge cases that are not handled: +// - cut is too short (doesn't span two edges) +// - cutting near the edge (handling of boundary/fixed nodes) +// +// TODO parametrization +// + updates on point relocation +// * done by propagation of topological changes +// + factor out optimization related stuff +// + modify optimization process to use parametrized 2D surface +// - check if cutting still works +// - fix parametrization +// - compute metric tensors +// - add Bézier surfaces +// - fix edge swapping + +#ifndef SOFA_COMPONENT_CONTROLLER_TEST2DADAPTER_INL +#define SOFA_COMPONENT_CONTROLLER_TEST2DADAPTER_INL + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define OTHER(x, a, b) ((x == a) ? b : a) + +// Return non-zero if triangle with points (a,b,c) is defined in +// counter-clockwise direction. +// NOTE: Constrained to 2D! +#define CCW(a,b,c) (\ + cross(Vec2(b[0]-a[0], b[1]-a[1]), \ + Vec2(c[0]-a[0], c[1]-a[1])) > 1e-15) + +namespace sofa +{ + +namespace component +{ + +namespace controller +{ + +//sofa::helper::TSimpleTimer<1,10> mytimer; + +template +void Test2DAdapter::PointInfoHandler:: applyCreateFunction( + unsigned int pointIndex, + PointInformation &/*pInfo*/, + const sofa::core::topology::Point &point, + const sofa::type::vector< unsigned int > &ancestors, + const sofa::type::vector< double > &coeffs) +{ + adapter->m_toUpdate[pointIndex] = true; + adapter->m_surf.pointAdd(pointIndex, point, ancestors, coeffs); +} + + + +template +void Test2DAdapter::PointInfoHandler::applyDestroyFunction(unsigned int pointIndex, PointInformation& /*pInfo*/) +{ + //std::cout << "pt " << __FUNCTION__ << pointIndex << std::endl; + adapter->m_toUpdate.erase(pointIndex); + adapter->m_surf.pointRemove(pointIndex); +} + +template +void Test2DAdapter::PointInfoHandler::swap(unsigned int i1, + unsigned int i2) +{ + //std::cout << "pt " << __FUNCTION__ << " " << i1 << " " << i2 << std::endl; + Inherited::swap(i1, i2); + + // m_pointId may change + if (adapter->m_pointId == i1) { + adapter->m_pointId = i2; + } else if (adapter->m_pointId == i2) { + adapter->m_pointId = i1; + } + + // Update indices in m_toUpdate + if (adapter->m_toUpdate[i1] && !adapter->m_toUpdate[i2]) { + adapter->m_toUpdate.erase(i1); + adapter->m_toUpdate[i2] = true; + } else if (adapter->m_toUpdate[i2] && !adapter->m_toUpdate[i1]) { + adapter->m_toUpdate.erase(i2); + adapter->m_toUpdate[i1] = true; + } + + adapter->m_surf.pointSwap(i1, i2); +} + + + + +template +void Test2DAdapter::TriangleInfoHandler::applyCreateFunction( + unsigned int triangleIndex, TriangleInformation &tInfo, + const sofa::core::topology::Triangle& elem, + const sofa::type::vector< unsigned int > &/*ancestors*/, + const sofa::type::vector< double > &/*coeffs*/) +{ + //std::cout << "tri " << __FUNCTION__ << triangleIndex << " (" << elem << ") [" << adapter->m_container->getTriangle(triangleIndex) << "]" << std::endl; + + // Check if vertices are on the boundary + // NOTE: We cannot do any complicated checking here because the topology + // may be inconsistent. + const Triangle& t = adapter->m_container->getTriangle(triangleIndex); + adapter->m_toUpdate[t[0]] = true; + adapter->m_toUpdate[t[1]] = true; + adapter->m_toUpdate[t[2]] = true; + + // Compute initial normal + adapter->computeTriangleNormal(elem, adapter->m_state->read(sofa::core::VecCoordId::restPosition())->getValue(), tInfo.normal); +} + +template +void Test2DAdapter::TriangleInfoHandler::applyDestroyFunction(unsigned int triangleIndex, TriangleInformation &/*tInfo*/) +{ + //std::cout << "tri " << __FUNCTION__ << triangleIndex << std::endl; + // Check if vertices are on the boundary + // NOTE: We cannot do any complicated checking here because the topology + // may be inconsistent. + const Triangle& t = adapter->m_container->getTriangle(triangleIndex); + adapter->m_toUpdate[t[0]] = true; + adapter->m_toUpdate[t[1]] = true; + adapter->m_toUpdate[t[2]] = true; +} + +template +void Test2DAdapter::TriangleInfoHandler::swap(unsigned int i1, + unsigned int i2) +{ + //std::cout << "tri " << __FUNCTION__ << " " << i1 << " " << i2 << std::endl; + Inherited::swap(i1, i2); + + if (adapter->m_pointTriId == i1) { + adapter->m_pointTriId = i2; + } else if (adapter->m_pointTriId == i2) { + adapter->m_pointTriId = i1; + } +} + +#if 0 +template +void Test2DAdapter::TriangleInfoHandler::addOnMovedPosition(const sofa::type::vector &indexList, + const sofa::type::vector< topology::Triangle > & elems) +{ + std::cout << "tri " << __FUNCTION__ << " " << indexList << std::endl; + Inherited::addOnMovedPosition(indexList, elems); +} + +template +void Test2DAdapter::TriangleInfoHandler::removeOnMovedPosition(const sofa::type::vector &indices) +{ + std::cout << "tri " << __FUNCTION__ << " " << indices << std::endl; + Inherited::removeOnMovedPosition(indices); +} + + +#endif + + + + + + + + +template +Test2DAdapter::Test2DAdapter() +: m_sigma(initData(&m_sigma, (Real)0.01, "sigma", "Minimal increase in functional to accept the change")) +, m_functionals(initData(&m_functionals, "functionals", "Current values of the functional for each triangle")) +//, m_mappedState(initLink("mappedState", "Points to project onto the topology.")) +, m_projectedPoints(initData(&m_projectedPoints, "projectedPoints", "Points to project onto the topology.")) +, m_interpolationIndices(initData(&m_interpolationIndices, "interpolationIndices", + "Interpolation indices for projected points.")) +, m_interpolationValues(initData(&m_interpolationValues, "interpolationValues", + "Interpolation values for projected points.")) +, stepCounter(0) +, m_precision(1e-8) +, m_pointId(InvalidID) +, m_pointTriId(InvalidID) +, m_opt(this, m_surf) +, pointInfo(initData(&pointInfo, "pointInfo", "Internal point data")) +, triInfo(initData(&triInfo, "triInfo", "Internal triangle data")) +{ + pointHandler = new PointInfoHandler(this, &pointInfo); + triHandler = new TriangleInfoHandler(this, &triInfo); +} + +template +Test2DAdapter::~Test2DAdapter() +{ + if(pointHandler) delete pointHandler; + if(triHandler) delete triHandler; +} + +template +void Test2DAdapter::init() +{ + m_state = dynamic_cast*> (this->getContext()->getMechanicalState()); + if (!m_state) { + msg_error() << "Unable to find MechanicalState" ; + return; + } + + this->getContext()->get(m_container); + if (m_container == NULL) { + msg_error() << "Unable to find triangular topology" ; + return; + } + + this->getContext()->get(m_modifier); + if (m_modifier == NULL) { + msg_error() << "Unable to find TriangleSetTopologyModifier" ; + return; + } + + this->getContext()->get(m_algoGeom); + if (m_algoGeom == NULL) { + msg_error() << "Unable to find TriangleSetGeometryAlgorithms" ; + return; + } + + this->getContext()->get(m_algoTopo); + if (m_algoTopo == NULL) { + msg_error() << "Unable to find TriangleSetTopologyAlgorithms" ; + return; + } + + pointInfo.createTopologicalEngine(m_container, pointHandler); + pointInfo.registerTopologicalData(); + + triInfo.createTopologicalEngine(m_container, triHandler); + triInfo.registerTopologicalData(); + + + reinit(); +} + +template +void Test2DAdapter::reinit() +{ + *this->f_listening.beginEdit() = true; + this->f_listening.endEdit(); + + if ((m_sigma.getValue() < (Real)0.0) || (m_sigma.getValue() > (Real)1.0)) { + msg_warning() << "The value of sigma must be between 0 and 1." ; + *m_sigma.beginEdit() = 0.01; + m_sigma.endEdit(); + } + + m_functionals.beginEdit()->resize(m_container->getNbTriangles(), Real(0.0)); + m_functionals.endEdit(); + + type::vector& pts = *pointInfo.beginEdit(); + pts.resize(m_container->getNbPoints()); + //for (int i=0; igetNbPoints(); i++) + //{ + // pointHandler->applyCreateFunction( + // i, + // pts[i], + // m_container->getPoint(i), + // (const sofa::type::vector< unsigned int > )0, + // (const sofa::type::vector< double >)0); + //} + pointInfo.endEdit(); + + + type::vector& tris = *triInfo.beginEdit(); + tris.resize(m_container->getNbTriangles()); + for (int i=0; igetNbTriangles(); i++) + { + triHandler->applyCreateFunction( + i, + tris[i], + m_container->getTriangle(i), + (const sofa::type::vector< unsigned int > )0, + (const sofa::type::vector< double >)0); + } + triInfo.endEdit(); + + recheckBoundary(); + + projectionInit(); + + stepCounter = 0; + + m_surf.init(m_container, m_state->read(sofa::core::VecCoordId::restPosition())->getValue()); +} + + +template +void Test2DAdapter::onEndAnimationStep(const double /*dt*/) +{ + //std::cout << "CPU step\n"; + + if ((m_container == NULL) || (m_state == NULL)) + return; + + stepCounter++; + + // Update boundary vertices + recheckBoundary(); + + //if (stepCounter < f_interval.getValue()) + // return; // Stay still for a few steps + + //stepCounter = 0; + + Data* datax = m_state->write(sofa::core::VecCoordId::restPosition()); + //VecCoord& x = *datax->beginEdit(); + // WARNING: Notice that we're working on a copy that is NOT updated by + // external changes! + VecCoord x = datax->getValue(); + // + const VecCoord& x0 = datax->getValue(); + + Index nTriangles = m_container->getNbTriangles(); + if (nTriangles == 0) + return; + + // Update projection of tracked point in rest shape + if (m_pointId != InvalidID) { + Triangle tri = m_container->getTriangle(m_pointTriId); + type::vector< double > bary = m_algoGeom->compute3PointsBarycoefs( + m_point, tri[0], tri[1], tri[2], false); + m_pointRest = + x0[ tri[0] ] * bary[0] + + x0[ tri[1] ] * bary[1] + + x0[ tri[2] ] * bary[2]; + } + + + //sofa::helper::system::thread::ctime_t start, stop; + //sofa::helper::system::thread::CTime timer; + //start = timer.getTime(); + + //mytimer.start("Init"); + sofa::type::vector &functionals = *m_functionals.beginEdit(); + + functionals.resize(nTriangles); + m_opt.initValues(functionals, m_container); + + //std::cout << "m: " << functionals << "\n"; + //mytimer.stop(); + + ngamma = 0; + sumgamma = maxgamma = 0.0; + mingamma = 1.0; + + Real maxdelta=0.0; + unsigned int moved=0; + //mytimer.step("Optimize"); + for (Index i=0; i maxdelta) { + maxdelta = delta; + } + + // Update projection of tracked point in rest shape + if (m_pointId == i) { + Triangle tri = m_container->getTriangle(m_pointTriId); + type::vector< double > bary = m_algoGeom->compute3PointsBarycoefs( + m_point, tri[0], tri[1], tri[2], false); + m_pointRest = + x0[ tri[0] ] * bary[0] + + x0[ tri[1] ] * bary[1] + + x0[ tri[2] ] * bary[2]; + } + } + //mytimer.stop(); + } + //mytimer.step("Eval"); + + //stop = timer.getTime(); + //std::cout << "---------- CPU time = " << stop-start << "\n"; + + // Evaluate improvement + Real sum=0.0, sum2=0.0, min = DBL_MAX; + Index minTriID = InvalidID; + for (Index i=0; i < nTriangles; i++) { + if (functionals[i] < min) { + min = functionals[i]; + minTriID = i; + } + sum += functionals[i]; + sum2 += functionals[i] * functionals[i]; + } + sum /= nTriangles; + sum2 = helper::rsqrt(sum2/nTriangles); + + // Try swapping edge for the worst triangle + //mytimer.step("Swap"); + //swapEdge(minTriID); + //NOTE: we do some work twice, this can be optimized. + //for (Index i=0; igetNumberOfTriangles(); i++) { + // swapEdge(i); + //} + //mytimer.stop(); + + + //std::cout << stepCounter << "] moved " << moved << " points, max delta=" << helper::rsqrt(maxdelta) + // << " gamma min/avg/max: " << mingamma << "/" << sumgamma/ngamma + // << "/" << maxgamma + + // << " Quality min/avg/RMS: " << min << "/" << sum << "/" << sum2 + + // << "\n"; + + datax->endEdit(); + + m_functionals.endEdit(); + + //// Write metrics to file + //std::ofstream of("/tmp/metrics.csv", std::ios::app); + //of << "geom," << stepCounter; + //for (Index i=0; i < m_functionals.getValue().size(); i++) { + // of << "," << m_functionals.getValue()[i]; + //} + //of << "\n"; + //of.close(); +} + +template +void Test2DAdapter::onKeyPressedEvent(core::objectmodel::KeypressedEvent *key) +{ + if (key->getKey() == 'M') { // Ctrl+M + std::cout << "Writing metrics to file /tmp/metrics.csv\n"; + // Write metrics to file + std::ofstream of("/tmp/metrics.csv", std::ios::app); + of << "geom," << stepCounter; + for (Index i=0; i < m_functionals.getValue().size(); i++) { + of << "," << m_functionals.getValue()[i]; + } + of << "\n"; + of.close(); + } +} + +#if 0 // TODO +template +void Test2DAdapter::swapEdge(Index triID) +{ + Index swapTri = InvalidID; + Real swapVal1, swapVal2, swapMin = DBL_MIN; + Triangle swapT1, swapT2; + // Keep old value + Real oldValue = m_functionals.getValue()[triID]; + + const VecCoord& x0 = m_state->read( + sofa::core::ConstVecCoordId::restPosition())->getValue(); + const VecCoord& x = m_state->read( + sofa::core::ConstVecCoordId::position())->getValue(); + const Triangle &t1 = m_container->getTriangle(triID); + + const EdgesInTriangle &elist = m_container->getEdgesInTriangle(triID); + + int orientation0 = CCW(x0[t1[0]], x0[t1[1]], x0[t1[2]]); + int orientation = CCW(x[t1[0]], x[t1[1]], x[t1[2]]); + + for (int ie=0; ie < 3; ie++) { + + const TrianglesAroundEdge &tlist = + m_container->getTrianglesAroundEdge(elist[ie]); + if (tlist.size() != 2) + continue; // No other triangle or non-manifold edge + + bool bProtected = false; + for (unsigned int i=0; igetEdge(elist[ie]); + const Triangle &t2 = m_container->getTriangle(triID2); + + if (orientation0 != CCW(x0[t2[0]], x0[t2[1]], x0[t2[2]])) { + std::cout << "Unable to swap edge for triangles with different " + "orientation in rest shape.\n"; + continue; + } else if (orientation != CCW(x[t2[0]], x[t2[1]], x[t2[2]])) { + std::cout << "Unable to swap edge for triangles with different " + "orientation.\n"; + continue; + } + + // Detect indices of what + Index ne1 = InvalidID, ne2 = InvalidID; // Points not on the shared edge + int swap1 = -1, swap2 = -1; + for (int i=0; i<3; i++) { + if ((t1[i] != e[0]) && (t1[i] != e[1])) { + ne1 = t1[i]; + } else if (t1[i] == e[0]) { + swap1 = i; + } + if ((t2[i] != e[0]) && (t2[i] != e[1])) { + ne2 = t2[i]; + } else if (t2[i] == e[1]) { + swap2 = i; + } + } + + + // Swap edge shared between the triangles + Triangle nt1 = t1, nt2 = t2; + nt1[swap1] = ne2; + nt2[swap2] = ne1; + + // Check if this helps + Real newValue = funcTriangle(nt1, x0, + triInfo.getValue()[triID].normal); + Real newValue2 = funcTriangle(nt2, x0, + triInfo.getValue()[triID2].normal); + + // Is there improvement? + // NOTE: We assume that oldValue < oldValue2. + if ((newValue > oldValue) && (newValue2 > oldValue) && + (newValue > swapMin) && (newValue2 > swapMin)) { + // Yes, this helps! + swapTri = triID2; + swapMin = (newValue < newValue2) ? newValue : newValue2; + swapVal1 = newValue; + swapVal2 = newValue2; + swapT1 = nt1; + swapT2 = nt2; + } + } + + // Perform the topological operation + if (swapTri != InvalidID) { + //std::cout << "Swapping\n"; + + if ((triID == m_pointTriId) || (swapTri == m_pointTriId)) { + std::cout << "Cannot swap tracked triangle!\n"; + // TODO: we need to project tracked position to find out which + // of the triangles contains it. + } + + //if (CCW(x0[swapT1[0]], x0[swapT1[1]], x0[swapT1[2]]) != + // CCW(x0[swapT2[0]], x0[swapT2[1]], x0[swapT2[2]])) { + // std::cout << "WHOOPS! Inverting triangle! (rest)\n"; + //} else if (CCW(x[swapT1[0]], x[swapT1[1]], x[swapT1[2]]) != + // CCW(x[swapT2[0]], x[swapT2[1]], x[swapT2[2]])) { + // std::cout << "WHOOPS! Inverting triangle!\n"; + //} + + sofa::type::vector< unsigned int > del; + del.push_back(triID); + del.push_back(swapTri); + m_modifier->removeTriangles(del, true, false); + + sofa::type::vector add; + add.push_back(swapT1); + add.push_back(swapT2); + m_modifier->addTriangles(add); + } + + // TODO: projection +} +#endif + +template +void Test2DAdapter::computeTriangleNormal(const Triangle &t, const VecCoord &x, Vec3 &normal) const +{ + Vec3 A, B; + A = x[ t[1] ] - x[ t[0] ]; + B = x[ t[2] ] - x[ t[0] ]; + + Real An = A.norm(), Bn = B.norm(); + if (An < 1e-20 || Bn < 1e-20) { + msg_warning() << "Found degenerated triangle: " + << x[ t[0] ] << " / " << x[ t[1] ] << " / " << x[ t[2] ] + << " :: " << An << ", " << Bn ; + + normal = Vec3(0,0,0); + return; + } + + A /= An; + B /= Bn; + normal = cross(A, B); + normal.normalize(); +} + +template +void Test2DAdapter::recheckBoundary() +{ + type::vector& pts = *pointInfo.beginEdit(); + for (std::map::const_iterator i=m_toUpdate.begin(); + i != m_toUpdate.end(); i++) { + pts[i->first].type = detectNodeType(i->first, pts[i->first].boundary); + } + pointInfo.endEdit(); + m_toUpdate.clear(); +} + +template +void Test2DAdapter::relocatePoint(Index pt, Coord target, + Index hint, bool bInRest) +{ + if (m_modifier == NULL || + m_algoGeom == NULL || + m_container == NULL || + m_state == NULL) + return; + + //mytimer.step("Relocate"); + + Index tId = hint; + + const VecCoord& x = m_state->read( + bInRest + ? sofa::core::ConstVecCoordId::restPosition() + : sofa::core::ConstVecCoordId::position() + )->getValue(); + + if ((x[pt] - target).norm() < m_precision) return; // Nothing to do + + if (tId == InvalidID) { + tId = m_algoGeom->getTriangleInDirection(pt, target - x[pt]); + } + + if (tId == InvalidID) { + // Try once more and more carefully + TrianglesAroundVertex N1 = m_container->getTrianglesAroundVertex(pt); + for (Index it=0; itisPointInsideTriangle(N1[it], false, target, t, bInRest)) { + Triangle tri = m_container->getTriangle(N1[it]); + type::vector bary = + m_algoGeom->compute3PointsBarycoefs( + target, tri[0], tri[1], tri[2], bInRest); + bool bGood = true; + for (int bc=0; bc<3; bc++) { + if (bary[bc] < -1e-15) { + bGood = false; + break; + } + } + + if (!bGood) continue; + + tId = N1[it]; + break; + } + } + } + + if (tId == InvalidID) { + // We screwed up something. Probably a triangle got inverted accidentaly. + msg_warning() << "Unexpected triangle Id -1! Cannot move point " + << pt << ", marking point as fixed." ; + + (*pointInfo.beginEdit())[pt].forceFixed = true; + pointInfo.endEdit(); + + //EdgesAroundVertex N1e = m_container->getEdgesAroundVertex(pt); + //for (Index ip=0; ipgetEdge(N1e[ip]); + // std::cout << m_container->getPointDataArray().getValue()[ e[0] ] << " " << m_container->getPointDataArray().getValue()[ e[1] ] << "\n"; + //} + + return; + } + + Triangle tri = m_container->getTriangle(tId); + + type::vector move_ids; + type::vector< type::vector< unsigned int > > move_ancestors; + type::vector< type::vector< double > > move_coefs; + + move_ids.push_back(pt); + + move_ancestors.resize(1); + move_ancestors.back().push_back(tri[0]); + move_ancestors.back().push_back(tri[1]); + move_ancestors.back().push_back(tri[2]); + + move_coefs.push_back( + m_algoGeom->compute3PointsBarycoefs(target, + tri[0], tri[1], tri[2], bInRest)); + + // Do the real work + m_modifier->movePointsProcess(move_ids, move_ancestors, move_coefs); + m_modifier->notifyEndingEvent(); + m_modifier->propagateTopologicalChanges(); + + //mytimer.step("Projection"); + projectionUpdate(pt); + + // Check + //const VecCoord& xnew = m_state->read( + // sofa::core::ConstVecCoordId::restPosition())->getValue(); + //std::cout << "requested " << target << "\tgot " << xnew[pt] << "\tdelta" + // << (target - xnew[pt]).norm() << "\n"; +} + +template +typename Test2DAdapter::PointInformation::NodeType Test2DAdapter::detectNodeType(Index pt, Vec3 &boundaryDirection) +{ + if (m_container == NULL) + return PointInformation::NORMAL; + + const VecCoord& x0 = m_state->read(sofa::core::ConstVecCoordId::restPosition())->getValue(); + + VecVec3 dirlist; // Directions of boundary edges + + //std::cout << "checking " << pt << "\n"; + EdgesAroundVertex N1e = m_container->getEdgesAroundVertex(pt); + for (Index ie=0; iegetTrianglesAroundEdge(N1e[ie]); + unsigned int count = tlist.size(); + //std::cout << " " << count << " (" << tlist << ")\n"; + if (count < 1 || count > 2) { + // What a strange topology! Better not touch ... + return PointInformation::FIXED; + } else if (count == 1) { + Edge e = m_container->getEdge(N1e[ie]); + Vec3 dir; + if (e[0] == pt) { + dir = x0[e[1]] - x0[e[0]]; + } else { + dir = x0[e[0]] - x0[e[1]]; + } + dir.normalize(); + dirlist.push_back(dir); + } + } + + if (dirlist.size() == 0) { + // No boundary edges + return PointInformation::NORMAL; + } else if (dirlist.size() != 2) { + // What a strange topology! Better not touch ... + return PointInformation::FIXED; + } + + if ((1.0 + dirlist[0] * dirlist[1]) > 1e-15) { + // Not on a line + return PointInformation::FIXED; + } + + boundaryDirection = dirlist[0]; + + return PointInformation::BOUNDARY; +} + +template +void Test2DAdapter::projectionInit() +{ + if (!m_container) return; + + const VecCoord& x0= m_state->read( + sofa::core::ConstVecCoordId::restPosition())->getValue(); + + const VecCoord& xProj = m_projectedPoints.getValue(); + unsigned int nVertices = xProj.size(); + + sofa::type::vector > &indices = + *m_interpolationIndices.beginEdit(); + sofa::type::vector > &values = + *m_interpolationValues.beginEdit(); + + indices.resize(nVertices); + values.resize(nVertices); + + type::vector& tris = *triInfo.beginEdit(); + // Clear list of previously attached points. + for (Index i=0; igetNumberOfTriangles(); i++) { + tris[i].attachedPoints.clear(); + } + + PointProjection proj(*m_container); + + for (Index i=0; igetTriangle(triangleID); + indices[i].clear(); + indices[i].push_back(t[0]); + indices[i].push_back(t[1]); + indices[i].push_back(t[2]); + + // Add the barycentric coordinates to the list + values[i].clear(); + values[i].push_back(vertexBaryCoord[0]); + values[i].push_back(vertexBaryCoord[1]); + values[i].push_back(vertexBaryCoord[2]); + } + + m_interpolationIndices.endEdit(); + m_interpolationValues.endEdit(); + triInfo.endEdit(); +} + +template +void Test2DAdapter::projectionUpdate(Index pt) +{ + if (!m_container) return; + + PointProjection proj(*m_container); + + const VecCoord& x0= m_state->read( + sofa::core::ConstVecCoordId::restPosition())->getValue(); + + const VecCoord& xProj = m_projectedPoints.getValue(); + + sofa::type::vector > &indices = + *m_interpolationIndices.beginEdit(); + sofa::type::vector > &values = + *m_interpolationValues.beginEdit(); + + type::vector& tris = *triInfo.beginEdit(); + + TrianglesAroundVertex N1 = m_container->getTrianglesAroundVertex(pt); + + // List of newly attached points for each triangle + // We keep it separated to avoid double work. + std::map > newAttached; + + // Recompute all barycentric coordinates + for (unsigned int it=0; itgetTriangle(t); + + sofa::type::vector oldAttached = tris[t].attachedPoints; + tris[t].attachedPoints.clear(); + + for (unsigned int ip=0; ipgetTriangle(newTri); + indices[ptAttached][0] = newTri2[0]; + indices[ptAttached][1] = newTri2[1]; + indices[ptAttached][2] = newTri2[2]; + + // Update barycentric coordinates + values[ptAttached][0] = newBary[0]; + values[ptAttached][1] = newBary[1]; + values[ptAttached][2] = newBary[2]; + } + } + + // Add newly attached points to the lists + for (std::map >::const_iterator i = + newAttached.begin(); + i != newAttached.end(); i++) { + for (unsigned int j=0; j < i->second.size(); j++) { + tris[i->first].attachedPoints.push_back(i->second[j]); + } + } + + m_interpolationIndices.endEdit(); + m_interpolationValues.endEdit(); + triInfo.endEdit(); +} + +template +void Test2DAdapter::draw(const core::visual::VisualParams* vparams) +{ + if ((!vparams->displayFlags().getShowBehaviorModels())) + return; + + if (!m_state) return; + const VecCoord& x = m_state->read(sofa::core::ConstVecCoordId::position())->getValue(); + + type::vector boundary; + type::vector fixed; + const type::vector &pts = pointInfo.getValue(); + if (pts.size() != x.size()) return; + for (Index i=0; i < x.size(); i++) { + if (pts[i].isFixed()) { + fixed.push_back(x[i]); + } else if (pts[i].isBoundary()) { + boundary.push_back(x[i]); + } + } + vparams->drawTool()->drawPoints(boundary, 10, + type::RGBAColor(0.5, 0.5, 1.0, 1.0)); + vparams->drawTool()->drawPoints(fixed, 10, + type::RGBAColor(0.8, 0.0, 0.8, 1.0)); + + if (m_pointId != InvalidID) { + // Draw tracked position + type::vector vv; + vv.push_back( + type::Vector3(m_point[0], m_point[1], m_point[2])); + vparams->drawTool()->drawPoints(vv, 6, + type::RGBAColor(1.0, 1.0, 1.0, 1.0)); + // Draw tracked position (projected in rest shape) + //vv.clear(); + //vv.push_back(m_pointRest); + //vparams->drawTool()->drawPoints(vv, 4, + // type::RGBAColor(1.0, 1.0, 0.0, 1.0)); + // Draw attached point + vv.clear(); + vv.push_back(x[m_pointId]); + vparams->drawTool()->drawPoints(vv, 6, + /*cutting() + ? type::RGBAColor(1.0, 1.0, 0.0, 1.0) + :*/ type::RGBAColor(1.0, 1.0, 1.0, 1.0)); + } + + + if (m_protectedEdges.size() > 0) { + type::vector points; + for (VecIndex::const_iterator i=m_protectedEdges.begin(); + i != m_protectedEdges.end(); i++) { + const Edge &e = m_container->getEdge(*i); + points.push_back(x[ e[0] ]); + points.push_back(x[ e[1] ]); + } + vparams->drawTool()->drawLines(points, 4, + type::RGBAColor(0.0, 1.0, 0.0, 1.0)); + } + + m_surf.draw(vparams); +} + +} // namespace controller + +} // namespace component + +} // namespace sofa + +#undef OTHER +#undef CCW + +#endif // SOFA_COMPONENT_CONTROLLER_TEST2DADAPTER_INL diff --git a/src/Shell/controller/TriangleSwitchExample.cpp b/src/Shell/controller/TriangleSwitchExample.cpp new file mode 100644 index 0000000..8b2c763 --- /dev/null +++ b/src/Shell/controller/TriangleSwitchExample.cpp @@ -0,0 +1,31 @@ +#define SOFA_COMPONENT_CONTROLLER_TRIANGLESWITCHEXAMPLE_CPP + +#include +#include +#include + + +namespace sofa +{ + +namespace component +{ + +namespace controller +{ + +using namespace sofa::defaulttype; + + +// Register in the Factory +int TriangleSwitchExampleClass = core::RegisterObject("Repeatedly switches topology of two neighbouring triangles in predefined interval") +.add< TriangleSwitchExample >(true) // default template +; + +template class SOFA_SHELL_API TriangleSwitchExample; + +} // namespace controller + +} // namespace component + +} // namespace sofa diff --git a/src/Shell/controller/TriangleSwitchExample.h b/src/Shell/controller/TriangleSwitchExample.h new file mode 100644 index 0000000..f9c4309 --- /dev/null +++ b/src/Shell/controller/TriangleSwitchExample.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace sofa::component::controller +{ + +template +class TriangleSwitchExample : public Controller +{ +public: + SOFA_CLASS(SOFA_TEMPLATE(TriangleSwitchExample,DataTypes),Controller); + + typedef typename DataTypes::VecCoord VecCoord; + typedef typename DataTypes::VecDeriv VecDeriv; + typedef typename DataTypes::Coord Coord; + typedef typename DataTypes::Deriv Deriv; + typedef typename Coord::value_type Real; + typedef sofa::type::Vec<3,Real> Vec3; + + typedef topology::container::dynamic::TriangleSetTopologyContainer::TriangleID Index; + typedef topology::container::dynamic::TriangleSetTopologyContainer::Triangle Triangle; + typedef topology::container::dynamic::TriangleSetTopologyContainer::SeqTriangles SeqTriangles; + typedef sofa::type::vector VecIndex; + +protected: + + TriangleSwitchExample(); + + virtual ~TriangleSwitchExample(); + +public: + + void init() override; + void reinit() override; + + Data f_interval; + + void onEndAnimationStep(const double dt) override; + + void computeTriangleNormal(const Triangle &t, Vec3 &normal); + +private: + + unsigned int stepCounter; + topology::container::dynamic::TriangleSetTopologyContainer* m_container; + topology::container::dynamic::TriangleSetTopologyModifier* m_modifier; + sofa::core::behavior::MechanicalState* m_state; +}; +} // namespace + diff --git a/src/Shell/controller/TriangleSwitchExample.inl b/src/Shell/controller/TriangleSwitchExample.inl new file mode 100644 index 0000000..0b4356f --- /dev/null +++ b/src/Shell/controller/TriangleSwitchExample.inl @@ -0,0 +1,195 @@ +#ifndef SOFA_COMPONENT_CONTROLLER_TRIANGLESWITCHEXAMPLE_INL +#define SOFA_COMPONENT_CONTROLLER_TRIANGLESWITCHEXAMPLE_INL + +#include + +namespace sofa +{ + +namespace component +{ + +namespace controller +{ + +template +TriangleSwitchExample::TriangleSwitchExample() +: f_interval(initData(&f_interval, (unsigned int)10, "interval", "Switch triangles every number of steps")) +, stepCounter(0) +{ +} + +template +TriangleSwitchExample::~TriangleSwitchExample() +{ +} + +template +void TriangleSwitchExample::init() +{ + m_state = dynamic_cast*> (this->getContext()->getMechanicalState()); + if (!m_state) + msg_error() << "Unable to find MechanicalState."; + + this->getContext()->get(m_container); + if (m_container == NULL) + msg_error() << "Unable to find triangular topology."; + + this->getContext()->get(m_modifier); + if (m_modifier == NULL) + msg_error() << "Unable to find TriangleSetTopologyModifier."; + + reinit(); +} + +template +void TriangleSwitchExample::reinit() +{ + *this->f_listening.beginEdit() = true; + this->f_listening.endEdit(); + + // Check interval + if (f_interval.getValue() == 0) { + msg_warning() << "interval has to be nonzero."; + *f_interval.beginEdit() = 1; + f_interval.endEdit(); + } + + stepCounter = 0; +} + + +template +void TriangleSwitchExample::onEndAnimationStep(const double /*dt*/) +{ + if ((m_container == NULL) || (m_modifier == NULL) || (m_state == NULL)) + return; + + stepCounter++; + + if (stepCounter < f_interval.getValue()) + return; // Stay still for a few steps + + stepCounter = 0; + + + SeqTriangles add_list; + VecIndex remove_list; + + for (Index i=0; i < m_container->getNbTriangles()-1; i+=2) { + Triangle t1, t2, t1n, t2n; + t1 = m_container->getTriangle(i); + t2 = m_container->getTriangle(i+1); + + remove_list.push_back(i); + remove_list.push_back(i+1); + + // Find common vertices (the shared edge) and the other two + VecIndex common, other; + + int v1, v2; + for (v1=0; v1 < 3; v1++) { + for (v2=0; v2 < 3; v2++) { + if (t1[v1] == t2[v2]) break; + } + if (v2 < 3) { + // found + common.push_back(t1[v1]); + } else { + // not found + other.push_back(t1[v1]); + } + } + + for (v2=0; v2 < 3; v2++) { + for (v1=0; v1 < 3; v1++) { + if (t1[v1] == t2[v2]) break; + } + if (v1 >= 3) { + // not found + other.push_back(t2[v2]); + break; + } + } + + if ((common.size() != 2) || (other.size() != 2)) { + msg_error() << "Invalid triangle pair " << i << ", " << i+1; + continue; + } + + //std::cout << "common= " << common << " other= " << other << std::endl; + + // Construct new triangles + t1n[0] = t2n[0] = other[0]; + t1n[1] = t2n[1] = other[1]; + t1n[2] = common[0]; + t2n[2] = common[1]; + + // Make sure the vertex order of the triangle is correct + + Vec3 nt1, nt1n; + computeTriangleNormal(t1, nt1); + computeTriangleNormal(t1n, nt1n); + if (dot(nt1, nt1n) > 0) { + // Invert the vertex order + Index tmp = t1n[1]; + t1n[1] = t1n[2]; + t1n[2] = tmp; + } + + Vec3 nt2, nt2n; + computeTriangleNormal(t2, nt2); + computeTriangleNormal(t2n, nt2n); + if (dot(nt2, nt2n) > 0) { + // Invert the vertex order + Index tmp = t2n[1]; + t2n[1] = t2n[2]; + t2n[2] = tmp; + } + + // Add triangles to the list + add_list.push_back(t1n); + add_list.push_back(t2n); + } + + // Add new and remove old triangles + m_modifier->addTriangles(add_list); + m_modifier->removeTriangles(remove_list, true, true); + m_modifier->notifyEndingEvent(); +} + +// Computes triangle normal +template +void TriangleSwitchExample::computeTriangleNormal(const Triangle &t, Vec3 &normal) +{ + const VecCoord& x = m_state->read(sofa::core::vec_id::read_access::restPosition)->getValue(); + + //std::cout << "tri: " << t << " n=" << x.size() << std::endl; + + Vec3 A, B; + A = x[t[1]].getCenter() - x[t[0]].getCenter(); + B = x[t[2]].getCenter() - x[t[0]].getCenter(); + + Real An = A.norm(), Bn = B.norm(); + if (An < 1e-20 || Bn < 1e-20) { + msg_error() << "Found degenerated triangle " << t << + " -- " << x[t[0]].getCenter() << " / " << x[t[1]].getCenter() << + " / " << x[t[2]].getCenter() << " :: " << An << ", " << Bn; + + normal = Vec3(0,0,0); + return; + } + + A.normalize(); + B.normalize(); + normal = cross(A, B); + normal.normalize(); +} + +} // namespace controller + +} // namespace component + +} // namespace sofa + +#endif // SOFA_COMPONENT_CONTROLLER_TRIANGLESWITCHEXAMPLE_INL diff --git a/src/Shell/cutting/AdaptiveCutting.cpp b/src/Shell/cutting/AdaptiveCutting.cpp new file mode 100644 index 0000000..2e98793 --- /dev/null +++ b/src/Shell/cutting/AdaptiveCutting.cpp @@ -0,0 +1,123 @@ +#include + +#include +#include +#include +#include +#include + +namespace sofa +{ + +namespace component +{ + +// ---------- Settings ----------------------------------------------------- + +namespace configurationsetting +{ + +SOFA_DECL_CLASS(AdaptiveCuttingSetting) +int AdaptiveCuttingSettingClass = core::RegisterObject("Configuration for adaptive cutting operation.") + .add< AdaptiveCuttingSetting >() + //.addAlias("AdaptiveCutting") +; + +} // namespace configurationsetting + +// ---------- Performer ---------------------------------------------------- + +namespace collision +{ + +#ifndef SOFA_DOUBLE +helper::Creator< + InteractionPerformer::InteractionPerformerFactory, + AdaptiveCuttingPerformer > + AdaptiveCuttingPerformerVec3fClass("AdaptiveCutting", true); +#endif +#ifndef SOFA_FLOAT +helper::Creator< + InteractionPerformer::InteractionPerformerFactory, + AdaptiveCuttingPerformer > + AdaptiveCuttingPerformerVec3dClass("AdaptiveCutting", true); +#endif + +#ifndef SOFA_DOUBLE +template class SOFA_SHELLS_API AdaptiveCuttingPerformer; +//template class SOFA_SHELLS_API AdaptiveCuttingPerformer; +#endif +#ifndef SOFA_FLOAT +template class SOFA_SHELLS_API AdaptiveCuttingPerformer; +//template class SOFA_SHELLS_API AdaptiveCuttingPerformer; +#endif + +} // namespace collision + +} // namespace component + + +// ---------- Operation ---------------------------------------------------- + +namespace gui +{ + +int AdaptiveCuttingOperationReg = sofa::gui::RegisterOperation("AdaptiveCutting") + .add< AdaptiveCuttingOperation >(); + +void AdaptiveCuttingOperation::start() +{ + // Add cutting point + CuttingAdapter *ca = getAdapter(); + if (ca) { + ca->addCuttingPoint(); + } + Operation::start(); // TODO: do we need this? +} + + +void AdaptiveCuttingOperation::endOperation() +{ + // Free the tracked point + CuttingAdapter *ca = getAdapter(); + if (ca) { + ca->freeTrackedPoint(); + } + this->end(); // TODO: do we need this? +} + +void AdaptiveCuttingOperation::wait() +{ + // Update the position in the adaptivity component + if (!pickHandle) return; + sofa::component::collision::BodyPicked *picked = pickHandle->getLastPicked(); + if (!picked) return; + + CuttingAdapter *ca = getAdapter(); + if (ca) { + ca->setTrackedPoint(*picked); + } +} + +component::controller::CuttingAdapter* AdaptiveCuttingOperation::getAdapter() +{ + if (!pickHandle) return NULL; + + sofa::component::collision::BodyPicked *picked = pickHandle->getLastPicked(); + if (!picked) return NULL; + + component::controller::CuttingAdapter *ca = NULL; + if (picked->body) { + if (!picked->body->getContext()) std::cout << "no context!\n"; + picked->body->getContext()->get(ca); + } else if (picked->mstate) { + if (!picked->mstate->getContext()) std::cout << "no context!2\n"; + picked->mstate->getContext()->get(ca); + } + + return ca; +} + +} // namespace gui + +} // namespace sofa diff --git a/src/Shell/cutting/AdaptiveCutting.h b/src/Shell/cutting/AdaptiveCutting.h new file mode 100644 index 0000000..4d3549c --- /dev/null +++ b/src/Shell/cutting/AdaptiveCutting.h @@ -0,0 +1,151 @@ +#ifndef SOFA_GUI_ADAPTIVECUTTING_H +#define SOFA_GUI_ADAPTIVECUTTING_H + +#include +#include +#include +#include +#include + +#include + +namespace sofa +{ + +namespace component +{ + +// ---------- Settings ----------------------------------------------------- + +namespace configurationsetting +{ + +class SOFA_SHELLS_API AdaptiveCuttingSetting: public MouseButtonSetting +{ +public: + SOFA_CLASS(AdaptiveCuttingSetting, MouseButtonSetting); + +protected: + AdaptiveCuttingSetting() { } + +public: + std::string getOperationType() override { return "AdaptiveCutting"; } +}; + +} // namespace configurationsetting + +// ---------- Performer ---------------------------------------------------- + +namespace collision +{ + +template +class AdaptiveCuttingPerformer: public TInteractionPerformer +{ +public: + //typedef sofa::component::collision::BaseContactMapper< DataTypes > MouseContactMapper; + //typedef sofa::core::behavior::MechanicalState< DataTypes > MouseContainer; + //typedef sofa::core::behavior::BaseForceField MouseForceField; + + AdaptiveCuttingPerformer(BaseMouseInteractor *i) : TInteractionPerformer(i) + {} + + void start() { + //std::cout << __PRETTY_FUNCTION__ << " not implemented" << std::endl; + /*TODO*/ + } + + void execute() { + //std::cout << __PRETTY_FUNCTION__ << " not implemented" << std::endl; + //updatePosition(); + /* TODO */ + } + + //void updatePosition() { + // if (!this->interactor) return; + // BodyPicked picked = this->interactor->getBodyPicked(); + + // if (!picked.body && !picked.mstate) return; + // core::behavior::MechanicalState* mstate=NULL; + + // if (picked.body) { + // mstate = dynamic_cast*>( + // picked.body->getContext()->getMechanicalState()); + // } else { + // mstate = dynamic_cast*>( + // picked.mstate); + // } + + // if (!mstate) + // { + // this->interactor->serr + // << "Incompatible MState during Mouse Interaction" + // << this->interactor->sendl; + // return; + // } + + // sofa::component::controller::Test2DAdapter* adapter; + // mstate->getContext()->get(adapter); + + // if (!adapter) return; + // adapter->setTrackedPoint(picked); + //} + + + void configure(configurationsetting::MouseButtonSetting* setting) + { + configurationsetting::AdaptiveCuttingSetting* s = dynamic_cast(setting); + if (s) + { + //std::cout << __PRETTY_FUNCTION__ << " not implemented" << std::endl; /*TODO*/ + } + } +}; + +} // namespace collision + +} // namespace component + + +// ---------- Operation ---------------------------------------------------- + +namespace gui +{ + +class SOFA_SHELLS_API AdaptiveCuttingOperation : public Operation +{ +public: + + typedef component::controller::CuttingAdapter CuttingAdapter; + + AdaptiveCuttingOperation(sofa::component::configurationsetting::AdaptiveCuttingSetting::SPtr s = sofa::core::objectmodel::New()) : Operation(s) + {} + //virtual ~AdaptiveCuttingOperation() {} + + /// This function is called each time the mouse is clicked. + void start(); + //void execution() { std::cout << __PRETTY_FUNCTION__ << " not implemented" << std::endl; /* TODO */ Operation::execution();} + /// This function is called after each mouse click. + //void end(); { std::cout << __PRETTY_FUNCTION__ << " not implemented" << std::endl; /* TODO */ Operation::end();} + /// This function is called when shift key is released. + void endOperation(); + /// Called on mouse move + void wait(); + + static std::string getDescription() {return "Cutting with adaptive mesh";} + + std::string defaultPerformerType() { return "AdaptiveCutting"; } + +private: + + /// Get the adaptive component associated with selected object. + CuttingAdapter* getAdapter(); + + +}; + +} // namespace gui + +} // namespace sofa + +#endif // #ifndef SOFA_GUI_ADAPTIVECUTTING_H diff --git a/src/Shell/engine/FindClosePoints.cpp b/src/Shell/engine/FindClosePoints.cpp new file mode 100644 index 0000000..ec12e11 --- /dev/null +++ b/src/Shell/engine/FindClosePoints.cpp @@ -0,0 +1,36 @@ +#define SOFA_COMPONENT_ENGINE_FINDCLOSEPOINTS_CPP +#include +#include +#include + +namespace sofa +{ + +namespace component +{ + +namespace engine +{ + +int FindClosePointsClass = core::RegisterObject( + "Finds points whose distance is smaller than defined threshold") + +.add< FindClosePoints >(true) // default template +.add< FindClosePoints >() +.add< FindClosePoints >() +.add< FindClosePoints >() +.add< FindClosePoints >() +; + +template class SOFA_SHELL_API FindClosePoints; +template class SOFA_SHELL_API FindClosePoints; +template class SOFA_SHELL_API FindClosePoints; +template class SOFA_SHELL_API FindClosePoints; +template class SOFA_SHELL_API FindClosePoints; + + +} // namespace constraint + +} // namespace component + +} // namespace sofa diff --git a/src/Shell/engine/FindClosePoints.h b/src/Shell/engine/FindClosePoints.h new file mode 100644 index 0000000..8797d10 --- /dev/null +++ b/src/Shell/engine/FindClosePoints.h @@ -0,0 +1,80 @@ +#ifndef SOFA_COMPONENT_ENGINE_FINDCLOSEPOINTS_H +#define SOFA_COMPONENT_ENGINE_FINDCLOSEPOINTS_H + +#include +#include +#include +#include +#include + +namespace sofa +{ + +namespace component +{ + +namespace engine +{ + +/** + * @brief Finds points whose distance is smaller than defined threshold. + * + * @tparam DataTypes Associated data type. + */ +template +class FindClosePoints : public sofa::core::DataEngine +{ +public: + SOFA_CLASS(SOFA_TEMPLATE(FindClosePoints,DataTypes),core::DataEngine); + + typedef typename DataTypes::VecCoord VecCoord; + typedef typename DataTypes::Coord Coord; + typedef typename Coord::value_type Real; + + typedef unsigned int Index; + +protected: + FindClosePoints(); + + virtual ~FindClosePoints(); + +public: + + void init() override; + void reinit() override; + void doUpdate() override; + + // Data + Data f_input_threshold; + Data f_input_position; + + Data< type::vector< type::fixed_array > > f_output_closePoints; + +}; + +#if defined(WIN32) && !defined(SOFA_COMPONENT_ENGINE_FINDCLOSEPOINTS_CPP) +#pragma warning(disable : 4231) +#ifndef SOFA_FLOAT +template class SOFA_SHELL_API FindClosePoints; +template class SOFA_SHELL_API FindClosePoints; +template class SOFA_SHELL_API FindClosePoints; +template class SOFA_SHELL_API FindClosePoints; +template class SOFA_SHELL_API FindClosePoints; +#endif //SOFA_FLOAT +#ifndef SOFA_DOUBLE +template class SOFA_SHELL_API FindClosePoints; +template class SOFA_SHELL_API FindClosePoints; +template class SOFA_SHELL_API FindClosePoints; +template class SOFA_SHELL_API FindClosePoints; +template class SOFA_SHELL_API FindClosePoints; +#endif //SOFA_DOUBLE + + +#endif + +} // namespace engine + +} // namespace component + +} // namespace sofa +#endif // #ifndef SOFA_COMPONENT_ENGINE_FINDCLOSEPOINTS_H diff --git a/src/Shell/engine/FindClosePoints.inl b/src/Shell/engine/FindClosePoints.inl new file mode 100644 index 0000000..88b989c --- /dev/null +++ b/src/Shell/engine/FindClosePoints.inl @@ -0,0 +1,78 @@ +#ifndef SOFA_COMPONENT_ENGINE_FINDCLOSEPOINTS_INL +#define SOFA_COMPONENT_ENGINE_FINDCLOSEPOINTS_INL + +#include + +namespace sofa +{ + +namespace component +{ + +namespace engine +{ + +//using namespace sofa::helper; +using namespace sofa::type; +using namespace core::objectmodel; + +template +FindClosePoints::FindClosePoints() +: f_input_threshold(initData(&f_input_threshold, (Real)1e-5, "threshold","Threshold")) +, f_input_position(initData(&f_input_position,"position","Vertices")) +, f_output_closePoints(initData(&f_output_closePoints,"closePoints","Pairs of indices of points considered close")) +{ +} + +template +FindClosePoints::~FindClosePoints() +{ +} + +template +void FindClosePoints::init() +{ + addInput(&f_input_threshold); + addInput(&f_input_position); + addOutput(&f_output_closePoints); + + setDirtyValue(); +} + +template +void FindClosePoints::reinit() +{ + update(); +} + +template +void FindClosePoints::doUpdate() +{ + + Real threshold = f_input_threshold.getValue(); + const VecCoord& points = f_input_position.getValue(); + + type::vector< type::fixed_array >& list = *f_output_closePoints.beginEdit(); + list.clear(); + + if (points.size() > 1) { + for (Index i=0; i(i,j)); + } + } + } + } + + + f_output_closePoints.endEdit(); +} + +} // namespace engine + +} // namespace component + +} // namespace sofa + +#endif // #ifndef SOFA_COMPONENT_ENGINE_FINDCLOSEPOINTS_INL diff --git a/src/Shell/forcefield/BezierTriangularBendingFEMForceField.cpp b/src/Shell/forcefield/BezierTriangularBendingFEMForceField.cpp new file mode 100644 index 0000000..e61b39c --- /dev/null +++ b/src/Shell/forcefield/BezierTriangularBendingFEMForceField.cpp @@ -0,0 +1,56 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#define SOFA_COMPONENT_FORCEFIELD_BEZIER_TRIANGULAR_BENDING_FEM_FORCEFIELD_CPP +#include +#include +#include +#include +#include +#include + + +namespace sofa +{ + +namespace component +{ + +namespace forcefield +{ + +using namespace sofa::defaulttype; + +// Register in the Factory +int BezierTriangularBendingFEMForceFieldClass = core::RegisterObject("Triangular finite elements with bending based on Bézier triangle formulation") +.add< BezierTriangularBendingFEMForceField >() +; + +template class SOFA_SHELL_API BezierTriangularBendingFEMForceField; + +} // namespace forcefield + +} // namespace component + +} // namespace sofa diff --git a/src/Shell/forcefield/BezierTriangularBendingFEMForceField.h b/src/Shell/forcefield/BezierTriangularBendingFEMForceField.h new file mode 100644 index 0000000..5ba9b87 --- /dev/null +++ b/src/Shell/forcefield/BezierTriangularBendingFEMForceField.h @@ -0,0 +1,333 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#ifndef SOFA_COMPONENT_FORCEFIELD_BEZIER_TRIANGULAR_BENDING_FEM_FORCEFIELD_H +#define SOFA_COMPONENT_FORCEFIELD_BEZIER_TRIANGULAR_BENDING_FEM_FORCEFIELD_H + +#if !defined(__GNUC__) || (__GNUC__ > 3 || (_GNUC__ == 3 && __GNUC_MINOR__ > 3)) +#pragma once +#endif + + +#include +#include +#include + +#include +#include + +#include +#include + + +// Uncomment the following to use quaternions instead of matrices for +// rotations. Quaternions are slightly faster but numericaly much, much *less* +// stable. I don't recommend that! +//#define CRQUAT + + +namespace sofa +{ + +namespace component +{ + +namespace forcefield +{ + +using namespace sofa::type; +using sofa::type::vector; +using namespace sofa::core::topology; +using namespace sofa::core::behavior; + +/// This class can be overridden if needed for additionnal storage within template specializations. +template +class BezierTriangularBendingFEMForceFieldInternalData +{ +public: +}; + + +template +class BezierTriangularBendingFEMForceField : public core::behavior::ForceField +{ + public: + SOFA_CLASS(SOFA_TEMPLATE(BezierTriangularBendingFEMForceField,DataTypes), SOFA_TEMPLATE(core::behavior::ForceField,DataTypes)); + + typedef core::behavior::ForceField Inherited; + typedef typename DataTypes::VecCoord VecCoord; + typedef typename DataTypes::VecDeriv VecDeriv; + + typedef typename DataTypes::Coord Coord; + typedef typename DataTypes::Deriv Deriv; + typedef typename Coord::value_type Real; + + typedef Vec<3,Real> Vec3; + typedef Vec<2,Real> Vec2; + + typedef Mat<3,3,Real> Mat33; + + typedef sofa::type::Quat Quat; + + typedef Data DataVecCoord; + typedef Data DataVecDeriv; + + typedef sofa::defaulttype::Vec3Types::VecCoord VecCoordHigh; + + typedef sofa::Index Index; + typedef sofa::core::topology::BaseMeshTopology::Triangle Triangle; + typedef sofa::core::topology::BaseMeshTopology::SeqTriangles SeqTriangles; + + class TriangleInformation; + + protected: + + // Displacement vector for in-plane forces: + // [ U1x, U1y, dT1z, U2x, U2y, dT2z, U3x, U3y, dT3z ] + typedef Vec<9, Real> Displacement; + // Displacement vector for bending forces: + // [ U1z, dT1x, dT1y, U2z, dT2x, dT2y, U3z, dT3x, dT3y ] + typedef Vec<9, Real> DisplacementBending; + typedef Mat<3, 3, Real> MaterialStiffness; ///< matrix of material stiffness + typedef Mat<3, 9, Real> StrainDisplacement; ///< strain-displacement matrix for in-plane forces + typedef Mat<3, 9, Real> StrainDisplacementBending; + typedef Mat<3, 3, Real > Transformation; ///< matrix for rigid transformations like rotations + typedef Mat<9, 9, Real> StiffnessMatrix; + typedef Mat<9, 9, Real> StiffnessMatrixBending; + typedef Mat<18, 18, Real> StiffnessMatrixGlobalSpace; + + sofa::core::topology::BaseMeshTopology* _topology; + sofa::core::topology::BaseMeshTopology* _topologyTarget; + + ////////////////////////// Inherited attributes //////////////////////////// + /// https://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html + /// Bring inherited attributes and function in the current lookup context. + /// otherwise any access to the base::attribute would require + /// the "this->" approach. + using ForceField::d_componentState ; + //////////////////////////////////////////////////////////////////////////// + +public: + + class TriangleInformation + { + public: + + type::fixed_array restLocalPositions; +#ifdef CRQUAT + type::fixed_array restLocalOrientationsInv; +#else + type::fixed_array restLocalOrientationsInv; +#endif + + // Indices of each vertex + Index a, b, c; + + // Indices in rest shape topology. Normaly is the same as a, b, + // c, but is different if topologyMapper is set. + Index a0, b0, c0; + + // Corotational frame + Vec3 frameCenter; + Transformation frameOrientation; // frame orientation + Transformation frameOrientationInv; // it's inverse (transposition) +#ifdef CRQUAT + Quat frameOrientationQ; // representation as quaternion +#endif + + // Matrix of interpolation functions + Mat<3,3> interpol; + + // Segments in rest position used to keep Bézier points rigidly fixed + Vec3 P0_P1_inFrame0; + Vec3 P0_P2_inFrame0; + Vec3 P1_P2_inFrame1; + Vec3 P1_P0_inFrame1; + Vec3 P2_P0_inFrame2; + Vec3 P2_P1_inFrame2; + + // Nodes of the Bezier triangle + type::fixed_array bezierNodes; // ... in global frame + type::fixed_array pts; // ... in local frame + // ... of the rest shape + type::fixed_array bezierNodes0; // ... in global frame + + // the strain-displacement matrices at each Gauss point + StrainDisplacement strainDisplacementMatrix1; + StrainDisplacement strainDisplacementMatrix2; + StrainDisplacement strainDisplacementMatrix3; + StrainDisplacement strainDisplacementMatrix4; + + // the strain-displacement matrices at each Gauss point + StrainDisplacementBending strainDisplacementMatrixB1; + StrainDisplacementBending strainDisplacementMatrixB2; + StrainDisplacementBending strainDisplacementMatrixB3; + StrainDisplacementBending strainDisplacementMatrixB4; + + // Stiffness matrix K = J * M * Jt + StiffnessMatrix stiffnessMatrix; + + // Stiffness matrix for bending K = Jt * M * J + StiffnessMatrixBending stiffnessMatrixBending; + + // Surface Area * 2 + Real area2; + + TriangleInformation() { } + + /// Output stream + inline friend std::ostream& operator<< ( std::ostream& os, const TriangleInformation& /*ti*/ ) + { + return os; + } + + /// Input stream + inline friend std::istream& operator>> ( std::istream& in, TriangleInformation& /*ti*/ ) + { + return in; + } + }; + + class TRQSTriangleHandler : public TopologyDataHandler > + { + public: + TRQSTriangleHandler(BezierTriangularBendingFEMForceField* _ff, TriangleData >* _data) + : TopologyDataHandler >(_data) + , ff(_ff) + { + } + + void applyCreateFunction(unsigned int triangleIndex, TriangleInformation& , + const Triangle & t, + const sofa::type::vector< unsigned int > &, + const sofa::type::vector< double > &); + + protected: + BezierTriangularBendingFEMForceField* ff; + }; + + BezierTriangularBendingFEMForceField(); + + virtual ~BezierTriangularBendingFEMForceField(); + void init() override; + void reinit() override; + void addForce(const sofa::core::MechanicalParams* /*mparams*/, DataVecDeriv& dataF, const DataVecCoord& dataX, const DataVecDeriv& /*dataV*/ ) override ; + void addDForce(const sofa::core::MechanicalParams* /*mparams*/, DataVecDeriv& datadF, const DataVecDeriv& datadX ) override ; + void addKToMatrix(const core::MechanicalParams* mparams, const sofa::core::behavior::MultiMatrixAccessor* matrix) override; + void addBToMatrix(sofa::linearalgebra::BaseMatrix * /*mat*/, double /*bFact*/, unsigned int &/*offset*/) override; + void handleTopologyChange() override; + + SReal getPotentialEnergy(const sofa::core::MechanicalParams* /*mparams*/, const DataVecCoord& /*x*/) const override { return 0; } + + void draw(const core::visual::VisualParams* vparams) override; + + sofa::core::topology::BaseMeshTopology* getTopology() {return _topology;} + + Data f_poisson; + Data f_young; + Data f_thickness; + Data< type::vector > normals; + + // Allow transition between rest shapes + SingleLink, + shell::controller::MeshInterpolator, + BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> restShape; + + // Indirect rest shape indexing (e.g. for "joining" two meshes) + bool mapTopology; + SingleLink, + shell::engine::JoinMeshPoints, + BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> topologyMapper; + + + static void computeEdgeBezierPoints(const Index& a, const Index& b, const Index& c, + const VecCoord& x, const type::vector& norms, + type::fixed_array &bezierPoints); + + void handleEvent(sofa::core::objectmodel::Event *event) override; + + const TriangleInformation& getTriangleInfo(Index t) { + return triangleInfo.getValue()[t]; + } + +protected : + + TriangleData< sofa::type::vector > triangleInfo; + TRQSTriangleHandler* triangleHandler; + + /// Material stiffness matrices for plane stress and bending + MaterialStiffness materialMatrix; + MaterialStiffness materialMatrixBending; + + + void initTriangleOnce(const int i, const Index&a, const Index&b, const Index&c); + void initTriangle(const int i); + + void computeLocalTriangle(const VecCoord &x, const Index elementIndex); + + void computeDisplacements( Displacement &Disp, DisplacementBending &BDisp, const VecCoord &x, TriangleInformation *tinfo); + void computeStrainDisplacementMatrixMembrane(TriangleInformation &tinfo); + void computeStrainDisplacementMatrixBending(TriangleInformation &tinfo); + void computeStiffnessMatrixMembrane(StiffnessMatrix &K, const TriangleInformation &tinfo); + void computeStiffnessMatrixBending(StiffnessMatrixBending &K, const TriangleInformation &tinfo); + void computeForceMembrane(Displacement &F, const Displacement& D, const Index elementIndex); + void computeForceBending(DisplacementBending &F, const DisplacementBending& D, const Index elementIndex); + + // Strain-displacement matrices + void matrixSDM(StrainDisplacement &J, const Vec3 &GP, const TriangleInformation& tinfo); + void matrixSDB(StrainDisplacementBending &J, const Vec3 &GP, const TriangleInformation& tinfo); + + /// f += Kx where K is the stiffness matrix and x a displacement + virtual void applyStiffness(VecDeriv& f, const VecDeriv& dx, const Index elementIndex, const double kFactor); + virtual void computeMaterialMatrix(); + + void computePosBezierPoint(const TriangleInformation *tinfo, const Index& a, const Index& b, const Index& c, const VecCoord& x, sofa::type::fixed_array &X_bezierPoints); + void bezierFunctions(const Vec2& baryCoord, sofa::type::fixed_array &f_bezier); + void bezierDerivateFunctions(const Vec2& baryCoord, sofa::type::fixed_array &df_dx_bezier, sofa::type::fixed_array &df_dy_bezier); + void interpolateRefFrame(TriangleInformation *tinfo, const Vec2& baryCoord, const Index& a, const Index& b, const Index& c, const VecCoord& x, sofa::type::fixed_array& X_bezierPoints ); + + + void accumulateForce(VecDeriv& f, const VecCoord & p, const Index elementIndex); + + void convertStiffnessMatrixToGlobalSpace(StiffnessMatrixGlobalSpace &K_gs, TriangleInformation *tinfo); +}; + + +#if defined(WIN32) && !defined(SOFA_COMPONENT_FORCEFIELD_BEZIER_TRIANGULAR_BENDING_FEM_FORCEFIELD_CPP) +#pragma warning(disable : 4231) +#ifndef SOFA_FLOAT +extern template class BezierTriangularBendingFEMForceField; +#endif +#ifndef SOFA_DOUBLE +extern template class BezierTriangularBendingFEMForceField; +#endif +#endif + +} // namespace forcefield + +} // namespace component + +} // namespace sofa + +#endif // #ifndef SOFA_COMPONENT_FORCEFIELD_BEZIER_TRIANGULAR_BENDING_FEM_FORCEFIELD_H diff --git a/src/Shell/forcefield/BezierTriangularBendingFEMForceField.inl b/src/Shell/forcefield/BezierTriangularBendingFEMForceField.inl new file mode 100644 index 0000000..5aa55b4 --- /dev/null +++ b/src/Shell/forcefield/BezierTriangularBendingFEMForceField.inl @@ -0,0 +1,1638 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#ifndef SOFA_COMPONENT_FORCEFIELD_BEZIER_TRIANGULAR_BENDING_FEM_FORCEFIELD_INL +#define SOFA_COMPONENT_FORCEFIELD_BEZIER_TRIANGULAR_BENDING_FEM_FORCEFIELD_INL + +#include +#include +#include +#include +#include +#include +#include +#include //for debugging +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +// Use 4-point Gaussian quadrature to integrate over the triangle +#define GAUSS4 + +namespace sofa +{ + namespace component + { + namespace forcefield + { + using namespace sofa::type; + using namespace sofa::component::topology; + + + +// -------------------------------------------------------------------------------------- +// --- Topology Creation/Destruction functions +// -------------------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::TRQSTriangleHandler::applyCreateFunction(unsigned int triangleIndex, TriangleInformation &, const Triangle &t, const sofa::type::vector &, const sofa::type::vector &) +{ + if (ff) { + ff->initTriangleOnce(triangleIndex, t[0], t[1], t[2]); + ff->initTriangle(triangleIndex); + } +} + + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +BezierTriangularBendingFEMForceField::BezierTriangularBendingFEMForceField() +: f_poisson(initData(&f_poisson,(Real)0.45,"poissonRatio","Poisson's ratio in Hooke's law")) +, f_young(initData(&f_young,(Real)3000.,"youngModulus","Young's modulus in Hooke's law")) +, f_thickness(initData(&f_thickness,(Real)0.1,"thickness","Thickness of the plates")) +, normals(initData(&normals, "normals","Node normals at the rest shape")) +, restShape(initLink("restShape","MeshInterpolator component for variable rest shape")) +, mapTopology(false) +, topologyMapper(initLink("topologyMapper","Component supplying different topology for the rest shape")) +, triangleInfo(initData(&triangleInfo, "triangleInfo", "Internal triangle data")) +{ + triangleHandler = new TRQSTriangleHandler(this, &triangleInfo); +} + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template void BezierTriangularBendingFEMForceField::handleTopologyChange() +{ + msg_warning() << "handleTopologyChange() not implemented" ; + +} + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +BezierTriangularBendingFEMForceField::~BezierTriangularBendingFEMForceField() +{ + if(triangleHandler) delete triangleHandler; +} + +// -------------------------------------------------------------------------------------- +// --- Initialization stage +// -------------------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::init() +{ + d_componentState.setValue(core::objectmodel::ComponentState::Valid); + this->Inherited::init(); + + if (this->mstate == nullptr) { + msg_warning() << "Mechanical state is required but not found." ; + d_componentState.setValue(core::objectmodel::ComponentState::Invalid); + return; + } + + _topology = this->getContext()->getMeshTopology(); + + // Create specific handler for TriangleData + triangleInfo.createTopologyHandler(_topology); + + reinit(); +} + +// -------------------------------------------------------------------------------------- +// --- Re-initialization (called when we change a parameter through the GUI) +// -------------------------------------------------------------------------------------- +template void BezierTriangularBendingFEMForceField::reinit() +{ + _topology = this->getContext()->getMeshTopology(); + + if (_topology->getNbTriangles()==0) + { + msg_error() << "BezierTriangularBendingFEMForceField: object must have a Triangular Set Topology."; + d_componentState.setValue(core::objectmodel::ComponentState::Invalid); + return; + } + + if (topologyMapper.get() != nullptr) + { + shell::engine::JoinMeshPoints* jmp = topologyMapper.get(); + if (jmp->f_output_triangles.getValue().size() == 0) + { + msg_warning() << "Mapped topology must be triangular. No triangles found." ; + } else { + mapTopology = true; + } + } + + if (restShape.get() != nullptr) { + // Listen for MeshChangedEvent + *this->f_listening.beginEdit() = true; + this->f_listening.endEdit(); + + // Check if there is same number of nodes + const VecCoord &rx = restShape.get()->f_position.getValue(); + if (!mapTopology) { + if (rx.size() != this->mstate->read(sofa::core::vec_id::read_access::position)->getValue().size()) { + msg_warning() << "Different number of nodes in rest shape and mechanical state." ; + } + } else if (rx.size() != topologyMapper.get()->f_input_position.getValue().size()) { + msg_warning() << "Different number of nodes in rest shape and (original) mapped topology." ; + } + + // Check normal count + type::vector norms = restShape.get()->f_normals.getValue(); + if (norms.size() == 0) { + msg_warning() << "No normals defined in rest shape, assuming flat triangles." ; + } else if (norms.size() != + topologyMapper.get()->f_input_position.getValue().size()) { + msg_warning() << "Normals count doesn't correspond with nodes count in topologyMapper." ; + return; + } + + } else if (mapTopology) { + // Mapped topology, no changing rest shape + + if (normals.getValue().size() != 0) { + msg_warning() << "Ignoring defined normals because topologyMapper is defined." ; + } + + // Check normal count + type::vector norms = topologyMapper.get()->f_input_normals.getValue(); + if (norms.size() == 0) { + msg_warning() << "No normals defined in topologyMapper, assuming flat triangles." ; + } else if (norms.size() != + topologyMapper.get()->f_input_position.getValue().size()) { + msg_warning() << "Normals count doesn't correspond with nodes count in topologyMapper." ; + return; + } + + } + + if (!mapTopology && restShape.get() == nullptr) { + // No topology mapper, no changing rest shape -> normal behaviour + + // Check normal count + if (normals.getValue().size() == 0) { + msg_warning() << "No normals defined, assuming flat triangles." ; + } else if (normals.getValue().size() != this->mstate->read(sofa::core::vec_id::read_access::position)->getValue().size()) { + msg_warning() << "Normals count doesn't correspond with nodes count." ; + return; + } +} + + // Compute the material matrices + computeMaterialMatrix(); + + type::vector& triangleInf = *(triangleInfo.beginEdit()); + + /// Prepare to store info in the triangle array + triangleInf.resize(_topology->getNbTriangles()); + + for (sofa::Index i=0; i<_topology->getNbTriangles(); ++i) + { + triangleHandler->applyCreateFunction(i, triangleInf[i], _topology->getTriangle(i), (const sofa::type::vector< unsigned int > )0, (const sofa::type::vector< double >)0); + } + + triangleInfo.endEdit(); +} + +// -------------------------------------------------------------------------------------- +// --- Initialisation of the triangle that has to be done only once +// -------------------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::initTriangleOnce(const int i, const Index&a, const Index&b, const Index&c) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &triangleInf[i]; + + // Store indices of each vertex + tinfo->a = a; + tinfo->b = b; + tinfo->c = c; + + // Store indices of each vertex in rest shape + Index a0=a, b0=b, c0=c; + if (mapTopology) { + shell::engine::JoinMeshPoints* jmp = topologyMapper.get(); + + // Get indices in original topology + a0 = jmp->getSrcNodeFromTri(i, a0); + b0 = jmp->getSrcNodeFromTri(i, b0); + c0 = jmp->getSrcNodeFromTri(i, c0); + } + + tinfo->a0 = a0; + tinfo->b0 = b0; + tinfo->c0 = c0; + + triangleInfo.endEdit(); +} + +// -------------------------------------------------------------------------------------- +// --- Initialisation of the triangle that should be done when rest shape changes +// -------------------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::initTriangle(const int i) +{ + if (d_componentState.getValue() == core::objectmodel::ComponentState::Invalid) + return; + + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &triangleInf[i]; + + Index a0 = tinfo->a0; + Index b0 = tinfo->b0; + Index c0 = tinfo->c0; + + // Gets vertices of rest positions + const VecCoord& x0 = (restShape.get() != nullptr) + // if having changing rest shape take it + ? restShape.get()->f_position.getValue() + : (mapTopology + // if rest shape is fixed but we have mapped topology use it + ? topologyMapper.get()->f_input_position.getValue() + // otherwise just take rest shape in mechanical state + : this->mstate->read(sofa::core::vec_id::read_access::position)->getValue() + ); + + const type::vector& norms = (restShape.get() != nullptr) + // if having changing rest shape take its normals + ? restShape.get()->f_normals.getValue() + : (mapTopology + // if rest shape is fixed but we have mapped topology use it + ? topologyMapper.get()->f_input_normals.getValue() + // otherwise just take normals from parameter + : normals.getValue() + ); + + // Compute initial bezier points at the edges + computeEdgeBezierPoints(a0, b0, c0, x0, norms, tinfo->bezierNodes); + + // Get the segments' position in the reference frames of the rest-shape + tinfo->P0_P1_inFrame0 = x0[a0].getOrientation().inverseRotate( tinfo->bezierNodes[3] ); + tinfo->P0_P2_inFrame0 = x0[a0].getOrientation().inverseRotate( tinfo->bezierNodes[4] ); + + tinfo->P1_P2_inFrame1 = x0[b0].getOrientation().inverseRotate( tinfo->bezierNodes[5] ); + tinfo->P1_P0_inFrame1 = x0[b0].getOrientation().inverseRotate( tinfo->bezierNodes[6] ); + + tinfo->P2_P1_inFrame2 = x0[c0].getOrientation().inverseRotate( tinfo->bezierNodes[7] ); + tinfo->P2_P0_inFrame2 = x0[c0].getOrientation().inverseRotate( tinfo->bezierNodes[8] ); + + // Compute the initial position and rotation of the reference frame + this->interpolateRefFrame(tinfo, Vec2(1.0/3.0,1.0/3.0), + tinfo->a0, tinfo->b0, tinfo->c0, x0, + tinfo->bezierNodes); + + tinfo->bezierNodes0 = tinfo->bezierNodes; + + // Compute positions in local frame + computeLocalTriangle(x0, i); + + // Initial positions + tinfo->restLocalPositions[0] = tinfo->frameOrientation * (x0[a0].getCenter() - tinfo->frameCenter); + tinfo->restLocalPositions[1] = tinfo->frameOrientation * (x0[b0].getCenter() - tinfo->frameCenter); + tinfo->restLocalPositions[2] = tinfo->frameOrientation * (x0[c0].getCenter() - tinfo->frameCenter); + + // Initial rotations +#ifdef CRQUAT + tinfo->restLocalOrientationsInv[0] = (tinfo->frameOrientationQ * x0[a0].getOrientation()).inverse(); + tinfo->restLocalOrientationsInv[1] = (tinfo->frameOrientationQ * x0[b0].getOrientation()).inverse(); + tinfo->restLocalOrientationsInv[2] = (tinfo->frameOrientationQ * x0[c0].getOrientation()).inverse(); +#else + x0[a0].getOrientation().toMatrix(tinfo->restLocalOrientationsInv[0]); + x0[b0].getOrientation().toMatrix(tinfo->restLocalOrientationsInv[1]); + x0[c0].getOrientation().toMatrix(tinfo->restLocalOrientationsInv[2]); + + tinfo->restLocalOrientationsInv[0].transpose( tinfo->frameOrientation * tinfo->restLocalOrientationsInv[0] ); + tinfo->restLocalOrientationsInv[1].transpose( tinfo->frameOrientation * tinfo->restLocalOrientationsInv[1] ); + tinfo->restLocalOrientationsInv[2].transpose( tinfo->frameOrientation * tinfo->restLocalOrientationsInv[2] ); +#endif + + // Compute strain-displacement matrices at Gauss points + computeStrainDisplacementMatrixMembrane(*tinfo); + computeStrainDisplacementMatrixBending(*tinfo); + + // Compute stiffness matrices K = ∫ J^T*M*J dV + computeStiffnessMatrixMembrane(tinfo->stiffnessMatrix, *tinfo); + computeStiffnessMatrixBending(tinfo->stiffnessMatrixBending, *tinfo); + + triangleInfo.endEdit(); +} + +// ------------------------ +// --- Compute the position of the Bézier points situated at the edges based on +// --- the normals at triangle nodes. +// --- +// --- NOTE: While the output vector contains 10 nodes only nodes at index 3-8 +// --- are filled in. +// --- +// --- We do that by computing the actual bezier nodes and then computing +// --- rotation relative to the node orientation: +// --- +// --- To maintain C^0 continuity the nodes have to satisfy a few conditions. +// --- We use the conditions outlined in [Ubach and Oñate 2010]. The node has +// --- to lie on the: +// --- +// --- (1) plane perpendicular to the normal at the normal at triangle node +// --- (2) plane that contains the curve of triangle's contour +// --- - we us the plane defined by the edge (director between nodes of the +// --- flat triangle) and the average between normals at the triangle nodes +// --- connected by the edge +// --- (3) plane perpendicular to the edge of the flat triangle placed at 1/3 +// --- of it's length +// --- +// --- TODO: check the books if this also means C^1 continuity, IMO it should. +// ------------------------ +template +void BezierTriangularBendingFEMForceField::computeEdgeBezierPoints( + const Index& a, const Index& b, const Index& c, + const VecCoord& x, + const type::vector& norms, + type::fixed_array &bezierPoints) +{ + if (norms.size() == 0) { + // No normals, assume flat triangles + + // Edge A-B + bezierPoints[3] = (x[b].getCenter() - x[a].getCenter())/3.0; + bezierPoints[6] = (x[a].getCenter() - x[b].getCenter())/3.0; + + // Edge B-C + bezierPoints[5] = (x[c].getCenter() - x[b].getCenter())/3.0; + bezierPoints[7] = (x[b].getCenter() - x[c].getCenter())/3.0; + + // Edge C-A + bezierPoints[8] = (x[a].getCenter() - x[c].getCenter())/3.0; + bezierPoints[4] = (x[c].getCenter() - x[a].getCenter())/3.0; + + return; + } + + // Edge A-B + Vec3 n = (norms[a] + norms[b]) / 2.0; + n.normalize(); + + Vec3 e = x[b].getCenter() - x[a].getCenter(); + Real elen = e.norm()/3.0; + e.normalize(); + + Mat33 M, MI; + + // (1) (2) (3) + M = Mat33(norms[a], cross(e,n), e); + + // Solve M*x = (0, 0, |e|/3) + MI.invert(M); + bezierPoints[3] = MI * Vec3(0, 0, elen); + + M = Mat33(norms[b], cross(-e,n), -e); + MI.invert(M); + bezierPoints[6] = MI * Vec3(0, 0, elen); + + + // Edge B-C + n = (norms[b] + norms[c]) / 2.0; + n.normalize(); + + e = x[c].getCenter() - x[b].getCenter(); + elen = e.norm()/3.0; + e.normalize(); + + M = Mat33(norms[b], cross(e,n), e); + MI.invert(M); + bezierPoints[5] = MI * Vec3(0, 0, elen); + + M = Mat33(norms[c], cross(-e,n), -e); + MI.invert(M); + bezierPoints[7] = MI * Vec3(0, 0, elen); + + + // Edge C-A + n = (norms[c] + norms[a]) / 2.0; + n.normalize(); + + e = x[a].getCenter() - x[c].getCenter(); + elen = e.norm()/3.0; + e.normalize(); + + M = Mat33(norms[c], cross(e,n), e); + MI.invert(M); + bezierPoints[8] = MI * Vec3(0, 0, elen); + + M = Mat33(norms[a], cross(-e,n), -e); + MI.invert(M); + bezierPoints[4] = MI * Vec3(0, 0, elen); +} + +// ------------------------ +// --- Compute the position of the Bézier points +// ------------------------ +template +void BezierTriangularBendingFEMForceField::computePosBezierPoint( + const TriangleInformation *tinfo, + const Index& a, const Index& b, const Index& c, + const VecCoord& x, + sofa::type::fixed_array &X_bezierPoints) +{ + // 3 points corresponding to the node position + X_bezierPoints[0] = x[a].getCenter(); + X_bezierPoints[1] = x[b].getCenter(); + X_bezierPoints[2] = x[c].getCenter(); + + // compute the corresponding Bezier Points + + // (3,4) is attached to frame 0 + X_bezierPoints[3] = x[a].getCenter()+ x[a].getOrientation().rotate( tinfo->P0_P1_inFrame0 ); + X_bezierPoints[4] = x[a].getCenter()+ x[a].getOrientation().rotate( tinfo->P0_P2_inFrame0 ); + + // (5,6) is attached to frame 1 + X_bezierPoints[5] = x[b].getCenter()+ x[b].getOrientation().rotate( tinfo->P1_P2_inFrame1 ); + X_bezierPoints[6] = x[b].getCenter()+ x[b].getOrientation().rotate( tinfo->P1_P0_inFrame1 ); + + // (7,8) is attached to frame 2 + // TODO: 7/8 here are switched compared to initTriangle & + // computeEdgeBezierPoints, verify and consolidate + X_bezierPoints[7] = x[c].getCenter()+ x[c].getOrientation().rotate( tinfo->P2_P0_inFrame2 ); + X_bezierPoints[8] = x[c].getCenter()+ x[c].getOrientation().rotate( tinfo->P2_P1_inFrame2 ); + + // (9) use a kind of skinning function (average of the position obtained when attached respectively to 0, 1 and 2) + X_bezierPoints[9] = (x[a].getCenter()+ x[a].getOrientation().rotate( (tinfo->P0_P1_inFrame0 + tinfo->P0_P2_inFrame0) ))/3.0 + + (x[b].getCenter()+ x[b].getOrientation().rotate( (tinfo->P1_P2_inFrame1 + tinfo->P1_P0_inFrame1) ))/3.0 + + (x[c].getCenter()+ x[c].getOrientation().rotate( (tinfo->P2_P0_inFrame2 + tinfo->P2_P1_inFrame2) ))/3.0; +} + + +template +void BezierTriangularBendingFEMForceField::bezierFunctions(const Vec2& baryCoord, sofa::type::fixed_array &f_bezier) +{ + Real a=1-baryCoord[0]-baryCoord[1]; + Real b=baryCoord[0]; + Real c=baryCoord[1]; + + f_bezier[0]= a*a*a; + f_bezier[1]= b*b*b; + f_bezier[2]= c*c*c; + f_bezier[3]=3*a*a*b; f_bezier[4]=3*a*a*c; + f_bezier[5]=3*b*b*c; f_bezier[6]=3*b*b*a; + f_bezier[7]=3*c*c*a; f_bezier[8]=3*c*c*b; + f_bezier[9]=6*a*b*c; +} + +template +void BezierTriangularBendingFEMForceField::bezierDerivateFunctions(const Vec2& baryCoord, sofa::type::fixed_array &df_dx_bezier, sofa::type::fixed_array &df_dy_bezier) +{ + Real a=1-baryCoord[0]-baryCoord[1]; + Real b=baryCoord[0]; + Real c=baryCoord[1]; + + df_dx_bezier[0]= -3.0*a*a; + df_dx_bezier[1]= 3.0*b*b; + df_dx_bezier[2]= 0; + df_dx_bezier[3]= -6.0*a*b+3.0*a*a ; df_dx_bezier[4]= -6.0*a*c; + df_dx_bezier[5]= 6.0*b*c; df_dx_bezier[6]=6.0*b*a - 3.0*b*b; + df_dx_bezier[7]= -3.0*c*c; df_dx_bezier[8]=3.0*c*c; + df_dx_bezier[9]= -6.0*b*c + 6.0*a*c; + + df_dy_bezier[0]= -3.0*a*a; + df_dy_bezier[1]= 0.0; + df_dy_bezier[2]= 3.0*c*c; + df_dy_bezier[3]=-6.0*a*b; df_dy_bezier[4]=-6.0*a*c+3.0*a*a; + df_dy_bezier[5]=3.0*b*b; df_dy_bezier[6]=-3.0*b*b; + df_dy_bezier[7]=-3.0*c*c+6.0*c*a; df_dy_bezier[8]=6.0*c*b; + df_dy_bezier[9]=-6.0*b*c + 6.0*a*b; +} + +template +void BezierTriangularBendingFEMForceField::interpolateRefFrame( + TriangleInformation *tinfo, + const Vec2& /*baryCoord*/, + const Index& a, const Index& b, const Index& c, + const VecCoord& x, + sofa::type::fixed_array& X_bezierPoints) +{ + // get the position of the bezier Points + this->computePosBezierPoint(tinfo, a, b, c, x, X_bezierPoints); + +#if 0 //Reference frame by rotation at the center of the bezier triangle + // use the bezier functions to interpolate the positions + sofa::type::fixed_array f_bezier; + this->bezierFunctions(baryCoord, f_bezier); + interpolatedFrame.getCenter().clear(); + for (unsigned int i=0;i<10;i++){ + interpolatedFrame.getCenter() += X_bezierPoints[i]*f_bezier[i]; + //msg_info() << " add pos = "<frameOrientation = R; + tinfo->frameOrientationInv.transpose(R); + +#ifdef CRQUAT + tinfo->frameOrientationQ.fromMatrix(tinfo->frameOrientation); + tinfo->frameOrientationQ.normalize(); +#endif +} + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::applyStiffness(VecDeriv& v, const VecDeriv& dx, const Index elementIndex, const double kFactor) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &triangleInf[elementIndex]; + + // Get the indices of the 3 vertices for the current triangle + const Index& a = tinfo->a; + const Index& b = tinfo->b; + const Index& c = tinfo->c; + + // Computes in-plane displacements and bending displacements + Displacement Disp; + DisplacementBending Disp_bending; + Vec3 x, o; + + x = tinfo->frameOrientation * getVCenter(dx[a]); + o = tinfo->frameOrientation * getVOrientation(dx[a]); + Disp[0] = x[0]; + Disp[1] = x[1]; + Disp[2] = o[2]; + Disp_bending[0] = x[2]; + Disp_bending[1] = o[0]; + Disp_bending[2] = o[1]; + + x = tinfo->frameOrientation * getVCenter(dx[b]); + o = tinfo->frameOrientation * getVOrientation(dx[b]); + Disp[3] = x[0]; + Disp[4] = x[1]; + Disp[5] = o[2]; + Disp_bending[3] = x[2]; + Disp_bending[4] = o[0]; + Disp_bending[5] = o[1]; + + x = tinfo->frameOrientation * getVCenter(dx[c]); + o = tinfo->frameOrientation * getVOrientation(dx[c]); + Disp[6] = x[0]; + Disp[7] = x[1]; + Disp[8] = o[2]; + Disp_bending[6] = x[2]; + Disp_bending[7] = o[0]; + Disp_bending[8] = o[1]; + + // Compute dF + Displacement dF; + dF = tinfo->stiffnessMatrix * Disp; + + // Compute dF_bending + DisplacementBending dF_bending; + dF_bending = tinfo->stiffnessMatrixBending * Disp_bending; + + // Go back into global frame + Vec3 fa1, fa2, fb1, fb2, fc1, fc2; + fa1 = tinfo->frameOrientationInv * Vec3(dF[0], dF[1], dF_bending[0]); + fa2 = tinfo->frameOrientationInv * Vec3(dF_bending[1], dF_bending[2], dF[2]); + + fb1 = tinfo->frameOrientationInv * Vec3(dF[3], dF[4], dF_bending[3]); + fb2 = tinfo->frameOrientationInv * Vec3(dF_bending[4], dF_bending[5], dF[5]); + + fc1 = tinfo->frameOrientationInv * Vec3(dF[6], dF[7], dF_bending[6]); + fc2 = tinfo->frameOrientationInv * Vec3(dF_bending[7], dF_bending[8], dF[8]); + + v[a] += Deriv(-fa1, -fa2) * kFactor; + v[b] += Deriv(-fb1, -fb2) * kFactor; + v[c] += Deriv(-fc1, -fc2) * kFactor; + + triangleInfo.endEdit(); +} + +// ----------------------------------------------------------------------------- +// --- Compute all nodes of the Bézier triangle in local frame +// ----------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::computeLocalTriangle( + const VecCoord &/*x*/, const Index elementIndex) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &triangleInf[elementIndex]; + + type::fixed_array &pts = tinfo->pts; + + // The element is being rotated along the frame situated at the center of + // the element + + //// Rotate the already computed nodes + for (int i = 0; i < 10; i++) { + pts[i] = tinfo->frameOrientation * (tinfo->bezierNodes[i] - tinfo->frameCenter); + } + + + // TODO: this is no longer correct, or is it? (the nodes of the shell don't + // lie on the plane of reference frame and have non-zero z-coordinate) + Mat<3, 3, Real> m; + m(0,0) = 1; m(0,1) = 1; m(0,2) = 1; + m(1,0) = pts[0][0]; m(1,1) = pts[1][0]; m(1,2) = pts[2][0]; + m(2,0) = pts[0][1]; m(2,1) = pts[1][1]; m(2,2) = pts[2][1]; + + tinfo->interpol.invert(m); + tinfo->area2 = cross(pts[1] - pts[0], pts[2] - pts[0]).norm(); + + triangleInfo.endEdit(); +} + +// ----------------------------------------------------------------------------- +// --- Compute displacement vectors for in-plane and bending deformations in +// --- co-rotational frame of reference. +// ----------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::computeDisplacements( Displacement &Disp, DisplacementBending &BDisp, const VecCoord &x, TriangleInformation *tinfo) +{ + Index a = tinfo->a; + Index b = tinfo->b; + Index c = tinfo->c; + + // Rotations + +#ifdef CRQUAT + Quat Q0 = (tinfo->frameOrientationQ * x[a].getOrientation()) * tinfo->restLocalOrientationsInv[0]; + Quat Q1 = (tinfo->frameOrientationQ * x[b].getOrientation()) * tinfo->restLocalOrientationsInv[1]; + Quat Q2 = (tinfo->frameOrientationQ * x[c].getOrientation()) * tinfo->restLocalOrientationsInv[2]; +#else + Transformation tmp0, tmp1, tmp2; + x[a].getOrientation().toMatrix(tmp0); + x[b].getOrientation().toMatrix(tmp1); + x[c].getOrientation().toMatrix(tmp2); + + Quat Q0; Q0.fromMatrix( tinfo->frameOrientation * tmp0 * tinfo->restLocalOrientationsInv[0] ); + Quat Q1; Q1.fromMatrix( tinfo->frameOrientation * tmp1 * tinfo->restLocalOrientationsInv[1] ); + Quat Q2; Q2.fromMatrix( tinfo->frameOrientation * tmp2 * tinfo->restLocalOrientationsInv[2] ); + // TODO: can we do this without the quaternions? +#endif + + Vec3 dQ0 = Q0.toEulerVector(); + Vec3 dQ1 = Q1.toEulerVector(); + Vec3 dQ2 = Q2.toEulerVector(); + + //bending => rotation along X and Y + BDisp[1] = dQ0[0]; + BDisp[2] = dQ0[1]; + BDisp[4] = dQ1[0]; + BDisp[5] = dQ1[1]; + BDisp[7] = dQ2[0]; + BDisp[8] = dQ2[1]; + + // inPlane => rotation along Z + Disp[2] = dQ0[2]; + Disp[5] = dQ1[2]; + Disp[8] = dQ2[2]; + + // Translations + + // translation compute the current position of the node on the element frame + //Vec3 Center_T_node0 = _global_R_element.inverseRotate( x[a].getCenter() - tinfo->frame.getCenter() ); + //Vec3 Center_T_node1 = _global_R_element.inverseRotate( x[b].getCenter() - tinfo->frame.getCenter() ); + //Vec3 Center_T_node2 = _global_R_element.inverseRotate( x[c].getCenter() - tinfo->frame.getCenter() ); + + // Compare this current position with the rest position + Vec3 dX0 = tinfo->pts[0] - tinfo->restLocalPositions[0]; + Vec3 dX1 = tinfo->pts[1] - tinfo->restLocalPositions[1]; + Vec3 dX2 = tinfo->pts[2] - tinfo->restLocalPositions[2]; + + // inPlane => translation along X and Y + Disp[0] = dX0[0]; + Disp[1] = dX0[1]; + Disp[3] = dX1[0]; + Disp[4] = dX1[1]; + Disp[6] = dX2[0]; + Disp[7] = dX2[1]; + + // bending => translation along Z + BDisp[0] = dX0[2]; + BDisp[3] = dX1[2]; + BDisp[6] = dX2[2]; +} + +// ---------------------------------------------------------------------------- +// --- Compute the strain-displacement matrix for in-plane deformation +// ----------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::computeStrainDisplacementMatrixMembrane(TriangleInformation &tinfo) +{ + // Calculation of the 3 Gauss points +#ifndef GAUSS4 + Vec3 gaussPoint1 = tinfo.pts[0]*(2.0/3.0) + tinfo.pts[1]/6.0 + tinfo.pts[2]/6.0; + Vec3 gaussPoint2 = tinfo.pts[0]/6.0 + tinfo.pts[1]*(2.0/3.0) + tinfo.pts[2]/6.0; + Vec3 gaussPoint3 = tinfo.pts[0]/6.0 + tinfo.pts[1]/6.0 + tinfo.pts[2]*(2.0/3.0); + + matrixSDM(tinfo.strainDisplacementMatrix1, gaussPoint1, tinfo); + matrixSDM(tinfo.strainDisplacementMatrix2, gaussPoint2, tinfo); + matrixSDM(tinfo.strainDisplacementMatrix3, gaussPoint3, tinfo); +#else + matrixSDM(tinfo.strainDisplacementMatrix1, Vec3(0.211324865, 0.166666667, 0), tinfo); + matrixSDM(tinfo.strainDisplacementMatrix2, Vec3(0.211324865, 0.622008467, 0), tinfo); + matrixSDM(tinfo.strainDisplacementMatrix3, Vec3(0.788675134, 0.044658198, 0), tinfo); + matrixSDM(tinfo.strainDisplacementMatrix4, Vec3(0.788675134, 0.166666667, 0), tinfo); +#endif + + msg_info() << "pts: " << tinfo.pts ; + msg_info() << "x2b: " << tinfo.interpol ; + + msg_info() << "Bm: " << tinfo.strainDisplacementMatrix1 << + "\n " << tinfo.strainDisplacementMatrix2 << + "\n " << tinfo.strainDisplacementMatrix3 << +#ifdef GAUSS4 + "\n " << tinfo.strainDisplacementMatrix4 << +#endif + "\n"; + + Displacement u = Vec<9,Real>(1, -5, 0, 1, -5, 0, 1, -5, 0); + + msg_info() << "-- Disp test Bm (u=" << u << ")\n" << + "1 : " << tinfo.strainDisplacementMatrix1 * u << "\n" + "2 : " << tinfo.strainDisplacementMatrix2 * u << "\n" + "3 : " << tinfo.strainDisplacementMatrix3 * u << "\n" +#ifdef GAUSS4 + "4 : " << tinfo.strainDisplacementMatrix4 * u << "\n" +#endif + ; +} + +// ---------------------------------------------------------------------------- +// --- Compute the strain-displacement matrix for in-plane deformation +// ----------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::matrixSDM( + StrainDisplacement &J, const Vec3 &GP, const TriangleInformation& tinfo) +{ + // Directional vectors from corner nodes + Vec3 P4P1 = tinfo.pts[3] - tinfo.pts[0]; + Vec3 P5P1 = tinfo.pts[4] - tinfo.pts[0]; + Vec3 P6P2 = tinfo.pts[5] - tinfo.pts[1]; + Vec3 P7P2 = tinfo.pts[6] - tinfo.pts[1]; + Vec3 P8P3 = tinfo.pts[7] - tinfo.pts[2]; + Vec3 P9P3 = tinfo.pts[8] - tinfo.pts[2]; + Vec3 P10P1 = tinfo.pts[9] - tinfo.pts[0]; + Vec3 P10P2 = tinfo.pts[9] - tinfo.pts[1]; + Vec3 P10P3 = tinfo.pts[9] - tinfo.pts[2]; + +#ifndef GAUSS4 + Vec3 P(1, GP[0], GP[1]); + + Vec3 p; // Barycentric coordinates of the point GP + p[0] = tinfo.interpol.line(0)*P; + p[1] = tinfo.interpol.line(1)*P; + p[2] = tinfo.interpol.line(2)*P; + +#else + Vec3 p(GP[0], GP[1], 1-GP[0]-GP[1]); +#endif + + Real b1 = tinfo.interpol(0,1); + Real c1 = tinfo.interpol(0,2); + Real b2 = tinfo.interpol(1,1); + Real c2 = tinfo.interpol(1,2); + Real b3 = tinfo.interpol(2,1); + Real c3 = tinfo.interpol(2,2); + + Vec3 p2(p[0]*p[0], p[1]*p[1], p[2]*p[2]); // Squares of p + + // Derivatives of powers of p (by x and y) + Real DPhi1n3_x= 3.0 * b1 * p2[0]; + Real DPhi1n2_x= 2.0 * b1 * p[0]; + Real DPhi1n3_y= 3.0 * c1 * p2[0]; + Real DPhi1n2_y= 2.0 * c1 * p[0]; + Real DPhi2n3_x= 3.0 * b2 * p2[1]; + Real DPhi2n2_x= 2.0 * b2 * p[1]; + Real DPhi2n3_y= 3.0 * c2 * p2[1]; + Real DPhi2n2_y= 2.0 * c2 * p[1]; + Real DPhi3n3_x= 3.0 * b3 * p2[2]; + Real DPhi3n2_x= 2.0 * b3 * p[2]; + Real DPhi3n3_y= 3.0 * c3 * p2[2]; + Real DPhi3n2_y= 2.0 * c3 * p[2]; + + Real DPhi123_x = b1*p[1]*p[2] + p[0]*b2*p[2] + p[0]*p[1]*b3; + Real DPhi123_y = c1*p[1]*p[2] + p[0]*c2*p[2] + p[0]*p[1]*c3; + + // Derivatives of the U1, U2, U3 parts (with respect to x and y) + Real du_dx_U1= DPhi1n3_x + 3.0*p2[0]*b2 + 3.0*DPhi1n2_x*p[1] + 3.0*p2[0]*b3 + 3.0*DPhi1n2_x*p[2] + 2.0*DPhi123_x; + Real du_dx_U2= DPhi2n3_x + 3.0*p2[1]*b3 + 3.0*DPhi2n2_x*p[2] + 3.0*p2[1]*b1 + 3.0*DPhi2n2_x*p[0] + 2.0*DPhi123_x; + Real du_dx_U3= DPhi3n3_x + 3.0*p2[2]*b1 + 3.0*DPhi3n2_x*p[0] + 3.0*p2[2]*b2 + 3.0*DPhi3n2_x*p[1] + 2.0*DPhi123_x; + + // du_dx = du_dx_DU1 * dU1 + ... + + Real du_dy_U1= DPhi1n3_y + 3.0*p2[0]*c2 + 3.0*DPhi1n2_y*p[1] + 3.0*p2[0]*c3 + 3.0*DPhi1n2_y*p[2] + 2.0*DPhi123_y; + Real du_dy_U2= DPhi2n3_y + 3.0*p2[1]*c3 + 3.0*DPhi2n2_y*p[2] + 3.0*p2[1]*c1 + 3.0*DPhi2n2_y*p[0] + 2.0*DPhi123_y; + Real du_dy_U3= DPhi3n3_y + 3.0*p2[2]*c1 + 3.0*DPhi3n2_y*p[0] + 3.0*p2[2]*c2 + 3.0*DPhi3n2_y*p[1] + 2.0*DPhi123_y; + + + // Derivatives of Theta1..3 parts (with respect to x and y) + Real dux_dx_T1= 3.0*DPhi1n2_x*p[1]*P4P1[1] + 3.0*p2[0]*b2*P4P1[1] + 3.0*DPhi1n2_x*p[2]*P5P1[1] + 3.0*p2[0]*b3*P5P1[1] + 2.0*DPhi123_x*P10P1[1]; + Real duy_dx_T1=-3.0*DPhi1n2_x*p[1]*P4P1[0] - 3.0*p2[0]*b2*P4P1[0] - 3.0*DPhi1n2_x*p[2]*P5P1[0] - 3.0*p2[0]*b3*P5P1[0] - 2.0*DPhi123_x*P10P1[0]; + + Real dux_dy_T1= 3.0*DPhi1n2_y*p[1]*P4P1[1] + 3.0*p2[0]*c2*P4P1[1] + 3.0*DPhi1n2_y*p[2]*P5P1[1] + 3.0*p2[0]*c3*P5P1[1] + 2.0*DPhi123_y*P10P1[1]; + Real duy_dy_T1=-3.0*DPhi1n2_y*p[1]*P4P1[0] - 3.0*p2[0]*c2*P4P1[0] - 3.0*DPhi1n2_y*p[2]*P5P1[0] - 3.0*p2[0]*c3*P5P1[0] - 2.0*DPhi123_y*P10P1[0]; + + + Real dux_dx_T2= 3.0*DPhi2n2_x*p[2]*P6P2[1] + 3.0*p2[1]*b3*P6P2[1] + 3.0*DPhi2n2_x*p[0]*P7P2[1] + 3.0*p2[1]*b1*P7P2[1] + 2.0*DPhi123_x*P10P2[1]; + Real duy_dx_T2=-3.0*DPhi2n2_x*p[2]*P6P2[0] - 3.0*p2[1]*b3*P6P2[0] - 3.0*DPhi2n2_x*p[0]*P7P2[0] - 3.0*p2[1]*b1*P7P2[0] - 2.0*DPhi123_x*P10P2[0]; + + Real dux_dy_T2= 3.0*DPhi2n2_y*p[2]*P6P2[1] + 3.0*p2[1]*c3*P6P2[1] + 3.0*DPhi2n2_y*p[0]*P7P2[1] + 3.0*p2[1]*c1*P7P2[1] + 2.0*DPhi123_y*P10P2[1]; + Real duy_dy_T2=-3.0*DPhi2n2_y*p[2]*P6P2[0] - 3.0*p2[1]*c3*P6P2[0] - 3.0*DPhi2n2_y*p[0]*P7P2[0] - 3.0*p2[1]*c1*P7P2[0] - 2.0*DPhi123_y*P10P2[0]; + + + Real dux_dx_T3= 3.0*DPhi3n2_x*p[0]*P8P3[1] + 3.0*p2[2]*b1*P8P3[1] + 3.0*DPhi3n2_x*p[1]*P9P3[1] + 3.0*p2[2]*b2*P9P3[1] + 2.0*DPhi123_x*P10P3[1]; + Real duy_dx_T3=-3.0*DPhi3n2_x*p[0]*P8P3[0] - 3.0*p2[2]*b1*P8P3[0] - 3.0*DPhi3n2_x*p[1]*P9P3[0] - 3.0*p2[2]*b2*P9P3[0] - 2.0*DPhi123_x*P10P3[0]; + + Real dux_dy_T3= 3.0*DPhi3n2_y*p[0]*P8P3[1] + 3.0*p2[2]*c1*P8P3[1] + 3.0*DPhi3n2_y*p[1]*P9P3[1] + 3.0*p2[2]*c2*P9P3[1] + 2.0*DPhi123_y*P10P3[1]; + Real duy_dy_T3=-3.0*DPhi3n2_y*p[0]*P8P3[0] - 3.0*p2[2]*c1*P8P3[0] - 3.0*DPhi3n2_y*p[1]*P9P3[0] - 3.0*p2[2]*c2*P9P3[0] - 2.0*DPhi123_y*P10P3[0]; + + + J[0][0] = du_dx_U1; + J[0][1] = 0; + J[0][2] = -dux_dx_T1; + J[0][3] = du_dx_U2; + J[0][4] = 0; + J[0][5] = -dux_dx_T2; + J[0][6] = du_dx_U3; + J[0][7] = 0; + J[0][8] = -dux_dx_T3; + + J[1][0] = 0; + J[1][1] = du_dy_U1; + J[1][2] = -duy_dy_T1; + J[1][3] = 0; + J[1][4] = du_dy_U2; + J[1][5] = -duy_dy_T2; + J[1][6] = 0; + J[1][7] = du_dy_U3; + J[1][8] = -duy_dy_T3; + + J[2][0] = du_dy_U1; + J[2][1] = du_dx_U1; + J[2][2] = -dux_dy_T1 - duy_dx_T1; + J[2][3] = du_dy_U2; + J[2][4] = du_dx_U2; + J[2][5] = -duy_dx_T2 - dux_dy_T2; + J[2][6] = du_dy_U3; + J[2][7] = du_dx_U3; + J[2][8] = -duy_dx_T3 - dux_dy_T3; +} + + +// ------------------------------------------------------------------------------------------------------------ +// --- Compute the bending strain-displacement matrix where (a, b, c) are the coordinates of the 3 nodes of a triangle +// ------------------------------------------------------------------------------------------------------------ +template +void BezierTriangularBendingFEMForceField::computeStrainDisplacementMatrixBending(TriangleInformation &tinfo) +{ +#ifndef GAUSS4 + // Calculation of the 3 Gauss points + Vec3 gaussPoint1 = tinfo.pts[0]*(2.0/3.0) + tinfo.pts[1]/6.0 + tinfo.pts[2]/6.0; + Vec3 gaussPoint2 = tinfo.pts[0]/6.0 + tinfo.pts[1]*(2.0/3.0) + tinfo.pts[2]/6.0; + Vec3 gaussPoint3 = tinfo.pts[0]/6.0 + tinfo.pts[1]/6.0 + tinfo.pts[2]*(2.0/3.0); + + matrixSDB(tinfo.strainDisplacementMatrixB1, gaussPoint1, tinfo); + matrixSDB(tinfo.strainDisplacementMatrixB2, gaussPoint2, tinfo); + matrixSDB(tinfo.strainDisplacementMatrixB3, gaussPoint3, tinfo); +#else + matrixSDB(tinfo.strainDisplacementMatrixB1, Vec3(0.211324865, 0.166666667, 0), tinfo); + matrixSDB(tinfo.strainDisplacementMatrixB2, Vec3(0.211324865, 0.622008467, 0), tinfo); + matrixSDB(tinfo.strainDisplacementMatrixB3, Vec3(0.788675134, 0.044658198, 0), tinfo); + matrixSDB(tinfo.strainDisplacementMatrixB4, Vec3(0.788675134, 0.166666667, 0), tinfo); +#endif + + msg_info() << "Bb: " << tinfo.strainDisplacementMatrixB1 << + "\n " << tinfo.strainDisplacementMatrixB2 << + "\n " << tinfo.strainDisplacementMatrixB3 << +#ifdef GAUSS4 + "\n " << tinfo.strainDisplacementMatrixB4 << +#endif + "\n"; +} + +// ---------------------------------------------------------------------------- +// --- Compute the strain-displacement matrix for bending deformation +// ----------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::matrixSDB( + StrainDisplacementBending &J, const Vec3 &GP, const TriangleInformation& tinfo) +{ + // Directional vectors from corner nodes + Vec3 P4P1 = tinfo.pts[0] - tinfo.pts[3] ; + Vec3 P5P1 = tinfo.pts[0] - tinfo.pts[4] ; + Vec3 P6P2 = tinfo.pts[1] - tinfo.pts[5] ; + Vec3 P7P2 = tinfo.pts[1] - tinfo.pts[6] ; + Vec3 P8P3 = tinfo.pts[2] - tinfo.pts[7] ; + Vec3 P9P3 = tinfo.pts[2] - tinfo.pts[8] ; + Vec3 P10P1 = tinfo.pts[0] - tinfo.pts[9] ; + Vec3 P10P2 = tinfo.pts[1] - tinfo.pts[9] ; + Vec3 P10P3 = tinfo.pts[2] - tinfo.pts[9] ; + + +#ifndef GAUSS4 + Vec3 P(1, GP[0], GP[1]); + + Vec3 p; // Barycentric coordinates of the point GP + p[0] = tinfo.interpol.line(0)*P; + p[1] = tinfo.interpol.line(1)*P; + p[2] = tinfo.interpol.line(2)*P; + +#else + Vec3 p(GP[0], GP[1], 1-GP[0]-GP[1]); +#endif + + Real b1 = tinfo.interpol(0,1); + Real c1 = tinfo.interpol(0,2); + Real b2 = tinfo.interpol(1,1); + Real c2 = tinfo.interpol(1,2); + Real b3 = tinfo.interpol(2,1); + Real c3 = tinfo.interpol(2,2); + + //Vec3 p2(p[0]*p[0], p[1]*p[1], p[2]*p[2]); // Squares of p + + // Doubles derivatives by x and y + Real D2Phi1n3_xx = 6*b1*b1*p[0]; + Real D2Phi1n3_xy = 6*b1*c1*p[0]; + Real D2Phi1n3_yy = 6*c1*c1*p[0]; + + Real D2Phi2n3_xx = 6*b2*b2*p[1]; + Real D2Phi2n3_xy = 6*b2*c2*p[1]; + Real D2Phi2n3_yy = 6*c2*c2*p[1]; + + Real D2Phi3n3_xx = 6*b3*b3*p[2]; + Real D2Phi3n3_xy = 6*b3*c3*p[2]; + Real D2Phi3n3_yy = 6*c3*c3*p[2]; + + Real D2Phi123_xx = 2.0*b1*b2*p[2] + 2.0*b1*p[1]*b3 + 2.0*p[0]*b2*b3; + Real D2Phi123_xy = c1*b2*p[2] + c1*p[1]*b3 + b1*c2*p[2]+ p[0]*c2*b3 + b1*p[1]*c3 + p[0]*b2*c3; + Real D2Phi123_yy = 2.0*c1*c2*p[2] + 2.0*c1*p[1]*c3 + 2.0*p[0]*c2*c3; + + Real D2Phi1n2Phi2_xx = 2.0*b1*b1*p[1]+ 4.0*p[0]*b1*b2; + Real D2Phi1n2Phi3_xx = 2.0*b1*b1*p[2]+ 4.0*p[0]*b1*b3; + Real D2Phi1n2Phi2_yy = 2.0*c1*c1*p[1]+ 4.0*p[0]*c1*c2; + Real D2Phi1n2Phi3_yy = 2.0*c1*c1*p[2]+ 4.0*p[0]*c1*c3; + Real D2Phi1n2Phi2_xy = 2.0*b1*c1*p[1]+ 2.0*b1*p[0]*c2 + 2.0*p[0]*c1*b2; + Real D2Phi1n2Phi3_xy = 2.0*b1*c1*p[2]+ 2.0*b1*p[0]*c3 + 2.0*p[0]*c1*b3; + + Real D2Phi2n2Phi1_xx = 2.0*b2*b2*p[0]+ 4.0*p[1]*b2*b1; + Real D2Phi2n2Phi3_xx = 2.0*b2*b2*p[2]+ 4.0*p[1]*b2*b3; + Real D2Phi2n2Phi1_yy = 2.0*c2*c2*p[0]+ 4.0*p[1]*c2*c1; + Real D2Phi2n2Phi3_yy = 2.0*c2*c2*p[2]+ 4.0*p[1]*c2*c3; + Real D2Phi2n2Phi1_xy = 2.0*b2*c2*p[0]+ 2.0*b2*p[1]*c1 + 2.0*p[1]*c2*b1; + Real D2Phi2n2Phi3_xy = 2.0*b2*c2*p[2]+ 2.0*b2*p[1]*c3 + 2.0*p[1]*c2*b3; + + Real D2Phi3n2Phi1_xx = 2.0*b3*b3*p[0]+ 4.0*p[2]*b3*b1; + Real D2Phi3n2Phi2_xx = 2.0*b3*b3*p[1]+ 4.0*p[2]*b3*b2; + Real D2Phi3n2Phi1_yy = 2.0*c3*c3*p[0]+ 4.0*p[2]*c3*c1; + Real D2Phi3n2Phi2_yy = 2.0*c3*c3*p[1]+ 4.0*p[2]*c3*c2; + Real D2Phi3n2Phi1_xy = 2.0*b3*c3*p[0]+ 2.0*b3*p[2]*c1 + 2.0*p[2]*c3*b1; + Real D2Phi3n2Phi2_xy = 2.0*b3*c3*p[1]+ 2.0*b3*p[2]*c2 + 2.0*p[2]*c3*b2; + + // Second derivatives of uz (with respect to x and y) -- translation parts + Real d2uz_dxx_dU1 = D2Phi1n3_xx + 3.0*D2Phi1n2Phi2_xx + 3.0*D2Phi1n2Phi3_xx + 2.0*D2Phi123_xx; + Real d2uz_dxy_dU1 = D2Phi1n3_xy + 3.0*D2Phi1n2Phi2_xy + 3.0*D2Phi1n2Phi3_xy + 2.0*D2Phi123_xy; + Real d2uz_dyy_dU1 = D2Phi1n3_yy + 3.0*D2Phi1n2Phi2_yy + 3.0*D2Phi1n2Phi3_yy + 2.0*D2Phi123_yy; + + Real d2uz_dxx_dU2 = D2Phi2n3_xx + 3.0*D2Phi2n2Phi1_xx + 3.0*D2Phi2n2Phi3_xx + 2.0*D2Phi123_xx; + Real d2uz_dxy_dU2 = D2Phi2n3_xy + 3.0*D2Phi2n2Phi1_xy + 3.0*D2Phi2n2Phi3_xy + 2.0*D2Phi123_xy; + Real d2uz_dyy_dU2 = D2Phi2n3_yy + 3.0*D2Phi2n2Phi1_yy + 3.0*D2Phi2n2Phi3_yy + 2.0*D2Phi123_yy; + + Real d2uz_dxx_dU3 = D2Phi3n3_xx + 3.0*D2Phi3n2Phi1_xx + 3.0*D2Phi3n2Phi2_xx + 2.0*D2Phi123_xx; + Real d2uz_dxy_dU3 = D2Phi3n3_xy + 3.0*D2Phi3n2Phi1_xy + 3.0*D2Phi3n2Phi2_xy + 2.0*D2Phi123_xy; + Real d2uz_dyy_dU3 = D2Phi3n3_yy + 3.0*D2Phi3n2Phi1_yy + 3.0*D2Phi3n2Phi2_yy + 2.0*D2Phi123_yy; + + // The macro gives a vector with first two values (the third one is 0) on + // the 3rd line of the 3x3 vector cross product matrix +#define CROSS_VEC(p) Vec2 cv##p(-(p)[1], (p)[0]) + CROSS_VEC(P4P1); CROSS_VEC(P6P2); CROSS_VEC(P8P3); + CROSS_VEC(P5P1); CROSS_VEC(P7P2); CROSS_VEC(P9P3); + CROSS_VEC(P10P1); CROSS_VEC(P10P2); CROSS_VEC(P10P3); +#undef CROSS_VEC + + // Second derivatives with of uz (with respect to x and y) -- rotation parts + Vec2 d2uz_dxx_dT1 = cvP4P1*3.0*D2Phi1n2Phi2_xx + cvP5P1*3.0*D2Phi1n2Phi3_xx + cvP10P1*2.0*D2Phi123_xx; + Vec2 d2uz_dxy_dT1 = cvP4P1*3.0*D2Phi1n2Phi2_xy + cvP5P1*3.0*D2Phi1n2Phi3_xy + cvP10P1*2.0*D2Phi123_xy; + Vec2 d2uz_dyy_dT1 = cvP4P1*3.0*D2Phi1n2Phi2_yy + cvP5P1*3.0*D2Phi1n2Phi3_yy + cvP10P1*2.0*D2Phi123_yy; + + Vec2 d2uz_dxx_dT2 = cvP6P2*3.0*D2Phi2n2Phi3_xx + cvP7P2*3.0*D2Phi2n2Phi1_xx + cvP10P2*2.0*D2Phi123_xx; + Vec2 d2uz_dxy_dT2 = cvP6P2*3.0*D2Phi2n2Phi3_xy + cvP7P2*3.0*D2Phi2n2Phi1_xy + cvP10P2*2.0*D2Phi123_xy; + Vec2 d2uz_dyy_dT2 = cvP6P2*3.0*D2Phi2n2Phi3_yy + cvP7P2*3.0*D2Phi2n2Phi1_yy + cvP10P2*2.0*D2Phi123_yy; + + Vec2 d2uz_dxx_dT3 = cvP8P3*3.0*D2Phi3n2Phi1_xx + cvP9P3*3.0*D2Phi3n2Phi2_xx + cvP10P3*2.0*D2Phi123_xx; + Vec2 d2uz_dxy_dT3 = cvP8P3*3.0*D2Phi3n2Phi1_xy + cvP9P3*3.0*D2Phi3n2Phi2_xy + cvP10P3*2.0*D2Phi123_xy; + Vec2 d2uz_dyy_dT3 = cvP8P3*3.0*D2Phi3n2Phi1_yy + cvP9P3*3.0*D2Phi3n2Phi2_yy + cvP10P3*2.0*D2Phi123_yy; + + + J[0][0] = -d2uz_dxx_dU1; + J[0][1] = -d2uz_dxx_dT1[0]; + J[0][2] = -d2uz_dxx_dT1[1]; + J[0][3] = -d2uz_dxx_dU2; + J[0][4] = -d2uz_dxx_dT2[0]; + J[0][5] = -d2uz_dxx_dT2[1]; + J[0][6] = -d2uz_dxx_dU3; + J[0][7] = -d2uz_dxx_dT3[0]; + J[0][8] = -d2uz_dxx_dT3[1]; + + J[1][0] = -d2uz_dyy_dU1; + J[1][1] = -d2uz_dyy_dT1[0]; + J[1][2] = -d2uz_dyy_dT1[1]; + J[1][3] = -d2uz_dyy_dU2; + J[1][4] = -d2uz_dyy_dT2[0]; + J[1][5] = -d2uz_dyy_dT2[1]; + J[1][6] = -d2uz_dyy_dU3; + J[1][7] = -d2uz_dyy_dT3[0]; + J[1][8] = -d2uz_dyy_dT3[1]; + + J[2][0] = -2.0*d2uz_dxy_dU1; + J[2][1] = -2.0*d2uz_dxy_dT1[0]; + J[2][2] = -2.0*d2uz_dxy_dT1[1]; + J[2][3] = -2.0*d2uz_dxy_dU2; + J[2][4] = -2.0*d2uz_dxy_dT2[0]; + J[2][5] = -2.0*d2uz_dxy_dT2[1]; + J[2][6] = -2.0*d2uz_dxy_dU3; + J[2][7] = -2.0*d2uz_dxy_dT3[0]; + J[2][8] = -2.0*d2uz_dxy_dT3[1]; +} + + + + +// ----------------------------------------------------------------------------- +// --- Compute the stiffness matrix K = J * M * Jt where J is the +// --- strain-displacement matrix and M the material matrix. Uses Gaussian +// --- quadrature for integration over the shell area. +// ----------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::computeStiffnessMatrixMembrane( + StiffnessMatrix &K, const TriangleInformation &tinfo) +{ + Mat<9, 3, Real> Jt1, Jt2, Jt3; + Jt1.transpose(tinfo.strainDisplacementMatrix1); + Jt2.transpose(tinfo.strainDisplacementMatrix2); + Jt3.transpose(tinfo.strainDisplacementMatrix3); + +#ifndef GAUSS4 + K = Jt1 * materialMatrix * tinfo.strainDisplacementMatrix1 + + Jt2 * materialMatrix * tinfo.strainDisplacementMatrix2 + + Jt3 * materialMatrix * tinfo.strainDisplacementMatrix3; + K /= 3.0; +#else + Mat<9, 3, Real> Jt4; + Jt4.transpose(tinfo.strainDisplacementMatrix4); + K = Jt1 * materialMatrix * tinfo.strainDisplacementMatrix1*0.197168783 + + Jt2 * materialMatrix * tinfo.strainDisplacementMatrix2*0.197168783 + + Jt3 * materialMatrix * tinfo.strainDisplacementMatrix3*0.052831216 + + Jt4 * materialMatrix * tinfo.strainDisplacementMatrix4*0.052831216; + K *= tinfo.area2; +#endif + + msg_info() << "2*Area = " << tinfo.area2 ; + msg_info() << "Km = " << K ; + Displacement u = Vec<9,Real>(1, -5, 0, 1, -5, 0, 1, -5, 0); + msg_info() << "-- Disp test Km (u=" << u << ")" << + " : " << K * u << " ... should be zero" ; +} + + +// ----------------------------------------------------------------------------- +// --- Compute the stiffness matrix for bending K = J * M * Jt where J is the +// --- strain-displacement matrix and M the material matrix. Uses Gaussian +// --- quadrature for integration over the shell area. +// ----------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::computeStiffnessMatrixBending(StiffnessMatrixBending &K, const TriangleInformation &tinfo) +{ + Mat<9, 3, Real> J1t, J2t, J3t; + J1t.transpose(tinfo.strainDisplacementMatrixB1); + J2t.transpose(tinfo.strainDisplacementMatrixB2); + J3t.transpose(tinfo.strainDisplacementMatrixB3); + +#ifndef GAUSS4 + K = J1t * materialMatrixBending * tinfo.strainDisplacementMatrixB1 + + J2t * materialMatrixBending * tinfo.strainDisplacementMatrixB2 + + J3t * materialMatrixBending * tinfo.strainDisplacementMatrixB3; + K /= 3.0; +#else + Mat<9, 3, Real> J4t; + J4t.transpose(tinfo.strainDisplacementMatrixB4); + K = J1t * materialMatrixBending * tinfo.strainDisplacementMatrixB1*0.197168783 + + J2t * materialMatrixBending * tinfo.strainDisplacementMatrixB2*0.197168783 + + J3t * materialMatrixBending * tinfo.strainDisplacementMatrixB3*0.052831216 + + J4t * materialMatrixBending * tinfo.strainDisplacementMatrixB4*0.052831216; + K *= tinfo.area2; +#endif + + msg_info() << "Kb = " << K ; +} + +// ----------------------------------------------------------------------------- +// --- Compute material matrix for plan stress and bending (Hooke's law) +// --- NOTE: These are alraedy integrated over the thickness of the shell. +// ----------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::computeMaterialMatrix() +{ + Real t = f_thickness.getValue(); + + // Material matrix for plain stress + materialMatrix[0][0] = 1; + materialMatrix[0][1] = f_poisson.getValue(); + materialMatrix[0][2] = 0; + materialMatrix[1][0] = f_poisson.getValue(); + materialMatrix[1][1] = 1; + materialMatrix[1][2] = 0; + materialMatrix[2][0] = 0; + materialMatrix[2][1] = 0; + materialMatrix[2][2] = (1 - f_poisson.getValue())/2; + + materialMatrix *= f_young.getValue() / ( + 1 - f_poisson.getValue() * f_poisson.getValue()); + + materialMatrix *= t; // consider the thickness + + // Material matrix for plane bending (a.k.a. flextural rigidity/bending stiffness) + materialMatrixBending = materialMatrix * t*t / 12; + + triangleInfo.endEdit(); +} + + +// ----------------------------------------------------------------------------- +// --- Compute force F = J * material * Jt * u +// ----------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::computeForceMembrane( + Displacement &F, const Displacement& D, const Index elementIndex) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation &tinfo = triangleInf[elementIndex]; + + // Compute forces + F = tinfo.stiffnessMatrix * D; + + triangleInfo.endEdit(); +} + + +// -------------------------------------------------------------------------------------- +// --- Compute force F = Jt * material * J * u +// -------------------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::computeForceBending(DisplacementBending &F_bending, const DisplacementBending& D_bending, const Index elementIndex) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation &tinfo = triangleInf[elementIndex]; + + // Compute forces + F_bending = tinfo.stiffnessMatrixBending * D_bending; + + triangleInfo.endEdit(); +} + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::accumulateForce(VecDeriv &f, const VecCoord &x, const Index elementIndex) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &triangleInf[elementIndex]; + + // Get the indices of the 3 vertices for the current triangle + const Index& a = tinfo->a; + const Index& b = tinfo->b; + const Index& c = tinfo->c; + + // Compute the quaternion that embodies the rotation between the triangle + // and world frames (co-rotational method) + interpolateRefFrame(tinfo, Vec2(1.0/3.0, 1.0/3.0), + tinfo->a, tinfo->b, tinfo->c, x, + tinfo->bezierNodes); + + computeLocalTriangle(x, elementIndex); + + // Compute in-plane and bending displacements in the triangle's frame + Displacement D; + DisplacementBending D_bending; + computeDisplacements(D, D_bending, x, tinfo); + + // Compute in-plane forces on this element (in the co-rotational space) + Displacement F; + computeForceMembrane(F, D, elementIndex); + + // Compute bending forces on this element (in the co-rotational space) + DisplacementBending F_bending; + computeForceBending(F_bending, D_bending, elementIndex); + + + + // Transform forces back into global reference frame + Vec3 fa1 = tinfo->frameOrientationInv * Vec3(F[0], F[1], F_bending[0]); + Vec3 fa2 = tinfo->frameOrientationInv * Vec3(F_bending[1], F_bending[2], F[2]); + + Vec3 fb1 = tinfo->frameOrientationInv * Vec3(F[3], F[4], F_bending[3]); + Vec3 fb2 = tinfo->frameOrientationInv * Vec3(F_bending[4], F_bending[5], F[5]); + + Vec3 fc1 = tinfo->frameOrientationInv * Vec3(F[6], F[7], F_bending[6]); + Vec3 fc2 = tinfo->frameOrientationInv * Vec3(F_bending[7], F_bending[8], F[8]); + + msg_info() << "E: " << elementIndex << "\tu: " << D << "\n\tf: " << F ; + msg_info() << "E: " << elementIndex << "\tuB: " << D_bending << "\n\tfB: " << F_bending ; + msg_info() << " xg [ " << a << "/" << b << "/" << c << " - " << x[a] << " || " << x[b] << " || " << x[c] ; + msg_info() << " xl [ " << tinfo->pts[0] << ", " << tinfo->pts[1] << ", " << tinfo->pts[2] ; + msg_info() << " fg: " << Deriv(-fa1, -fa2) << " | " << Deriv(-fb1, -fb2) << " | " << Deriv(-fc1, -fc2) ; + + f[a] += Deriv(-fa1, -fa2); + f[b] += Deriv(-fb1, -fb2); + f[c] += Deriv(-fc1, -fc2); + + triangleInfo.endEdit(); +} + + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +void BezierTriangularBendingFEMForceField::addForce(const sofa::core::MechanicalParams* /*mparams*/, DataVecDeriv& dataF, const DataVecCoord& dataX, const DataVecDeriv& /*dataV*/ ) +{ + VecDeriv& f = *(dataF.beginEdit()); + const VecCoord& p = dataX.getValue() ; + + int nbTriangles=_topology->getNbTriangles(); + f.resize(p.size()); + + for (int i=0; i +void BezierTriangularBendingFEMForceField::addDForce(const sofa::core::MechanicalParams* mparams, DataVecDeriv& datadF, const DataVecDeriv& datadX ) +{ + VecDeriv& df = *(datadF.beginEdit()); + const VecDeriv& dp = datadX.getValue() ; + + double kFactor = mparams->kFactor(); + + int nbTriangles=_topology->getNbTriangles(); + df.resize(dp.size()); + + for (int i=0; i +void BezierTriangularBendingFEMForceField::convertStiffnessMatrixToGlobalSpace(StiffnessMatrixGlobalSpace &K_gs, TriangleInformation *tinfo) +{ + // Stiffness matrix of current triangle + const StiffnessMatrix &K = tinfo->stiffnessMatrix; + + // Firstly, add all degrees of freedom (we add the unused translation in z) + StiffnessMatrixGlobalSpace K_18x18; + K_18x18.clear(); + unsigned int ig, jg; + + // Copy the stiffness matrix into 18x18 matrix (the new index of each bloc into global matrix is a combination of 0, 6 and 12 in indices) + for (unsigned int bx=0; bx<3; bx++) + { + // Global row index + ig = 6*bx; + + for (unsigned int by=0; by<3; by++) + { + // Global column index + jg = 6*by; + + // linear X + K_18x18[ig+0][jg+0] = K[3*bx+0][3*by+0]; // linear X + K_18x18[ig+0][jg+1] = K[3*bx+0][3*by+1]; // linear Y + K_18x18[ig+0][jg+5] = K[3*bx+0][3*by+2]; // angular Z + + // linear Y + K_18x18[ig+1][jg+0] = K[3*bx+1][3*by+0]; // linear X + K_18x18[ig+1][jg+1] = K[3*bx+1][3*by+1]; // linear Y + K_18x18[ig+1][jg+5] = K[3*bx+1][3*by+2]; // angular Z + + // angular Z + K_18x18[ig+5][jg+0] = K[3*bx+2][3*by+0]; // linear X + K_18x18[ig+5][jg+1] = K[3*bx+2][3*by+1]; // linear Y + K_18x18[ig+5][jg+5] = K[3*bx+2][3*by+2]; // angular Z + } + } + + + // Stiffness matrix in bending of current triangle + const StiffnessMatrixBending &K_bending = tinfo->stiffnessMatrixBending; + + // Copy the stiffness matrix by block 3x3 into global matrix (the new index of each bloc into global matrix is a combination of 2, 8 and 15 in indices) + for (unsigned int bx=0; bx<3; bx++) + { + // Global row index + ig = 6*bx+2; + + for (unsigned int by=0; by<3; by++) + { + // Global column index + jg = 6*by+2; + + // Iterates over the indices of the 3x3 block + for (unsigned int i=0; i<3; i++) + { + for (unsigned int j=0; j<3; j++) + { + K_18x18[ig+i][jg+j] += K_bending[3*bx+i][3*by+j]; + } + } + + } + } + + // Extend rotation matrix and its transpose + StiffnessMatrixGlobalSpace R18x18, Rt18x18; + + for(unsigned int i=0;i<3;++i) + { + for(unsigned int j=0;j<3;++j) + { + R18x18[i][j] = R18x18[i+3][j+3] = R18x18[i+6][j+6] = R18x18[i+9][j+9] = R18x18[i+12][j+12] = R18x18[i+15][j+15] = + tinfo->frameOrientation[i][j]; + Rt18x18[i][j] = Rt18x18[i+3][j+3] = Rt18x18[i+6][j+6] = Rt18x18[i+9][j+9] = Rt18x18[i+12][j+12] = Rt18x18[i+15][j+15] = + tinfo->frameOrientationInv[i][j]; + } + } + + // Then we put the stifness matrix into the global frame + K_gs = Rt18x18 * K_18x18 * R18x18; + +} + +#define ASSEMBLED_K + +#ifdef ASSEMBLED_K + +template +void BezierTriangularBendingFEMForceField::addKToMatrix(const core::MechanicalParams* mparams, const sofa::core::behavior::MultiMatrixAccessor* matrix) +{ + StiffnessMatrixGlobalSpace K_gs; + + // Build Matrix Block for this ForceField + unsigned int i, j ,n1, n2, row, column, ROW, COLUMN; + Index node1, node2; + + sofa::core::behavior::MultiMatrixAccessor::MatrixRef r = matrix->getMatrix(this->mstate); + type::vector& triangleInf = *(triangleInfo.beginEdit()); + + double kFactor = mparams->kFactor(); + + for(sofa::Index t=0 ; t != _topology->getNbTriangles() ; ++t) + { + TriangleInformation *tinfo = &triangleInf[t]; + const Triangle triangle = _topology->getTriangle(t); + + convertStiffnessMatrixToGlobalSpace(K_gs, tinfo); + + // find index of node 1 + for (n1=0; n1<3; n1++) + { + node1 = triangle[n1]; + + for(i=0; i<6; i++) + { + ROW = r.offset+6*node1+i; + row = 6*n1+i; + // find index of node 2 + for (n2=0; n2<3; n2++) + { + node2 = triangle[n2]; + + for (j=0; j<6; j++) + { + COLUMN = r.offset+6*node2+j; + column = 6*n2+j; + r.matrix->add(ROW, COLUMN, - K_gs[row][column] * kFactor); + } + } + } + } + } + + triangleInfo.endEdit(); +} + + +#else + +template +void BezierTriangularBendingFEMForceField::addKToMatrix(sofa::linearalgebra::BaseMatrix *mat, SReal /*k*/, unsigned int &offset) +{ + VecCoord X = *this->mstate->getX(); + VecDeriv df, dx; + + dx.resize(X.size()); + df.resize(X.size()); + + for (unsigned int i=0; iadd(6*k+l, 6*i+j, -df[k][l]); + } + } + + } + } +} + +#endif + + +template +void BezierTriangularBendingFEMForceField::addBToMatrix(sofa::linearalgebra::BaseMatrix * /*mat*/, double /*bFact*/, unsigned int &/*offset*/) +{ +} + + +template +void BezierTriangularBendingFEMForceField::handleEvent(sofa::core::objectmodel::Event *event) +{ + if ( /*sofa::core::objectmodel::MeshChangedEvent* ev =*/ dynamic_cast(event)) + { + // Update of the rest shape + // NOTE: the number of triangles should be the same in all topologies + unsigned int nbTriangles = _topology->getNbTriangles(); + for (unsigned int i=0; i +void BezierTriangularBendingFEMForceField::draw(const core::visual::VisualParams* vparams) +{ + // TODO: draw the mapping between topologies. I'm not sure whether to put + // it in showForceField or showBehaviorModels + if(vparams->displayFlags().getShowForceFields()) + { + + // Gets vertices of rest and initial positions respectively + const VecCoord& x0 = this->mstate->read(sofa::core::vec_id::read_access::position)->getValue(); + + + type::vector& triangleInf = *(triangleInfo.beginEdit()); + + // Render Bezier points + + glPointSize(8); + glDisable(GL_LIGHTING); + glBegin(GL_POINTS); + + for (sofa::Index i=0; i<_topology->getNbTriangles(); ++i) + { + TriangleInformation *tinfo = &triangleInf[i]; + + for (int j=0; j<9; j++) + { + glColor4f(0.0, 0.7, 0.0, 1.0); + glVertex3f( + tinfo->bezierNodes[j][0], + tinfo->bezierNodes[j][1], + tinfo->bezierNodes[j][2]); + } + + // Central node in lighter color + glColor4f(0.0, 1.0, 0.0, 1.0); + glVertex3f( + tinfo->bezierNodes[9][0], + tinfo->bezierNodes[9][1], + tinfo->bezierNodes[9][2]); + } + + glEnd(); + glPointSize(1); + + // Render the frame of each element + for (sofa::Index i=0; i<_topology->getNbTriangles(); ++i) + { + TriangleInformation *tinfo = &triangleInf[i]; + + Vec3 P1P2= x0[tinfo->b].getCenter() - x0[tinfo->a].getCenter(); + Vec3 P2P3= x0[tinfo->c].getCenter() - x0[tinfo->b].getCenter(); + Vec3 P3P1= x0[tinfo->a].getCenter() - x0[tinfo->c].getCenter(); + double length = (P1P2.norm()+P2P3.norm()+P3P1.norm())/8.0; + +#ifdef CRQUAT + Quat qFrame = tinfo->frameOrientationQ.inverse(); +#else + Quat qFrame; + qFrame.fromMatrix(tinfo->frameOrientationInv); +#endif + vparams->drawTool()->drawFrame( + tinfo->frameCenter, + qFrame, + Vec3(length, length, length)); + + } + + triangleInfo.endEdit(); + } // if(getShowForceFields()) +} + +} // namespace forcefield + +} // namespace component + +} // namespace sofa + + +#endif // #ifndef SOFA_COMPONENT_FORCEFIELD_BEZIER_TRIANGULAR_BENDING_FEM_FORCEFIELD_INL diff --git a/src/Shell/forcefield/CstFEMForceField.cpp b/src/Shell/forcefield/CstFEMForceField.cpp new file mode 100644 index 0000000..9d6763b --- /dev/null +++ b/src/Shell/forcefield/CstFEMForceField.cpp @@ -0,0 +1,56 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include +#include +#include +#include +#include + + +namespace sofa +{ + +namespace component +{ + +namespace forcefield +{ + +using namespace sofa::defaulttype; + +// Register in the Factory +int CstFEMForceFieldClass = core::RegisterObject("Constant Strain Triangular membrane element") +.add< CstFEMForceField >(true) +; + +template class SOFA_SHELL_API CstFEMForceField; + + +} // namespace forcefield + +} // namespace component + +} // namespace sofa diff --git a/src/Shell/forcefield/CstFEMForceField.h b/src/Shell/forcefield/CstFEMForceField.h new file mode 100644 index 0000000..a03bd71 --- /dev/null +++ b/src/Shell/forcefield/CstFEMForceField.h @@ -0,0 +1,215 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#ifndef SOFA_COMPONENT_FORCEFIELD_CST_FEM_FORCEFIELD_H +#define SOFA_COMPONENT_FORCEFIELD_CST_FEM_FORCEFIELD_H + +#include +#include +#include + +#include +#include + + +namespace sofa +{ + +namespace component +{ + +namespace forcefield +{ + +using namespace sofa::type; +using sofa::type::vector; +using namespace sofa::core::topology; +using namespace sofa::core::behavior; + +/// This class can be overridden if needed for additionnal storage within template specializations. +template +class CstFEMForceFieldInternalData +{ +public: +}; + + +template +class CstFEMForceField : public core::behavior::ForceField +{ + public: + SOFA_CLASS(SOFA_TEMPLATE(CstFEMForceField,DataTypes), SOFA_TEMPLATE(core::behavior::ForceField,DataTypes)); + + typedef core::behavior::ForceField Inherited; + typedef typename DataTypes::VecCoord VecCoord; + typedef typename DataTypes::VecDeriv VecDeriv; + //typedef typename DataTypes::VecReal VecReal; + + typedef typename DataTypes::Coord Coord; + typedef typename DataTypes::Deriv Deriv; + typedef typename Coord::value_type Real; + + typedef Vec<2,Real> Vec2; + typedef Vec<3,Real> Vec3; + typedef Vec<9,Real> Vec9; + + typedef sofa::type::Quat Quat; + + typedef Data DataVecCoord; + typedef Data DataVecDeriv; + + typedef sofa::defaulttype::Vec3Types::VecCoord VecCoordHigh; + + typedef sofa::Index Index; + typedef sofa::core::topology::BaseMeshTopology::Triangle Triangle; + typedef sofa::core::topology::BaseMeshTopology::SeqTriangles SeqTriangles; + + class TriangleInformation; + + protected: + + typedef Vec<6, Real> Displacement; // the displacement vector + typedef Mat<3, 3, Real> MaterialStiffness; // the matrix of material stiffness + typedef Mat<3, 6, Real> StrainDisplacement; // the strain-displacement matrix + typedef Mat<6, 3, Real> StrainDisplacementT; // the strain-displacement matrix transposed + typedef Mat<3, 3, Real> Transformation; // matrix for rigid transformations like rotations + typedef Mat<6, 6, Real> StiffnessMatrix; // element stiffness matrix + typedef Mat<9, 9, Real> StiffnessMatrixFull; // element stiffness matrix in 3D + + sofa::core::topology::BaseMeshTopology* _topology; + +public: + + class TriangleInformation + { + public: + + // Indices of each vertex + Index a, b, c; + + // Rest position in local (in-plane) coordinates + type::fixed_array restPositions; + + // Deformed position in local (in-plane) coordinates + type::fixed_array deformedPositions; + + // Frame rotation as matrix and quaternion + Transformation R, Rt; + + // Stiffness matrix + StiffnessMatrix stiffnessMatrix; + + // The following are in rest shape + // - element area + Real area; + // - directional vectors: 1-2, 2-3, 3-1 + type::fixed_array d; + + /// Output stream + inline friend std::ostream& operator<< ( std::ostream& os, const TriangleInformation& /*ti*/ ) + { + return os; + } + + /// Input stream + inline friend std::istream& operator>> ( std::istream& in, TriangleInformation& /*ti*/ ) + { + return in; + } + }; + + class TRQSTriangleHandler : public TopologyDataHandler > + { + public: + TRQSTriangleHandler(CstFEMForceField* _ff, TriangleData >* _data) + : TopologyDataHandler >(_data) + , ff(_ff) + { + } + + void applyCreateFunction(unsigned int triangleIndex, TriangleInformation& , + const Triangle & t, + const sofa::type::vector< unsigned int > &, + const sofa::type::vector< double > &); + + protected: + CstFEMForceField* ff; + }; + + CstFEMForceField(); + + virtual ~CstFEMForceField(); + void init() override; + void reinit() override; + void addForce(const sofa::core::MechanicalParams* /*mparams*/, DataVecDeriv& dataF, const DataVecCoord& dataX, const DataVecDeriv& /*dataV*/ ) override ; + void addDForce(const sofa::core::MechanicalParams* /*mparams*/, DataVecDeriv& datadF, const DataVecDeriv& datadX ) override ; + void addKToMatrix(const core::MechanicalParams* mparams, const sofa::core::behavior::MultiMatrixAccessor* matrix) override; + + SReal getPotentialEnergy(const sofa::core::MechanicalParams* /*mparams*/, const DataVecCoord& /*x*/) const override { return 0; } + + sofa::core::topology::BaseMeshTopology* getTopology() {return _topology;} + + Data f_poisson; + Data f_young; + Data f_thickness; + Data f_corotated; + Data f_stiffnessFactor; + //Data f_measure; + //Data< type::vector > f_measuredValues; + + TRQSTriangleHandler* triangleHandler; + +protected : + + /// Material stiffness matrix + MaterialStiffness materialMatrix, materialMatrixMembrane; + TriangleData< sofa::type::vector > triangleInfo; + + // What to measure + //bool bMeasureStrain; + //bool bMeasureStress; + + void initTriangle(const int i, const Index&a, const Index&b, const Index&c); + + //void computeRotation(Transformation& R, const VecCoord &x, const Index &a, const Index &b, const Index &c); + void computeRotation(Transformation& R, const type::fixed_array &x); + void computeMaterialStiffness(); + void computeStiffnessMatrix(StiffnessMatrix &K, TriangleInformation &tinfo); + + void computeDisplacement(Displacement &D, const VecCoord &x, const Index elementIndex); + void accumulateForce(VecDeriv& f, const VecCoord & p, const Index elementIndex); + void computeForce(Displacement &F, const Displacement& D, const Index elementIndex); + virtual void applyStiffness(VecDeriv& f, const VecDeriv& dx, const Index elementIndex, const double kFactor); + + void convertStiffnessMatrixToGlobalSpace(StiffnessMatrixFull &K_gs, const TriangleInformation &tinfo); +}; + + +} // namespace forcefield + +} // namespace component + +} // namespace sofa + +#endif diff --git a/src/Shell/forcefield/CstFEMForceField.inl b/src/Shell/forcefield/CstFEMForceField.inl new file mode 100644 index 0000000..f114a54 --- /dev/null +++ b/src/Shell/forcefield/CstFEMForceField.inl @@ -0,0 +1,664 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#ifndef SOFA_COMPONENT_FORCEFIELD_CST_FEM_FORCEFIELD_INL +#define SOFA_COMPONENT_FORCEFIELD_CST_FEM_FORCEFIELD_INL + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + + +namespace sofa +{ + namespace component + { + namespace forcefield + { + using namespace sofa::type; + using namespace sofa::core::topology; + +// ---------------------------------------------------------------------------- +// --- Topology Creation/Destruction functions +// ---------------------------------------------------------------------------- +template< class DataTypes> +void CstFEMForceField::TRQSTriangleHandler::applyCreateFunction(unsigned int triangleIndex, TriangleInformation &, const Triangle &t, const sofa::type::vector &, const sofa::type::vector &) +{ + if (ff) + { + ff->initTriangle(triangleIndex, t[0], t[1], t[2]); + } +} + + +// ---------------------------------------------------------------------------- +// --- Constructor +// ---------------------------------------------------------------------------- +template +CstFEMForceField::CstFEMForceField() +: f_poisson(initData(&f_poisson,(Real)0.45,"poissonRatio","Poisson ratio in Hooke's law")) +, f_young(initData(&f_young,(Real)3000.,"youngModulus","Young modulus in Hooke's law")) +, f_thickness(initData(&f_thickness,(Real)0.1,"thickness","Thickness of the plates")) +, f_corotated(initData(&f_corotated, true, "corotated", "Compute forces in corotational frame")) +//, f_measure(initData(&f_measure, "measure", "Compute the strain or stress")) +//, f_measuredValues(initData(&f_measuredValues, "measuredValues", "Measured values for stress or strain")) +, f_stiffnessFactor(initData(&f_stiffnessFactor, Real(1.0), "stiffnessFactor", "stiffness factor between 0 and 1 to reduce the weight of the forcefield")) +, triangleInfo(initData(&triangleInfo, "triangleInfo", "Internal triangle data")) + +{ + //f_measure.beginEdit()->setNames(3, + // "None", // Draw nothing + // "Strain (norm)", // L_2 norm of strain in x and y directions + // "Von Mises stress" // Von Mises stress criterion + // ); + //f_measure.beginEdit()->setSelectedItem("None"); + //f_measure.endEdit(); + + triangleHandler = new TRQSTriangleHandler(this, &triangleInfo); +} + + +// ---------------------------------------------------------------------------- +// --- Destructor +// ---------------------------------------------------------------------------- +template +CstFEMForceField::~CstFEMForceField() +{ + if(triangleHandler) delete triangleHandler; +} + +// ---------------------------------------------------------------------------- +// --- Initialization stage +// ---------------------------------------------------------------------------- +template +void CstFEMForceField::init() +{ + this->Inherited::init(); + + _topology = this->getContext()->getMeshTopology(); + + if (_topology->getNbTriangles()==0) + { + msg_error() << "CstFEMForceField: object must have a Triangular Set Topology."; + return; + } + + // Create specific handler for TriangleData + triangleInfo.createTopologyHandler(_topology); + + reinit(); +} + + +// ---------------------------------------------------------------------------- +// --- Re-initialization (called when we change a parameter through the GUI) +// ---------------------------------------------------------------------------- +template void CstFEMForceField::reinit() +{ + type::vector& ti = *(triangleInfo.beginEdit()); + + // Prepare material matrices + computeMaterialStiffness(); + + //// What to compute? + //if (f_measure.getValue().getSelectedItem() == "None") { + // bMeasureStrain = false; bMeasureStress = false; + //} else if (f_measure.getValue().getSelectedItem() == "Strain (norm)") { + // bMeasureStrain = true; bMeasureStress = false; + //} else if (f_measure.getValue().getSelectedItem() == "Von Mises stress") { + // bMeasureStrain = false; bMeasureStress = true; + //} else { + // return; + //} + + //if (bMeasureStrain || bMeasureStress) + //{ + // f_measuredValues.beginEdit()->resize(_topology->getNbPoints()); + // f_measuredValues.endEdit(); + //} + + /// Prepare to store info in the triangle array + ti.resize(_topology->getNbTriangles()); + + for (sofa::Index i=0; i<_topology->getNbTriangles(); ++i) + { + + triangleHandler->applyCreateFunction(i, ti[i], _topology->getTriangle(i), + (const sofa::type::vector< unsigned int >)0, (const sofa::type::vector< double >)0); + } + + triangleInfo.endEdit(); +} + + +template +void CstFEMForceField::addForce(const sofa::core::MechanicalParams* /*mparams*/, DataVecDeriv& dataF, const DataVecCoord& dataX, const DataVecDeriv& /*dataV*/ ) +{ + VecDeriv& f = *(dataF.beginEdit()); + const VecCoord& p = dataX.getValue() ; + + //std::cout << "--addForce" << std::endl; + + int nbTriangles=_topology->getNbTriangles(); + f.resize(p.size()); + + for (int i=0; i +void CstFEMForceField::addDForce(const sofa::core::MechanicalParams* mparams, DataVecDeriv& datadF, const DataVecDeriv& datadX ) +{ + VecDeriv& df = *(datadF.beginEdit()); + const VecDeriv& dp = datadX.getValue() ; + + Real kFactor = (Real)sofa::core::mechanicalparams::kFactor(mparams); + + //std::cout << "--addDForce" << std::endl; + + int nbTriangles=_topology->getNbTriangles(); + df.resize(dp.size()); + + for (int i=0; i +void CstFEMForceField::initTriangle(const int i, const Index&a, const Index&b, const Index&c) +{ + type::vector& ti = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &ti[i]; + + // Store indices of each vertex + tinfo->a = a; + tinfo->b = b; + tinfo->c = c; + + // Gets vertices of rest positions + const VecCoord& x0 = this->mstate->read(sofa::core::vec_id::read_access::position)->getValue(); + + // Rotation from global to local frame + Transformation R0; + + // Rest positions expressed in the local frame + tinfo->restPositions[0] = x0[a]; + tinfo->restPositions[1] = x0[b]; + tinfo->restPositions[2] = x0[c]; + + computeRotation(R0, tinfo->restPositions); + tinfo->R = R0; + tinfo->Rt.transpose(R0); + + tinfo->restPositions[0] = R0 * tinfo->restPositions[0]; + tinfo->restPositions[1] = R0 * tinfo->restPositions[1]; + tinfo->restPositions[2] = R0 * tinfo->restPositions[2]; + + // Do some precomputations + // - directional vectors + tinfo->d[0] = tinfo->restPositions[0] - tinfo->restPositions[1]; + tinfo->d[1] = tinfo->restPositions[1] - tinfo->restPositions[2]; + tinfo->d[2] = tinfo->restPositions[2] - tinfo->restPositions[0]; + + // - triangle area + tinfo->area = helper::rabs(tinfo->d[2][0]*(-tinfo->d[0][1]) - (-tinfo->d[0][0])*tinfo->d[2][1])/2.0; + + // Compute stiffness matrix + computeStiffnessMatrix(tinfo->stiffnessMatrix, *tinfo); + //std::cout << "Km^e=" << tinfo->stiffnessMatrix << std::endl; + + triangleInfo.endEdit(); +} + + +template +void CstFEMForceField::computeRotation(Transformation& R, const type::fixed_array &x) +{ + if (!f_corotated.getValue()) { + // Return identity matrix + R = Transformation(Vec3(1,0,0), Vec3(0,1,0), Vec3(0,0,1)); + return; + } + + // First vector on first edge + // Second vector in the plane of the two first edges + // Third vector orthogonal to first and second + + Vec3 edgex = x[1] - x[0]; + Vec3 edgey = x[2] - x[0]; + + Vec3 edgez = cross(edgex, edgey); + + edgey = cross(edgez, edgex); + + edgex.normalize(); + edgey.normalize(); + edgez.normalize(); + + R = Transformation(edgex, edgey, edgez); +} + + +// ---------------------------------------------------------------------------- +// --- Compute material stiffness +// ---------------------------------------------------------------------------- +template +void CstFEMForceField::computeMaterialStiffness() +{ + Real E = f_young.getValue(), + nu = f_poisson.getValue(), + t = f_thickness.getValue(); + + materialMatrix[0][0] = 1.0; + materialMatrix[0][1] = nu; + materialMatrix[0][2] = 0; + materialMatrix[1][0] = nu; + materialMatrix[1][1] = 1.0; + materialMatrix[1][2] = 0; + materialMatrix[2][0] = 0; + materialMatrix[2][1] = 0; + materialMatrix[2][2] = (1 - nu)/2.0; + + materialMatrix *= E / (1.0 - nu * nu); + + materialMatrixMembrane = materialMatrix; + + // Integrate through the shell thickness + materialMatrixMembrane *= t; +} + +// ---------------------------------------------------------------------------- +// --- Compute the stiffness matrix for membrane element +// ---------------------------------------------------------------------------- +template +void CstFEMForceField::computeStiffnessMatrix(StiffnessMatrix &K, TriangleInformation &tinfo) +{ + StrainDisplacement L; + StrainDisplacementT Lt; + L.clear(); + + L[0][0] = tinfo.d[1][1]; + L[0][1] = 0; + L[0][2] = tinfo.d[2][1]; + L[0][3] = 0; + L[0][4] = tinfo.d[0][1]; + L[0][5] = 0; + + L[1][0] = 0; + L[1][1] = -tinfo.d[1][0]; + L[1][2] = 0; + L[1][3] = -tinfo.d[2][0]; + L[1][4] = 0; + L[1][5] = -tinfo.d[0][0]; + + L[2][0] = -tinfo.d[1][0]; + L[2][1] = tinfo.d[1][1]; + L[2][2] = -tinfo.d[2][0]; + L[2][3] = tinfo.d[2][1]; + L[2][4] = -tinfo.d[0][0]; + L[2][5] = tinfo.d[0][1]; + + // Now should be: + // L *= h/2; + // K = 1/(h*Area) * L * E * L^T; + // But we may simplify this a little and we also have 'h' already in E + + Lt.transpose(L); + + K = Lt * materialMatrixMembrane * L / (4.0*tinfo.area); + + // Compute strain-displacement matrix + //for (unsigned int i=0; i< tinfo.measure.size(); i++) { + // tinfo.measure[i].B = Lt / (2*tinfo.area); + //} + + + if (this->f_printLog.getValue()) + { + Displacement u = Vec<6,Real>(1, -5, 1, -5, 1, -5); + Vec<6,Real> f = K*u; + if (helper::rabs(f.sum()) > 1e-12) { + dmsg_info() << "Area = " << tinfo.area; + dmsg_info() << "a / b / c = " << + tinfo.restPositions[0] << " / " << + tinfo.restPositions[1] << " / " << + tinfo.restPositions[2]; + dmsg_info() << "d = " << tinfo.d; + dmsg_info() << "Km = " << K; + dmsg_info() << "-- Disp test Km (u=" << u << ")" << + " : " << f << " ... should be zero"; + } + } +} + + + +// ---------------------------------------------------------------------------- +// --- Compute displacement vector D as the difference between current current +// --- position and initial position. +// ---------------------------------------------------------------------------- +template +void CstFEMForceField::computeDisplacement(Displacement &D, const VecCoord &x, const Index elementIndex) +{ + type::vector& ti = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &ti[elementIndex]; + + Index a = tinfo->a; + Index b = tinfo->b; + Index c = tinfo->c; + + // Compute local (in-plane) postions + tinfo->deformedPositions[0] = x[a]; + tinfo->deformedPositions[1] = x[b]; + tinfo->deformedPositions[2] = x[c]; + + // Compute rotation to local (in-plane) frame + computeRotation(tinfo->R, tinfo->deformedPositions); + tinfo->Rt.transpose(tinfo->R); + + // Compute local (in-plane) postions + tinfo->deformedPositions[0] = tinfo->R * tinfo->deformedPositions[0]; + tinfo->deformedPositions[1] = tinfo->R * tinfo->deformedPositions[1]; + tinfo->deformedPositions[2] = tinfo->R * tinfo->deformedPositions[2]; + + // Displacements + Vec3 uA = tinfo->deformedPositions[0] - tinfo->restPositions[0]; + Vec3 uB = tinfo->deformedPositions[1] - tinfo->restPositions[1]; + Vec3 uC = tinfo->deformedPositions[2] - tinfo->restPositions[2]; + + // Membrane + D[0] = uA[0]; + D[1] = uA[1]; + + D[2] = uB[0]; + D[3] = uB[1]; + + D[4] = uC[0]; + D[5] = uC[1]; + + triangleInfo.endEdit(); +} + + +template +void CstFEMForceField::accumulateForce(VecDeriv &f, const VecCoord &x, const Index elementIndex) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &triangleInf[elementIndex]; + + // Get the indices of the 3 vertices for the current triangle + const Index& a = tinfo->a; + const Index& b = tinfo->b; + const Index& c = tinfo->c; + + // Compute in-plane displacements + Displacement D; + computeDisplacement(D, x, elementIndex); + + // Compute the membrane and bending plate forces on this element + Displacement F; + computeForce(F, D, elementIndex); + + // Compute the measure (stress/strain) + //if (bMeasureStrain) { + // type::vector &values = *f_measuredValues.beginEdit(); + // for (unsigned int i=0; i< tinfo->measure.size(); i++) { + // Vec3 strain = tinfo->measure[i].B * Dm + tinfo->measure[i].Bb * Db; + // // Norm from strain in x and y + // // NOTE: Shear strain is not included + // values[ tinfo->measure[i].id ] = helper::rsqrt( + // strain[0] * strain[0] + strain[1] * strain[1]); + // } + // f_measuredValues.endEdit(); + //} else if (bMeasureStress) { + // type::vector &values = *f_measuredValues.beginEdit(); + // for (unsigned int i=0; i< tinfo->measure.size(); i++) { + // Vec3 stress = materialMatrix * tinfo->measure[i].B * Dm + // + materialMatrix * tinfo->measure[i].Bb * Db; + // // Von Mises stress criterion (plane stress) + // values[ tinfo->measure[i].id ] = helper::rsqrt( + // stress[0] * stress[0] - stress[0] * stress[1] + // + stress[1] * stress[1] + 3 * stress[2] * stress[2]); + // } + // f_measuredValues.endEdit(); + //} + + // Transform forces back into global frame + f[a] -= Deriv(tinfo->Rt * Vec3(F[0], F[1], 0))*f_stiffnessFactor.getValue(); + f[b] -= Deriv(tinfo->Rt * Vec3(F[2], F[3], 0))*f_stiffnessFactor.getValue(); + f[c] -= Deriv(tinfo->Rt * Vec3(F[4], F[5], 0))*f_stiffnessFactor.getValue(); + + triangleInfo.endEdit(); +} + + +// ---------------------------------------------------------------------------- +// --- Compute force F = K * u +// ---------------------------------------------------------------------------- +template +void CstFEMForceField::computeForce(Displacement &F, const Displacement& D, const Index elementIndex) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation &tinfo = triangleInf[elementIndex]; + + // Compute forces + F = tinfo.stiffnessMatrix * D; + + triangleInfo.endEdit(); +} + + +// ---------------------------------------------------------------------------- +// --- +// ---------------------------------------------------------------------------- +template +void CstFEMForceField::applyStiffness(VecDeriv& v, const VecDeriv& dx, const Index elementIndex, const double kFactor) +{ + type::vector& ti = *(triangleInfo.beginEdit()); + TriangleInformation &tinfo = ti[elementIndex]; + + // Computes displacement + Displacement D; + Vec3 x_a, x_b, x_c; + + x_a = tinfo.R * dx[tinfo.a]; + x_b = tinfo.R * dx[tinfo.b]; + x_c = tinfo.R * dx[tinfo.c]; + + D[0] = x_a[0]; + D[1] = x_a[1]; + + D[2] = x_b[0]; + D[3] = x_b[1]; + + D[4] = x_c[0]; + D[5] = x_c[1]; + + // Compute dF + Displacement dF; + dF = tinfo.stiffnessMatrix * D; + + // Transform into global frame + v[tinfo.a] -= Deriv(tinfo.Rt * Vec3(dF[0], dF[1], 0)) * kFactor * f_stiffnessFactor.getValue(); + v[tinfo.b] -= Deriv(tinfo.Rt * Vec3(dF[2], dF[3], 0)) * kFactor * f_stiffnessFactor.getValue(); + v[tinfo.c] -= Deriv(tinfo.Rt * Vec3(dF[4], dF[5], 0)) * kFactor * f_stiffnessFactor.getValue(); + + triangleInfo.endEdit(); +} + +//#define PRINT + +template +void CstFEMForceField::addKToMatrix(const core::MechanicalParams* mparams, const sofa::core::behavior::MultiMatrixAccessor* matrix) +{ + StiffnessMatrixFull Kg; + + // Build Matrix Block for this ForceField + unsigned int i, j ,n1, n2, row, column, ROW, COLUMN; + Index node1, node2; + + sofa::core::behavior::MultiMatrixAccessor::MatrixRef r = matrix->getMatrix(this->mstate); + type::vector& triangleInf = *(triangleInfo.beginEdit()); + + Real kFactor = (Real)sofa::core::mechanicalparams::kFactor(mparams); + +#ifdef PRINT + r.matrix->clear(); + std::cout << "Initial global matrix (" << r.matrix->rowSize() << "x" << r.matrix->colSize() << ")" << + " kFactor=" << kFactor << std::endl; + for (unsigned int i=0; irowSize(); i++) + { + for (unsigned int j=0; jcolSize(); j++) + { + std::cout << r.matrix->element(i,j) << ","; + //std::cout << -r.matrix->element(i,j) / kFactor << ","; + } + std::cout << std::endl; + } +#endif + + for(sofa::Index t=0 ; t != _topology->getNbTriangles() ; ++t) + { + const TriangleInformation &tinfo = triangleInf[t]; + const Triangle triangle = _topology->getTriangle(t); + + convertStiffnessMatrixToGlobalSpace(Kg, tinfo); + + // First node + for (n1=0; n1<3; n1++) + { + node1 = triangle[n1]; + + for(i=0; i<3; i++) + { + ROW = r.offset+3*node1+i; + row = 3*n1+i; + + // Second node + for (n2=0; n2<3; n2++) + { + node2 = triangle[n2]; + + for (j=0; j<3; j++) + { + COLUMN = r.offset+3*node2+j; + column = 3*n2+j; + + r.matrix->add(ROW, COLUMN, - Kg[row][column] * kFactor); + } + } + } + } + } + +#ifdef PRINT + std::cout << "Global matrix (" << r.matrix->rowSize() << "x" << r.matrix->colSize() << ")" << + " kFactor=" << kFactor << std::endl; + for (unsigned int i=0; irowSize(); i++) + { + for (unsigned int j=0; jcolSize(); j++) + { + std::cout << r.matrix->element(i,j) << ","; + //std::cout << -r.matrix->element(i,j) / kFactor << ","; + } + std::cout << std::endl; + } +#endif + + triangleInfo.endEdit(); +} + + + +template +void CstFEMForceField::convertStiffnessMatrixToGlobalSpace(StiffnessMatrixFull &Kg, const TriangleInformation &tinfo) +{ + StiffnessMatrixFull K1; + unsigned int ig, jg; + + + // Copy the stiffness matrix + const StiffnessMatrix &K = tinfo.stiffnessMatrix; + for (unsigned int bx=0; bx<3; bx++) + { + // Global row index + ig = 3*bx; + + for (unsigned int by=0; by<3; by++) + { + // Global column index + jg = 3*by; + + // X + K1[ig+0][jg+0] = K[2*bx+0][2*by+0]; // X + K1[ig+0][jg+1] = K[2*bx+0][2*by+1]; // Y + + // Y + K1[ig+1][jg+0] = K[2*bx+1][2*by+0]; // X + K1[ig+1][jg+1] = K[2*bx+1][2*by+1]; // Y + } + } + + // Extend rotation matrix and its transpose + StiffnessMatrixFull R, Rt; + + for(unsigned int i=0;i<3;++i) + { + for(unsigned int j=0;j<3;++j) + { + R[i][j] = R[i+3][j+3] = R[i+6][j+6] = tinfo.R[i][j]; + Rt[i][j] = Rt[i+3][j+3] = Rt[i+6][j+6] = tinfo.Rt[i][j]; + } + } + + // Transform stifness matrix into the global frame + Kg = Rt * K1 * R; +} + +} // namespace forcefield + +} // namespace component + +} // namespace sofa + + +#endif diff --git a/src/Shell/forcefield/TriangularShellForceField.cpp b/src/Shell/forcefield/TriangularShellForceField.cpp new file mode 100644 index 0000000..22e2b75 --- /dev/null +++ b/src/Shell/forcefield/TriangularShellForceField.cpp @@ -0,0 +1,57 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#define SOFA_COMPONENT_FORCEFIELD_TRIANGULAR_BENDING_FEM_FORCEFIELD_CPP +#include +#include +#include +#include +#include +#include + + +namespace sofa +{ + +namespace component +{ + +namespace forcefield +{ + +using namespace sofa::defaulttype; + +// Register in the Factory +int TriangularShellForceFieldClass = core::RegisterObject("Triangular finite elements with bending") +.add< TriangularShellForceField >() +; + +template class SOFA_SHELL_API TriangularShellForceField; + + +} // namespace forcefield + +} // namespace component + +} // namespace sofa diff --git a/src/Shell/forcefield/TriangularShellForceField.h b/src/Shell/forcefield/TriangularShellForceField.h new file mode 100644 index 0000000..3caafd0 --- /dev/null +++ b/src/Shell/forcefield/TriangularShellForceField.h @@ -0,0 +1,286 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#ifndef SOFA_COMPONENT_FORCEFIELD_TRIANGULAR_BENDING_FEM_FORCEFIELD_H +#define SOFA_COMPONENT_FORCEFIELD_TRIANGULAR_BENDING_FEM_FORCEFIELD_H + +#if !defined(__GNUC__) || (__GNUC__ > 3 || (_GNUC__ == 3 && __GNUC_MINOR__ > 3)) +#pragma once +#endif + + +#include +#include +#include +#include + +#include +#include + + +// Uncomment the following to use quaternions instead of matrices for +// rotations. Quaternions are slightly faster but numericaly quite unstable +// (and in my oppinion unusable). I don't recommend that! +//#define CRQUAT + + +namespace sofa +{ + +namespace component +{ + +namespace forcefield +{ + +using namespace sofa::type; +using sofa::type::vector; +using namespace sofa::core::topology; +using namespace sofa::core::behavior; + +/// This class can be overridden if needed for additional storage within template specializations. +template +class TriangularShellForceFieldInternalData +{ +public: +}; + + +template +class TriangularShellForceField : public core::behavior::ForceField +{ + public: + SOFA_CLASS(SOFA_TEMPLATE(TriangularShellForceField,DataTypes), SOFA_TEMPLATE(core::behavior::ForceField,DataTypes)); + + typedef core::behavior::ForceField Inherited; + typedef typename DataTypes::VecCoord VecCoord; + typedef typename DataTypes::VecDeriv VecDeriv; + //typedef typename DataTypes::VecReal VecReal; + + typedef typename DataTypes::Coord Coord; + typedef typename DataTypes::Deriv Deriv; + typedef typename Coord::value_type Real; + + typedef Vec<2,Real> Vec2; + typedef Vec<3,Real> Vec3; + typedef Vec<9,Real> Vec9; + + typedef sofa::type::Quat Quat; + + typedef Data DataVecCoord; + typedef Data DataVecDeriv; + + typedef sofa::defaulttype::Vec3Types::VecCoord VecCoordHigh; + + typedef sofa::Index Index; + typedef sofa::core::topology::BaseMeshTopology::Triangle Triangle; + typedef sofa::core::topology::BaseMeshTopology::SeqTriangles SeqTriangles; + + class TriangleInformation; + + protected: + + typedef Vec<9, Real> Displacement; // the displacement vector + typedef Mat<3, 3, Real> MaterialStiffness; // the matrix of material stiffness + typedef Mat<3, 9, Real> StrainDisplacement; // the strain-displacement matrix + typedef Mat<3, 3, Real> Transformation; // matrix for rigid transformations like rotations + typedef Mat<9, 9, Real> StiffnessMatrix; // element stiffness matrix + typedef Mat<18, 18, Real> StiffnessMatrixFull; // stiffness matrix for shell (= bending plate + membrane) + + typedef void (TriangularShellForceField::*compstiff)(StiffnessMatrix &K, TriangleInformation &tinfo); + typedef type::fixed_array AndesBeta; + + sofa::core::topology::BaseMeshTopology* _topology; + +public: + + class TriangleInformation + { + public: + + // Indices of each vertex + Index a, b, c; + + // Rest position in local (in-plane) coordinates + type::fixed_array restPositions; +#ifdef CRQUAT + type::fixed_array restOrientationsInv; +#else + type::fixed_array restOrientationsInv; +#endif + + // Deformed position in local (in-plane) coordinates + type::fixed_array deformedPositions; + + // Frame rotation as matrix and quaternion + Transformation R, Rt; +#ifdef CRQUAT + Quat Q; +#endif + + // The strain-displacement matrices at Gauss points + StrainDisplacement strainDisplacementMatrixMembrane[4]; + StrainDisplacement strainDisplacementMatrixBending[4]; + + // Stiffness matrix + StiffnessMatrix stiffnessMatrixMembrane; + StiffnessMatrix stiffnessMatrixBending; + + // Measure stress or strain + struct MeasurePoint { + Vec3 point; // Barycentric coordinates + StrainDisplacement B; // Strain-displacement Matrix + StrainDisplacement Bb; // Strain-displacement Matrix bending + Index id; // Index into the result array + }; + type::vector measure; + + + // The following are in rest shape + // - element area + Real area; + // - directional vectors: 1-2, 2-3, 3-1 + type::fixed_array d; + // - squared lengths of 'd' + type::fixed_array l2; + + /// Output stream + inline friend std::ostream& operator<< ( std::ostream& os, const TriangleInformation& /*ti*/ ) + { + return os; + } + + /// Input stream + inline friend std::istream& operator>> ( std::istream& in, TriangleInformation& /*ti*/ ) + { + return in; + } + }; + + class TRQSTriangleHandler : public TopologyDataHandler > + { + public: + TRQSTriangleHandler(TriangularShellForceField* _ff, TriangleData >* _data) : TopologyDataHandler >(_data), ff(_ff) {} + + void applyCreateFunction(unsigned int triangleIndex, TriangleInformation& , + const Triangle & t, + const sofa::type::vector< unsigned int > &, + const sofa::type::vector< double > &, const VecCoord& x0); + + protected: + TriangularShellForceField* ff; + }; + + TriangularShellForceField(); + + virtual ~TriangularShellForceField(); + void init() override; + void reinit() override; + void addForce(const sofa::core::MechanicalParams* /*mparams*/, DataVecDeriv& dataF, const DataVecCoord& dataX, const DataVecDeriv& /*dataV*/ ) override ; + void addDForce(const sofa::core::MechanicalParams* /*mparams*/, DataVecDeriv& datadF, const DataVecDeriv& datadX ) override ; + void addKToMatrix(const core::MechanicalParams* mparams, const sofa::core::behavior::MultiMatrixAccessor* matrix) override; + void draw(const core::visual::VisualParams* vparams) override; + + SReal getPotentialEnergy(const sofa::core::MechanicalParams* /*mparams*/, const DataVecCoord& /*x*/) const override { return 0; } + + sofa::core::topology::BaseMeshTopology* getTopology() {return _topology;} + + Data d_poisson; + Data d_young; + Data d_thickness; + Data d_membraneElement; + Data d_bendingElement; + Data d_corotated; + Data d_measure; + Data > d_measuredValues; + Data d_isShellveryThin; + Data d_use_rest_position; + Data d_arrow_radius; + + TRQSTriangleHandler* triangleHandler; + +protected : + + // Selected elements + compstiff csMembrane; + compstiff csBending; + + /// Material stiffness matrix + MaterialStiffness materialMatrix, materialMatrixMembrane, materialMatrixBending; + TriangleData< sofa::type::vector > triangleInfo; + + // What to measure + bool bMeasureStrain; + bool bMeasureStress; + + void initTriangle(const int i, const Index&a, const Index&b, const Index&c, const VecCoord& x0); + + void computeRotation(Transformation& R, const VecCoord &x, const Index &a, const Index &b, const Index &c); + void computeRotation(Transformation& R, const type::fixed_array &x); + void computeMaterialStiffness(); + + void computeDisplacement(Displacement &Dm, Displacement &Db, const VecCoord &x, const Index elementIndex); + void accumulateForce(VecDeriv& f, const VecCoord & p, const Index elementIndex); + void computeStiffnessMatrixMembrane(StiffnessMatrix &K, TriangleInformation &tinfo); + void computeStiffnessMatrixBending(StiffnessMatrix &K, TriangleInformation &tinfo); + void computeForce(Displacement &Fm, const Displacement& Dm, Displacement &Fb, const Displacement& Db,const Index elementIndex); + virtual void applyStiffness(VecDeriv& f, const VecDeriv& dx, const Index elementIndex, const double kFactor); + + void convertStiffnessMatrixToGlobalSpace(StiffnessMatrixFull &K_gs, const TriangleInformation &tinfo); + + // Membrane Elements + void computeStiffnessMatrixCST(StiffnessMatrix &K, TriangleInformation &tinfo); + void computeStiffnessMatrixAll3I(StiffnessMatrix &K, TriangleInformation &tinfo); + void computeStiffnessMatrixAll3M(StiffnessMatrix &K, TriangleInformation &tinfo); + void computeStiffnessMatrixAllLS(StiffnessMatrix &K, TriangleInformation &tinfo); + void computeStiffnessMatrixLSTRet(StiffnessMatrix &K, TriangleInformation &tinfo); + void computeStiffnessMatrixAndesOpt(StiffnessMatrix &K, TriangleInformation &tinfo); + + // Bending plate elements + void computeStiffnessMatrixDKT(StiffnessMatrix &K, TriangleInformation &tinfo); + + // Helper functions for the elements + void andesTemplate(StiffnessMatrix &K, const TriangleInformation &tinfo, const Real alpha, const AndesBeta &beta); + void dktSD(StrainDisplacement &B, const TriangleInformation &tinfo, const Real xi, const Real eta); + +}; + + +#if defined(WIN32) && !defined(SOFA_COMPONENT_FORCEFIELD_TRIANGULAR_BENDING_FEM_FORCEFIELD_CPP) +#pragma warning(disable : 4231) +#ifndef SOFA_FLOAT +extern template class TriangularShellForceField; +#endif +#ifndef SOFA_DOUBLE +extern template class TriangularShellForceField; +#endif +#endif + +} // namespace forcefield + +} // namespace component + +} // namespace sofa + +#endif diff --git a/src/Shell/forcefield/TriangularShellForceField.inl b/src/Shell/forcefield/TriangularShellForceField.inl new file mode 100644 index 0000000..fe379f7 --- /dev/null +++ b/src/Shell/forcefield/TriangularShellForceField.inl @@ -0,0 +1,1318 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#ifndef SOFA_COMPONENT_FORCEFIELD_TRIANGULAR_BENDING_FEM_FORCEFIELD_INL +#define SOFA_COMPONENT_FORCEFIELD_TRIANGULAR_BENDING_FEM_FORCEFIELD_INL + +#include +#include +#include +#include +#include +#include +#include +#include +#include // for reading the file +#include //for debugging +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + + +namespace sofa +{ +namespace component +{ +namespace forcefield +{ +using namespace sofa::type; +using namespace sofa::core::topology; + +// -------------------------------------------------------------------------------------- +// --- Topology Creation/Destruction functions +// -------------------------------------------------------------------------------------- +template< class DataTypes> +void TriangularShellForceField::TRQSTriangleHandler::applyCreateFunction(unsigned int triangleIndex, TriangleInformation &, const Triangle &t, const sofa::type::vector &, const sofa::type::vector &, const VecCoord& x0) +{ + if (ff) + ff->initTriangle(triangleIndex, t[0], t[1], t[2], x0); +} + + +// -------------------------------------------------------------------------------------- +// --- Constructor +// -------------------------------------------------------------------------------------- +template +TriangularShellForceField::TriangularShellForceField() + : d_poisson(initData(&d_poisson,(Real)0.45,"poissonRatio","Poisson ratio in Hooke's law")) + , d_young(initData(&d_young,(Real)3000.,"youngModulus","Young modulus in Hooke's law")) + , d_thickness(initData(&d_thickness,(Real)0.1,"thickness","Thickness of the plates")) + , d_membraneElement(initData(&d_membraneElement, "membraneElement", "The membrane element to use")) + , d_bendingElement(initData(&d_bendingElement, "bendingElement", "The bending plate element to use")) + , d_corotated(initData(&d_corotated, true, "corotated", "Compute forces in corotational frame")) + , d_measure(initData(&d_measure, "measure", "Compute the strain or stress")) + , d_measuredValues(initData(&d_measuredValues, "measuredValues", "Measured values for stress or strain")) + , d_isShellveryThin(initData(&d_isShellveryThin, false, "isShellveryThin", "This bool is to adapt " + "computation in case we are using verry tiny(thickness) shell element")) + , d_use_rest_position(initData(&d_use_rest_position, true, "use_rest_position", "Use the rest position inteat of using postion to update the restposition")) + , triangleInfo(initData(&triangleInfo, "triangleInfo", "Internal triangle data")) + , d_arrow_radius(initData(&d_arrow_radius, (Real)0.1, "arrow_radius", "the arrow radius")) + +{ + d_membraneElement.beginEdit()->setNames( { + "None", // No membrane element + "CST", // Constant strain triangle + // ANDES templates + "ALL-3I", // Allman 88 element integrated by 3-point interior rule + "ALL-3M", // Allman 88 element integrated by 3-midpoint rule + "ALL-LS", // Allman 88 element, least-square strain fit + "LST-Ret", // Retrofitted LST with α_b=1⁄4 + "ANDES-OPT" // Optimal ANDES element + }); + d_membraneElement.beginEdit()->setSelectedItem("ANDES-OPT"); + d_membraneElement.endEdit(); + + d_bendingElement.beginEdit()->setNames( { + "None", // No bending element + "DKT" // Discrete Kirchhoff Triangle + }); + d_bendingElement.beginEdit()->setSelectedItem("DKT"); + d_bendingElement.endEdit(); + + d_measure.beginEdit()->setNames( { + "None", // Draw nothing + "Strain (norm)", // L_2 norm of strain in x and y directions + "Von Mises stress" // Von Mises stress criterion + }); + d_measure.beginEdit()->setSelectedItem("None"); + d_measure.endEdit(); + + triangleHandler = new TRQSTriangleHandler(this, &triangleInfo); +} + + +// -------------------------------------------------------------------------------------- +// --- Destructor +// -------------------------------------------------------------------------------------- +template +TriangularShellForceField::~TriangularShellForceField() +{ + if(triangleHandler) delete triangleHandler; +} + +// -------------------------------------------------------------------------------------- +// --- Initialization stage +// -------------------------------------------------------------------------------------- +template +void TriangularShellForceField::init() +{ + this->Inherited::init(); + + _topology = this->getContext()->getMeshTopology(); + + if (_topology->getNbTriangles()==0) + { + msg_warning() << "TriangularShellForceField: object must have a Triangular Set Topology."; + return; + } + + // Create specific handler for TriangleData + triangleInfo.createTopologyHandler(_topology); + + reinit(); +} + + +// -------------------------------------------------------------------------------------- +// --- Re-initialization (called when we change a parameter through the GUI) +// -------------------------------------------------------------------------------------- +template void TriangularShellForceField::reinit() +{ + type::vector& ti = *(triangleInfo.beginEdit()); + + // Prepare material matrices + computeMaterialStiffness(); + + // Decode the selected elements to use + if (d_membraneElement.getValue().getSelectedItem() == "None") { + csMembrane = NULL; + for (unsigned int t=0; t::computeStiffnessMatrixCST; + } else if (d_membraneElement.getValue().getSelectedItem() == "ALL-3I") { + csMembrane = &TriangularShellForceField::computeStiffnessMatrixAll3I; + } else if (d_membraneElement.getValue().getSelectedItem() == "ALL-3M") { + csMembrane = &TriangularShellForceField::computeStiffnessMatrixAll3M; + } else if (d_membraneElement.getValue().getSelectedItem() == "ALL-LS") { + csMembrane = &TriangularShellForceField::computeStiffnessMatrixAllLS; + } else if (d_membraneElement.getValue().getSelectedItem() == "LST-Ret") { + csMembrane = &TriangularShellForceField::computeStiffnessMatrixLSTRet; + } else if (d_membraneElement.getValue().getSelectedItem() == "ANDES-OPT") { + csMembrane = &TriangularShellForceField::computeStiffnessMatrixAndesOpt; + } else { + msg_warning() << "Invalid membrane element '" << d_membraneElement.getValue().getSelectedItem() << "'" ; + return; + } + + if (d_bendingElement.getValue().getSelectedItem() == "None") { + csBending = NULL; + for (unsigned int t=0; t::computeStiffnessMatrixDKT; + } else { + msg_warning() << "Invalid bending plate element '" << d_bendingElement.getValue().getSelectedItem() << "'" ; + return; + } + + // What to compute? + if (d_measure.getValue().getSelectedItem() == "None") { + bMeasureStrain = false; bMeasureStress = false; + } else if (d_measure.getValue().getSelectedItem() == "Strain (norm)") { + bMeasureStrain = true; bMeasureStress = false; + } else if (d_measure.getValue().getSelectedItem() == "Von Mises stress") { + bMeasureStrain = false; bMeasureStress = true; + } else { + msg_warning() << "Invalid value for measure'" << d_measure.getValue().getSelectedItem() << "'" ; + return; + } + + if (bMeasureStrain || bMeasureStress) + { + d_measuredValues.beginEdit()->resize(_topology->getNbPoints()); + d_measuredValues.endEdit(); + } + + /// Prepare to store info in the triangle array + ti.resize(_topology->getNbTriangles()); + + const auto use_rest_position = d_use_rest_position.getValue(); + + if (use_rest_position) + { + const VecCoord& x0 =this->mstate->read(sofa::core::vec_id::read_access::restPosition)->getValue(); + for (sofa::Index i=0; i<_topology->getNbTriangles(); ++i) + triangleHandler->applyCreateFunction(i, ti[i], _topology->getTriangle(i), + (const sofa::type::vector< unsigned int >)0, (const sofa::type::vector< double >)0, x0); + } + else + { + const VecCoord& x0 =this->mstate->read(sofa::core::vec_id::read_access::position)->getValue(); + for (sofa::Index i=0; i<_topology->getNbTriangles(); ++i) + triangleHandler->applyCreateFunction(i, ti[i], _topology->getTriangle(i), + (const sofa::type::vector< unsigned int >)0, (const sofa::type::vector< double >)0, x0); + } + triangleInfo.endEdit(); +} + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +void TriangularShellForceField::addForce(const sofa::core::MechanicalParams* /*mparams*/, DataVecDeriv& dataF, const DataVecCoord& dataX, const DataVecDeriv& /*dataV*/ ) +{ + VecDeriv& f = *(dataF.beginEdit()); + const VecCoord& p = dataX.getValue() ; + + //dmsg_info() << "--addForce" ; + // sofa::helper::system::thread::ctime_t start, stop; + // sofa::helper::system::thread::CTime timer; + // + // start = timer.getTime(); + + int nbTriangles=_topology->getNbTriangles(); + f.resize(p.size()); + + for (int i=0; igetNbTriangles(); + df.resize(dp.size()); + + for (int i=0; irowSize() << "x" << r.matrix->colSize() << ")" << + " kFactor=" << kFactor ; + for (unsigned int i=0; irowSize(); i++) + { + for (unsigned int j=0; jcolSize(); j++) + { + dmsg_info() << r.matrix->element(i,j) << ","; + //dmsg_info() << -r.matrix->element(i,j) / kFactor << ","; + } + dmsg_info() ; + } +#endif + // XXX: Matrix not necessarily empty! + + for(sofa::Index t=0 ; t != _topology->getNbTriangles() ; ++t) + { + const TriangleInformation &tinfo = triangleInf[t]; + const Triangle triangle = _topology->getTriangle(t); + + convertStiffnessMatrixToGlobalSpace(K_gs, tinfo); + + // find index of node 1 + for (n1=0; n1<3; n1++) + { + node1 = triangle[n1]; + + for(i=0; i<6; i++) + { + ROW = r.offset+6*node1+i; + row = 6*n1+i; + // find index of node 2 + for (n2=0; n2<3; n2++) + { + node2 = triangle[n2]; + + for (j=0; j<6; j++) + { + COLUMN = r.offset+6*node2+j; + column = 6*n2+j; + r.matrix->add(ROW, COLUMN, - K_gs[row][column] * kFactor); + //r.matrix->add(ROW, COLUMN, K_gs[row][column]); + } + } + } + } + } + +#ifdef PRINT + dmsg_info() << "Global matrix (" << r.matrix->rowSize() << "x" << r.matrix->colSize() << ")" << + " kFactor=" << kFactor ; + for (unsigned int i=0; irowSize(); i++) + { + for (unsigned int j=0; jcolSize(); j++) + { + dmsg_info() << r.matrix->element(i,j) << ","; + //dmsg_info() << -r.matrix->element(i,j) / kFactor << ","; + } + dmsg_info() ; + } +#endif + + triangleInfo.endEdit(); +} + + +// -------------------------------------------------------------------------------------- +// --- Store the initial position of the nodes +// -------------------------------------------------------------------------------------- +template +void TriangularShellForceField::initTriangle(const int i, const Index&a, const Index&b, const Index&c, const VecCoord& x0) +{ + type::vector& ti = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &ti[i]; + + // Store indices of each vertex + tinfo->a = a; + tinfo->b = b; + tinfo->c = c; + + tinfo->measure.resize(3); + tinfo->measure[0].id = a; tinfo->measure[0].point = Vec3(0,0,0); + tinfo->measure[1].id = b; tinfo->measure[1].point = Vec3(1,0,0); + tinfo->measure[2].id = c; tinfo->measure[2].point = Vec3(0,1,0); + + // Rotation from global to local frame + Transformation R0; + + // Rest positions expressed in the local frame + tinfo->restPositions[0] = x0[a].getCenter(); + tinfo->restPositions[1] = x0[b].getCenter(); + tinfo->restPositions[2] = x0[c].getCenter(); + + if (d_corotated.getValue()) { + // Center the element + Vec3 center = (tinfo->restPositions[0] + tinfo->restPositions[1] + + tinfo->restPositions[2])/3; + + tinfo->restPositions[0] -= center; + tinfo->restPositions[1] -= center; + tinfo->restPositions[2] -= center; + } + + computeRotation(R0, tinfo->restPositions); + tinfo->R = R0; + tinfo->Rt.transpose(R0); +#ifdef CRQUAT + tinfo->Q.fromMatrix(tinfo->R); +#endif + + tinfo->restPositions[0] = R0 * tinfo->restPositions[0]; + tinfo->restPositions[1] = R0 * tinfo->restPositions[1]; + tinfo->restPositions[2] = R0 * tinfo->restPositions[2]; + +// Rest orientations -- inverted (!) +#ifdef CRQUAT + tinfo->restOrientationsInv[0] = (tinfo->Q * x0[a].getOrientation()).inverse(); + tinfo->restOrientationsInv[1] = (tinfo->Q * x0[b].getOrientation()).inverse(); + tinfo->restOrientationsInv[2] = (tinfo->Q * x0[c].getOrientation()).inverse(); +#else + x0[a].getOrientation().toMatrix(tinfo->restOrientationsInv[0]); + x0[b].getOrientation().toMatrix(tinfo->restOrientationsInv[1]); + x0[c].getOrientation().toMatrix(tinfo->restOrientationsInv[2]); + + tinfo->restOrientationsInv[0].transpose( tinfo->R * tinfo->restOrientationsInv[0] ); + tinfo->restOrientationsInv[1].transpose( tinfo->R * tinfo->restOrientationsInv[1] ); + tinfo->restOrientationsInv[2].transpose( tinfo->R * tinfo->restOrientationsInv[2] ); +#endif + + // Do some precomputations + // - directional vectors + tinfo->d[0] = tinfo->restPositions[0] - tinfo->restPositions[1]; + tinfo->d[1] = tinfo->restPositions[1] - tinfo->restPositions[2]; + tinfo->d[2] = tinfo->restPositions[2] - tinfo->restPositions[0]; + + // - squared lengths + tinfo->l2[0] = tinfo->d[0][0]*tinfo->d[0][0] + tinfo->d[0][1]*tinfo->d[0][1]; + tinfo->l2[1] = tinfo->d[1][0]*tinfo->d[1][0] + tinfo->d[1][1]*tinfo->d[1][1]; + tinfo->l2[2] = tinfo->d[2][0]*tinfo->d[2][0] + tinfo->d[2][1]*tinfo->d[2][1]; + + // - triangle area + tinfo->area = helper::rabs(tinfo->d[2][0]*(-tinfo->d[0][1]) - (-tinfo->d[0][0])*tinfo->d[2][1])/2; + + // Compute stiffness matrix for membrane element + computeStiffnessMatrixMembrane(tinfo->stiffnessMatrixMembrane, *tinfo); + //dmsg_info() << "Km^e=" << tinfo->stiffnessMatrixMembrane ; + + // Compute stiffness matrix for bending plate elemnt + computeStiffnessMatrixBending(tinfo->stiffnessMatrixBending, *tinfo); + //dmsg_info() << "Kb^e=" << tinfo->stiffnessMatrixBending ; + + + triangleInfo.endEdit(); +} + + +// -------------------------------------------------------------------------------------- +// Computes the rotation from global frame to local triangle frame +// -------------------------------------------------------------------------------------- +template +void TriangularShellForceField::computeRotation(Transformation& R, const VecCoord &x, const Index &a, const Index &b, const Index &c) +{ + + if (!d_corotated.getValue()) { + // Return identity matrix + R = Transformation(Vec3(1,0,0), Vec3(0,1,0), Vec3(0,0,1)); + return; + } + + // First vector on first edge + // Second vector in the plane of the two first edges + // Third vector orthogonal to first and second + + Vec3 edgex = x[b].getCenter() - x[a].getCenter(); + + Vec3 edgey = x[c].getCenter() - x[a].getCenter(); + + Vec3 edgez = cross(edgex, edgey); + + edgey = cross(edgez, edgex); + + edgex.normalize(); + edgey.normalize(); + edgez.normalize(); + + R = Transformation(edgex, edgey, edgez); + //Qframe.fromMatrix(Transformation(edgex, edgey, edgez)); +} + +template +void TriangularShellForceField::computeRotation(Transformation& R, const type::fixed_array &x) +{ + if (!d_corotated.getValue()) { + // Return identity matrix + R = Transformation(Vec3(1,0,0), Vec3(0,1,0), Vec3(0,0,1)); + return; + } + + // First vector on first edge + // Second vector in the plane of the two first edges + // Third vector orthogonal to first and second + + Vec3 edge_x = x[1] - x[0]; + Vec3 edge_y = x[2] - x[0]; + + Vec3 edge_z = cross(edge_x, edge_y); + + edge_y = cross(edge_z, edge_x); + + edge_x.normalize(); + edge_y.normalize(); + edge_z.normalize(); + + R = Transformation(edge_x, edge_y, edge_z); + //Qframe.fromMatrix(Transformation(edge_x, edge_y, edge_z)); +} + + +// -------------------------------------------------------------------------------------- +// --- Compute material stiffness +// -------------------------------------------------------------------------------------- +template +void TriangularShellForceField::computeMaterialStiffness() +{ + Real E = d_young.getValue(), + nu = d_poisson.getValue(), + t = d_thickness.getValue(); + + materialMatrix[0][0] = 1.0; + materialMatrix[0][1] = nu; + materialMatrix[0][2] = 0; + materialMatrix[1][0] = nu; + materialMatrix[1][1] = 1.0; + materialMatrix[1][2] = 0; + materialMatrix[2][0] = 0; + materialMatrix[2][1] = 0; + materialMatrix[2][2] = (1 - nu)/2.0; + + materialMatrix *= E / (1.0 - nu * nu); + + materialMatrixMembrane = materialMatrix; + materialMatrixBending = materialMatrix; + + // Integrate through the shell thickness + materialMatrixMembrane *= t; + if (d_isShellveryThin.getValue()) + materialMatrixBending *= t*t; + else + materialMatrixBending *= t*t*t / 12; +} + + +// ------------------------------------------------------------------------------------------------------------- +// --- Compute displacement vector D as the difference between current position and initial position +// ------------------------------------------------------------------------------------------------------------- +template +void TriangularShellForceField::computeDisplacement(Displacement &Dm, Displacement &Db, const VecCoord &x, const Index elementIndex) +{ + type::vector& ti = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &ti[elementIndex]; + + Index a = tinfo->a; + Index b = tinfo->b; + Index c = tinfo->c; + + // Compute local (in-plane) positions + tinfo->deformedPositions[0] = x[a].getCenter(); + tinfo->deformedPositions[1] = x[b].getCenter(); + tinfo->deformedPositions[2] = x[c].getCenter(); + + if (d_corotated.getValue()) { + Vec3 center = (tinfo->deformedPositions[0] + tinfo->deformedPositions[1] + + tinfo->deformedPositions[2])/3; + + tinfo->deformedPositions[0] -= center; + tinfo->deformedPositions[1] -= center; + tinfo->deformedPositions[2] -= center; + } + + // Compute rotation to local (in-plane) frame + computeRotation(tinfo->R, tinfo->deformedPositions); + tinfo->Rt.transpose(tinfo->R); +#ifdef CRQUAT + tinfo->Q.fromMatrix(tinfo->R); +#endif + + // Compute local (in-plane) positions + tinfo->deformedPositions[0] = tinfo->R * tinfo->deformedPositions[0]; + tinfo->deformedPositions[1] = tinfo->R * tinfo->deformedPositions[1]; + tinfo->deformedPositions[2] = tinfo->R * tinfo->deformedPositions[2]; + + // Displacements + Vec3 uA = tinfo->deformedPositions[0] - tinfo->restPositions[0]; + Vec3 uB = tinfo->deformedPositions[1] - tinfo->restPositions[1]; + Vec3 uC = tinfo->deformedPositions[2] - tinfo->restPositions[2]; + +// Rotations +#ifdef CRQUAT + Quat qA = (tinfo->Q * x[a].getOrientation()) * tinfo->restOrientationsInv[0]; + Quat qB = (tinfo->Q * x[b].getOrientation()) * tinfo->restOrientationsInv[1]; + Quat qC = (tinfo->Q * x[c].getOrientation()) * tinfo->restOrientationsInv[2]; +#else + Transformation tmpA, tmpB, tmpC; + x[a].getOrientation().toMatrix(tmpA); + x[b].getOrientation().toMatrix(tmpB); + x[c].getOrientation().toMatrix(tmpC); + + Quat qA; qA.fromMatrix( tinfo->R * tmpA * tinfo->restOrientationsInv[0] ); + Quat qB; qB.fromMatrix( tinfo->R * tmpB * tinfo->restOrientationsInv[1] ); + Quat qC; qC.fromMatrix( tinfo->R * tmpC * tinfo->restOrientationsInv[2] ); + // TODO: can we do this without the quaternions? +#endif + Vec3 rA = qA.toEulerVector(); + Vec3 rB = qB.toEulerVector(); + Vec3 rC = qC.toEulerVector(); + //dmsg_info() << "Θ: " << rA << " | " << rB << " | " << rC ; + + // Membrane + Dm[0] = uA[0]; + Dm[1] = uA[1]; + Dm[2] = rA[2]; + + Dm[3] = uB[0]; + Dm[4] = uB[1]; + Dm[5] = rB[2]; + + Dm[6] = uC[0]; + Dm[7] = uC[1]; + Dm[8] = rC[2]; + + // Bending plate + Db[0] = uA[2]; + Db[1] = rA[0]; + Db[2] = rA[1]; + + Db[3] = uB[2]; + Db[4] = rB[0]; + Db[5] = rB[1]; + + Db[6] = uC[2]; + Db[7] = rC[0]; + Db[8] = rC[1]; + + triangleInfo.endEdit(); +} + + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +void TriangularShellForceField::accumulateForce(VecDeriv &f, const VecCoord &x, const Index elementIndex) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &triangleInf[elementIndex]; + + // Get the indices of the 3 vertices for the current triangle + const Index& a = tinfo->a; + const Index& b = tinfo->b; + const Index& c = tinfo->c; + + // Compute in-plane displacements + Displacement Dm, Db; + computeDisplacement(Dm, Db, x, elementIndex); + + // Compute the membrane and bending plate forces on this element + Displacement Fm, Fb; + computeForce(Fm, Dm, Fb, Db, elementIndex); + + if (this->f_printLog.getValue()) { + dmsg_info() << "E: " << elementIndex << "\tu: " << Dm << "\tf: " << Fm << "\n"; + dmsg_info() << "E: " << elementIndex << "\tuB: " << Db << "\tfB: " << Fb << "\n"; + dmsg_info() << " xg [ " << a << "/" << b << "/" << c << " - " + << x[a] << ", " << x[b] << ", " << x[c] << "\n"; + dmsg_info() << " xl [ " << tinfo->deformedPositions[0] << ", " << tinfo->deformedPositions[1] << ", " << tinfo->deformedPositions[2] << "\n"; + dmsg_info() << " fg: " << + tinfo->Rt * Vec3(Fm[0], Fm[1], Fb[0]) << " " << tinfo->R * Vec3(Fb[1], Fb[2], Fm[2]) << " | " << + tinfo->Rt * Vec3(Fm[3], Fm[4], Fb[3]) << " " << tinfo->R * Vec3(Fb[4], Fb[5], Fm[5]) << " | " << + tinfo->Rt * Vec3(Fm[6], Fm[7], Fb[6]) << " " << tinfo->R * Vec3(Fb[7], Fb[8], Fm[8]) ; + + } + + // Compute the measure (stress/strain) + if (bMeasureStrain) { + type::vector &values = *d_measuredValues.beginEdit(); + for (unsigned int i=0; i< tinfo->measure.size(); i++) { + Vec3 strain = tinfo->measure[i].B * Dm + tinfo->measure[i].Bb * Db; + // Norm from strain in x and y + // NOTE: Shear strain is not included + values[ tinfo->measure[i].id ] = helper::rsqrt( + strain[0] * strain[0] + strain[1] * strain[1]); + } + d_measuredValues.endEdit(); + } else if (bMeasureStress) { + type::vector &values = *d_measuredValues.beginEdit(); + for (unsigned int i=0; i< tinfo->measure.size(); i++) { + Vec3 stress = materialMatrix * tinfo->measure[i].B * Dm + + materialMatrix * tinfo->measure[i].Bb * Db; + // Von Mises stress criterion (plane stress) + values[ tinfo->measure[i].id ] = helper::rsqrt( + stress[0] * stress[0] - stress[0] * stress[1] + + stress[1] * stress[1] + 3 * stress[2] * stress[2]); + } + d_measuredValues.endEdit(); + } + + // Transform forces back into global frame + getVCenter(f[a]) -= tinfo->Rt * Vec3(Fm[0], Fm[1], Fb[0]); + getVCenter(f[b]) -= tinfo->Rt * Vec3(Fm[3], Fm[4], Fb[3]); + getVCenter(f[c]) -= tinfo->Rt * Vec3(Fm[6], Fm[7], Fb[6]); + + getVOrientation(f[a]) -= tinfo->Rt * Vec3(Fb[1], Fb[2], Fm[2]); + getVOrientation(f[b]) -= tinfo->Rt * Vec3(Fb[4], Fb[5], Fm[5]); + getVOrientation(f[c]) -= tinfo->Rt * Vec3(Fb[7], Fb[8], Fm[8]); + + triangleInfo.endEdit(); +} + + +// ---------------------------------------------------------------------------------------------------------------------- +// --- Compute the stiffness matrix for membrane element +// ---------------------------------------------------------------------------------------------------------------------- +template +void TriangularShellForceField::computeStiffnessMatrixMembrane(StiffnessMatrix &K, TriangleInformation &tinfo) +{ + if (csMembrane == NULL) + return; + + (this->*csMembrane)(K, tinfo); + + if (this->f_printLog.getValue()) + dmsg_info() << "Km = " << K ; +} + + +// ---------------------------------------------------------------------------------------------------------------------- +// --- Compute the stiffness matrix for bending plate element +// ---------------------------------------------------------------------------------------------------------------------- +template +void TriangularShellForceField::computeStiffnessMatrixBending(StiffnessMatrix &K, TriangleInformation &tinfo) +{ + if (csBending == NULL) + return; + + (this->*csBending)(K, tinfo); + + if (this->f_printLog.getValue()) + dmsg_info() << "Kb = " << K ; +} + +// -------------------------------------------------------------------------------------- +// --- Compute force F = K * u +// -------------------------------------------------------------------------------------- +template +void TriangularShellForceField::computeForce(Displacement &Fm, const Displacement& Dm, Displacement &Fb, const Displacement& Db,const Index elementIndex) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation &tinfo = triangleInf[elementIndex]; + + // Compute forces + Fm = tinfo.stiffnessMatrixMembrane * Dm; + Fb = tinfo.stiffnessMatrixBending * Db; + + triangleInfo.endEdit(); +} + + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +void TriangularShellForceField::applyStiffness(VecDeriv& v, const VecDeriv& dx, const Index elementIndex, const double kFactor) +{ + type::vector& ti = *(triangleInfo.beginEdit()); + TriangleInformation &tinfo = ti[elementIndex]; + + // Get the indices of the 3 vertices for the current triangle + const Index& a = tinfo.a; + const Index& b = tinfo.b; + const Index& c = tinfo.c; + + // Computes displacements + Displacement Dm, Db; + Vec3 x_a, x_b, x_c; + Vec3 r_a, r_b, r_c; + + x_a = tinfo.R * getVCenter(dx[a]); + r_a = tinfo.R * getVOrientation(dx[a]); + + x_b = tinfo.R * getVCenter(dx[b]); + r_b = tinfo.R * getVOrientation(dx[b]); + + x_c = tinfo.R * getVCenter(dx[c]); + r_c = tinfo.R * getVOrientation(dx[c]); + + Dm[0] = x_a[0]; + Dm[1] = x_a[1]; + Dm[2] = r_a[2]; + + Dm[3] = x_b[0]; + Dm[4] = x_b[1]; + Dm[5] = r_b[2]; + + Dm[6] = x_c[0]; + Dm[7] = x_c[1]; + Dm[8] = r_c[2]; + + Db[0] = x_a[2]; + Db[1] = r_a[0]; + Db[2] = r_a[1]; + + Db[3] = x_b[2]; + Db[4] = r_b[0]; + Db[5] = r_b[1]; + + Db[6] = x_c[2]; + Db[7] = r_c[0]; + Db[8] = r_c[1]; + + // Compute dF + Displacement dFm, dFb; + dFm = tinfo.stiffnessMatrixMembrane * Dm; + dFb = tinfo.stiffnessMatrixBending * Db; + + if (this->f_printLog.getValue()) { + dmsg_info() << "E: " << elementIndex << "\tdu: " << Dm << "\tdf: " << dFm << "\n"; + dmsg_info() << "E: " << elementIndex << "\tduB: " << Db << "\tdfB: " << dFb << "\n"; + dmsg_info() << " dfg: " << + tinfo.Rt * Vec3(dFm[0], dFm[1], dFb[0]) * kFactor << " " << tinfo.Rt * Vec3(dFb[1], dFb[2], dFm[2]) * kFactor << " | " << + tinfo.Rt * Vec3(dFm[3], dFm[4], dFb[3]) * kFactor << " " << tinfo.Rt * Vec3(dFb[4], dFb[5], dFm[5]) * kFactor << " | " << + tinfo.Rt * Vec3(dFm[6], dFm[7], dFb[6]) * kFactor << " " << tinfo.Rt * Vec3(dFb[7], dFb[8], dFm[8]) * kFactor ; + } + + // Transform into global frame + getVCenter(v[a]) -= tinfo.Rt * Vec3(dFm[0], dFm[1], dFb[0]) * kFactor; + getVCenter(v[b]) -= tinfo.Rt * Vec3(dFm[3], dFm[4], dFb[3]) * kFactor; + getVCenter(v[c]) -= tinfo.Rt * Vec3(dFm[6], dFm[7], dFb[6]) * kFactor; + + getVOrientation(v[a]) -= tinfo.Rt * Vec3(dFb[1], dFb[2], dFm[2]) * kFactor; + getVOrientation(v[b]) -= tinfo.Rt * Vec3(dFb[4], dFb[5], dFm[5]) * kFactor; + getVOrientation(v[c]) -= tinfo.Rt * Vec3(dFb[7], dFb[8], dFm[8]) * kFactor; + + triangleInfo.endEdit(); +} + + +template +void TriangularShellForceField::convertStiffnessMatrixToGlobalSpace(StiffnessMatrixFull &K_gs, const TriangleInformation &tinfo) +{ + // Firstly, add all degrees of freedom (we add the unused translation in z) + StiffnessMatrixFull K_18x18; + K_18x18.clear(); + unsigned int ig, jg; + + + // Copy the stiffness matrix into 18x18 matrix (the new index of each block in global matrix is a combination of 0, 6 and 12 in indices) + const StiffnessMatrix &K = tinfo.stiffnessMatrixMembrane; + for (unsigned int bx=0; bx<3; bx++) + { + // Global row index + ig = 6*bx; + + for (unsigned int by=0; by<3; by++) + { + // Global column index + jg = 6*by; + + // linear X + K_18x18[ig+0][jg+0] = K[3*bx+0][3*by+0]; // linear X + K_18x18[ig+0][jg+1] = K[3*bx+0][3*by+1]; // linear Y + K_18x18[ig+0][jg+5] = K[3*bx+0][3*by+2]; // angular Z + + // linear Y + K_18x18[ig+1][jg+0] = K[3*bx+1][3*by+0]; // linear X + K_18x18[ig+1][jg+1] = K[3*bx+1][3*by+1]; // linear Y + K_18x18[ig+1][jg+5] = K[3*bx+1][3*by+2]; // angular Z + + // angular Z + K_18x18[ig+5][jg+0] = K[3*bx+2][3*by+0]; // linear X + K_18x18[ig+5][jg+1] = K[3*bx+2][3*by+1]; // linear Y + K_18x18[ig+5][jg+5] = K[3*bx+2][3*by+2]; // angular Z + } + } + + + // Copy the stiffness matrix by block 3x3 into global matrix (the new index of each bloc into global matrix is a combination of 2, 8 and 15 in indices) + const StiffnessMatrix &K_bending = tinfo.stiffnessMatrixBending; + for (unsigned int bx=0; bx<3; bx++) + { + // Global row index + ig = 6*bx+2; + + for (unsigned int by=0; by<3; by++) + { + // Global column index + jg = 6*by+2; + + // Iterates over the indices of the 3x3 block + for (unsigned int i=0; i<3; i++) + { + for (unsigned int j=0; j<3; j++) + { + K_18x18[ig+i][jg+j] += K_bending[3*bx+i][3*by+j]; + } + } + + } + } + + + // Extend rotation matrix and its transpose + StiffnessMatrixFull R18x18, Rt18x18; + + for(unsigned int i=0;i<3;++i) + { + for(unsigned int j=0;j<3;++j) + { + R18x18[i][j] = R18x18[i+3][j+3] = R18x18[i+6][j+6] = R18x18[i+9][j+9] = R18x18[i+12][j+12] = R18x18[i+15][j+15] = tinfo.R[i][j]; + Rt18x18[i][j] = Rt18x18[i+3][j+3] = Rt18x18[i+6][j+6] = Rt18x18[i+9][j+9] = Rt18x18[i+12][j+12] = Rt18x18[i+15][j+15] = tinfo.Rt[i][j]; + } + } + + // Then we put the stifness matrix into the global frame + K_gs = Rt18x18 * K_18x18 * R18x18; + //dmsg_info() << "R=" << R18x18 << " -- " << Rt18x18 ; + //dmsg_info() << "K_gs=" << K_gs ; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Membrane elements + +template +void TriangularShellForceField::computeStiffnessMatrixCST(StiffnessMatrix &K, TriangleInformation &tinfo) +{ + // NOTE: we could use the ANDES template below for CST too, but this is a little faster + Mat<9,3, Real> L; + Mat<3,9, Real> Lt; + L.clear(); + + L[0][0] = tinfo.d[1][1]; + L[0][1] = 0; + L[0][2] = -tinfo.d[1][0]; + + L[1][0] = 0; + L[1][1] = -tinfo.d[1][0]; + L[1][2] = tinfo.d[1][1]; + + L[3][0] = tinfo.d[2][1]; + L[3][1] = 0; + L[3][2] = -tinfo.d[2][0]; + + L[4][0] = 0; + L[4][1] = -tinfo.d[2][0]; + L[4][2] = tinfo.d[2][1]; + + L[6][0] = tinfo.d[0][1]; + L[6][1] = 0; + L[6][2] = -tinfo.d[0][0]; + + L[7][0] = 0; + L[7][1] = -tinfo.d[0][0]; + L[7][2] = tinfo.d[0][1]; + + // Now should be: + // L *= h/2; + // K = 1/(h*Area) * L * Em * L^T; + // But we may simplify this a little and we also have 'h' already in Em + + Lt.transpose(L); + + K = L * materialMatrixMembrane * Lt / (4*tinfo.area); + + // Compute strain-displacement matrix + for (unsigned int i=0; i< tinfo.measure.size(); i++) { + tinfo.measure[i].B = Lt / (2*tinfo.area); + } + +} + +// The ANDES template for membrane element +// See for example: C. A. Felippa, A study of optimal membrane triangles with drilling freedoms, 2003 +template +void TriangularShellForceField::andesTemplate(StiffnessMatrix &K, const TriangleInformation &tinfo, + const Real alpha, const AndesBeta &beta) +{ + Real h = d_thickness.getValue(); + Real A4 = 4*tinfo.area; + + // Force-lumping matrix + Mat<9,3, Real> L; + Mat<3,9, Real> Lt; + + // y23, 0, x32, + L[0][0] = tinfo.d[1][1]; + L[0][1] = 0; + L[0][2] = -tinfo.d[1][0]; + + // 0, x32, y23, + L[1][0] = 0; + L[1][1] = -tinfo.d[1][0]; + L[1][2] = tinfo.d[1][1]; + + // alpha/6*y23*(y13 - y21), alpha/6*x32*(x31 - x12), alpha/3*(x31*y13 - x12*y21), + L[2][0] = alpha/6.0*tinfo.d[1][1]*(tinfo.d[0][1] - tinfo.d[2][1]); + L[2][1] = alpha/6.0*(-tinfo.d[1][0])*(tinfo.d[2][0] - tinfo.d[0][0]); + L[2][2] = alpha/3.0*(tinfo.d[0][0]*tinfo.d[0][1] - tinfo.d[2][0]*tinfo.d[2][1]), + + // y31, 0, x13, + L[3][0] = tinfo.d[2][1]; + L[3][1] = 0; + L[3][2] = -tinfo.d[2][0]; + + // 0, x13, y31, + L[4][0] = 0; + L[4][1] = -tinfo.d[2][0]; + L[4][2] = tinfo.d[2][1]; + + // alpha/6*y31*(y21 - y32), alpha/6*x13*(x12 - x23), alpha/3*(x12*y21 - x23*y32), + L[5][0] = alpha/6*tinfo.d[2][1]*(tinfo.d[1][1] - tinfo.d[0][1]); + L[5][1] = alpha/6*(-tinfo.d[2][0])*(tinfo.d[0][0] - tinfo.d[1][0]); + L[5][2] = alpha/3*(tinfo.d[1][0]*tinfo.d[1][1] - tinfo.d[0][0]*tinfo.d[0][1]); + + // y12, 0, x21, + L[6][0] = tinfo.d[0][1]; + L[6][1] = 0; + L[6][2] = -tinfo.d[0][0]; + + // 0, x21, y12, + L[7][0] = 0; + L[7][1] = -tinfo.d[0][0]; + L[7][2] = tinfo.d[0][1]; + + // alpha/6*y12*(y32 - y13), alpha/6*x21*(x23 - x31), alpha/3*(x23*y32 - x31*y13) + L[8][0] = alpha/6*tinfo.d[0][1]*(tinfo.d[2][1] - tinfo.d[1][1]); + L[8][1] = alpha/6*(-tinfo.d[0][0])*(tinfo.d[1][0] - tinfo.d[2][0]); + L[8][2] = alpha/3*(tinfo.d[2][0]*tinfo.d[2][1] - tinfo.d[1][0]*tinfo.d[1][1]); + + // Now should be: + // L *= h/2; + // Kb = 1/(h*Area) * L * Em * L^T; + // But we may simplify this a little and we also have 'h' already in Em + + Lt.transpose(L); + + // Add base stiffness matrix + K = L * materialMatrixMembrane * Lt / A4; + + // Transformation matrix from DOFs to hierarchical drilling freedoms + Mat<3,9, Real> T( + Vec9(tinfo.d[1][0], tinfo.d[1][1], -A4, tinfo.d[2][0], tinfo.d[2][1], 0, tinfo.d[0][0], tinfo.d[0][1], 0), + Vec9(tinfo.d[1][0], tinfo.d[1][1], 0, tinfo.d[2][0], tinfo.d[2][1], -A4, tinfo.d[0][0], tinfo.d[0][1], 0), + Vec9(tinfo.d[1][0], tinfo.d[1][1], 0, tinfo.d[2][0], tinfo.d[2][1], 0, tinfo.d[0][0], tinfo.d[0][1], -A4) + ); + T /= -A4; + + Mat<9,3, Real> Tt; + Tt.transpose(T); + + Mat<3,3, Real> Q1( + Vec3(beta[1], beta[2], beta[3])/tinfo.l2[0], + Vec3(beta[4], beta[5], beta[6])/tinfo.l2[1], + Vec3(beta[7], beta[8], beta[9])/tinfo.l2[2]); + Q1 *= 2.0*tinfo.area/3.0; + + Mat<3,3, Real> Q2( + Vec3(beta[9], beta[7], beta[8])/tinfo.l2[0], + Vec3(beta[3], beta[1], beta[2])/tinfo.l2[1], + Vec3(beta[6], beta[4], beta[5])/tinfo.l2[2]); + Q2 *= 2.0*tinfo.area/3.0; + + Mat<3,3, Real> Q3( + Vec3(beta[5], beta[6], beta[4])/tinfo.l2[0], + Vec3(beta[8], beta[9], beta[7])/tinfo.l2[1], + Vec3(beta[2], beta[3], beta[1])/tinfo.l2[2]); + Q3 *= 2.0*tinfo.area/3.0; + + Mat<3,3, Real> Q4 = (Q1 + Q2)/2.0, Q4t; + Mat<3,3, Real> Q5 = (Q2 + Q3)/2.0, Q5t; + Mat<3,3, Real> Q6 = (Q3 + Q1)/2.0, Q6t; + Q4t.transpose(Q4); + Q5t.transpose(Q5); + Q6t.transpose(Q6); + + Mat<3,3, Real> Te, Tet, Enat; + Te[0][0] = -tinfo.d[1][1]*tinfo.d[2][1]*tinfo.l2[0]; + Te[0][1] = -tinfo.d[2][1]*tinfo.d[0][1]*tinfo.l2[1]; + Te[0][2] = -tinfo.d[0][1]*tinfo.d[1][1]*tinfo.l2[2]; + + Te[1][0] = -tinfo.d[1][0]*tinfo.d[2][0]*tinfo.l2[0]; + Te[1][1] = -tinfo.d[2][0]*tinfo.d[0][0]*tinfo.l2[1]; + Te[1][2] = -tinfo.d[1][0]*tinfo.d[1][0]*tinfo.l2[2]; + + Te[2][0] = (tinfo.d[1][1]*tinfo.d[2][0] + tinfo.d[1][0]*tinfo.d[2][1])*tinfo.l2[0]; + Te[2][1] = (tinfo.d[2][1]*tinfo.d[1][0] + tinfo.d[2][0]*tinfo.d[0][1])*tinfo.l2[1]; + Te[2][2] = (tinfo.d[0][1]*tinfo.d[1][0] + tinfo.d[0][0]*tinfo.d[1][1])*tinfo.l2[2]; + + Te /= tinfo.area*A4; + + Tet.transpose(Te); + Enat = Tet * materialMatrixMembrane * Te; + + // Add higher order stiffness matrix + K += beta[0] * Real(3.0/4.0) * h * tinfo.area * Tt * + // Higher order stiffness in terms of hierarchical rotations + (Q4t * Enat * Q4 + Q5t * Enat * Q5 + Q6t * Enat * Q6) + * T; +} + +template +void TriangularShellForceField::computeStiffnessMatrixAll3I(StiffnessMatrix &K, TriangleInformation &tinfo) +{ + return andesTemplate(K, tinfo, 1.0, AndesBeta(4.0/9.0, 1.0/12.0, 5.0/12.0, 1.0/2.0, 0.0, 1.0/3.0, -1.0/3.0, -1.0/12.0, -1.0/2.0, -5.0/12.0)); +} + +template +void TriangularShellForceField::computeStiffnessMatrixAll3M(StiffnessMatrix &K, TriangleInformation &tinfo) +{ + return andesTemplate(K, tinfo, 1.0, AndesBeta(4.0/9.0, 1.0/4.0, 5.0/4.0, 3.0/2.0, 0.0, 1.0, -1.0, -1.0/4.0, -3.0/2.0, -5.0/4.0)); +} + +template +void TriangularShellForceField::computeStiffnessMatrixAllLS(StiffnessMatrix &K, TriangleInformation &tinfo) +{ + return andesTemplate(K, tinfo, 1.0, AndesBeta(4.0/9.0, 3.0/20.0, 3.0/4.0, 9.0/10.0, 0.0, 3.0/5.0, -3.0/5.0, -3.0/20.0, -9.0/10.0, -3.0/4.0)); +} + +template +void TriangularShellForceField::computeStiffnessMatrixLSTRet(StiffnessMatrix &K, TriangleInformation &tinfo) +{ + return andesTemplate(K, tinfo, 4.0/3.0, AndesBeta(1.0/2.0, 2.0/3.0, -2.0/3.0, 0.0, 0.0, -4.0/3.0, 4.0/3.0, -2.0/3.0, 0.0, 2.0/3.0)); +} + +// Optimal ANDES membrane element +// See: C. A. Felippa, A study of optimal membrane triangles with drilling freedoms, 2003 +template +void TriangularShellForceField::computeStiffnessMatrixAndesOpt(StiffnessMatrix &K, TriangleInformation &tinfo) +{ + Real beta0 = helper::rmax(0.5 - 2.0*d_poisson.getValue()*d_poisson.getValue(), 0.01); + return andesTemplate(K, tinfo, 3.0/2.0, AndesBeta(beta0, 1.0, 2.0, 1.0, 0.0, 1.0, -1.0, -1.0, -1.0, -2.0)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Bending plate elements + +template +void TriangularShellForceField::computeStiffnessMatrixDKT(StiffnessMatrix &K, TriangleInformation &tinfo) +{ + // Weights and abscissa for 6-point Gaussian quadrature of a triangle + // (source: http://www.electromagnetics.biz/integration.htm) + Vec<6, Real> gx(0.8168476, 0.09157621, 0.09157621, 0.1081030, 0.4459485, 0.4459485); + Vec<6, Real> gy(0.09157621, 0.8168476, 0.09157621, 0.4459485, 0.1081030, 0.4459485); + Vec<6, Real> gw(0.05497587, 0.05497587, 0.05497587, 0.1116908, 0.1116908, 0.1116908); + + // Integrage over triangle area + K.clear(); + //dmsg_info() << "B=\n"; + for (int i=0; i<6; i++) { + StrainDisplacement B; + Mat<9,3, Real> Bt; + dktSD(B, tinfo, gx[i], gy[i]); // Compute strain-displacement matrix + //dmsg_info() << " " << B ; + Bt.transpose(B); + K += gw[i] * Bt * materialMatrixBending * B; + } + // Compute strain-displacement matrix + for (unsigned int i=0; i< tinfo.measure.size(); i++) { + dktSD(tinfo.measure[i].Bb, tinfo, + tinfo.measure[i].point[0], + tinfo.measure[i].point[1]); + } + + //dmsg_info() ; + K *= 2*tinfo.area; +} + +template +void TriangularShellForceField::dktSD(StrainDisplacement &B, const TriangleInformation &tinfo, const Real xi, const Real eta) +{ + Real P4 = -6*tinfo.d[1][0]/tinfo.l2[1]; + Real P5 = -6*tinfo.d[2][0]/tinfo.l2[2]; + Real P6 = -6*tinfo.d[0][0]/tinfo.l2[0]; + + Real t4 = -6*tinfo.d[1][1]/tinfo.l2[1]; + Real t5 = -6*tinfo.d[2][1]/tinfo.l2[2]; + Real t6 = -6*tinfo.d[0][1]/tinfo.l2[0]; + + Real q4 = 3*tinfo.d[1][0]*tinfo.d[1][1]/tinfo.l2[1]; + Real q5 = 3*tinfo.d[2][0]*tinfo.d[2][1]/tinfo.l2[2]; + Real q6 = 3*tinfo.d[0][0]*tinfo.d[0][1]/tinfo.l2[0]; + + Real r4 = 3*tinfo.d[1][1]*tinfo.d[1][1]/tinfo.l2[1]; + Real r5 = 3*tinfo.d[2][1]*tinfo.d[2][1]/tinfo.l2[2]; + Real r6 = 3*tinfo.d[0][1]*tinfo.d[0][1]/tinfo.l2[0]; + + + Real tmp = 1.0 - 2.0*xi; + Vec9 Hx_dxi( + P6*tmp + eta * (P5 - P6), + q6*tmp - eta * (q5 + q6), + -4.0 + 6.0*(xi + eta) + r6*tmp - eta*(r5 + r6), + -P6*tmp + eta*(P4 + P6), + q6*tmp - eta*(q6 - q4), + -2.0 + 6.0*xi + r6*tmp + eta*(r4 - r6), + - eta*(P5 + P4), + eta*(q4 - q5), + - eta*(r5 - r4) + ); + + //tmp = 1.0 - 2.0*xi; + Vec9 Hy_dxi( + t6*tmp + eta*(t5 - t6), + 1.0 + r6*tmp - eta*(r5 + r6), + -q6*tmp + eta*(q5 + q6), + -t6*tmp + eta*(t4 + t6), + -1.0 + r6*tmp + eta*(r4 - r6), + -q6*tmp - eta*(q4 - q6), + -eta*(t4 + t5), + eta*(r4 - r5), + -eta*(q4 - q5) + ); + + tmp = 1.0 - 2.0*eta; + Vec9 Hx_deta( + -P5*tmp - xi*(P6 - P5), + q5*tmp - xi*(q5 + q6), + -4.0 + 6.0*(xi + eta) + r5*tmp - xi*(r5 + r6), + xi*(P4 + P6), + xi*(q4 - q6), + -xi*(r6 - r4), + P5*tmp - xi*(P4 + P5), + q5*tmp + xi*(q4 - q5), + -2.0 + 6.0*eta + r5*tmp + xi*(r4 - r5) + ); + + // tmp = 1.0 - 2.0*eta; + Vec9 Hy_deta( + -t5 * tmp - xi*(t6 - t5), + 1 + r5*tmp - xi*(r5 + r6), + -q5 * tmp + xi*(q5 + q6), + xi * (t4 + t6), + xi * (r4 - r6), + -xi * (q4 - q6), + t5 * tmp - xi*(t4 + t5), + -1 + r5*tmp + xi*(r4 - r5), + -q5*tmp - xi*(q4 - q5) + ); + + B = StrainDisplacement( + Hx_dxi*tinfo.d[2][1] + Hx_deta*tinfo.d[0][1], + -Hy_dxi*tinfo.d[2][0] - Hy_deta*tinfo.d[0][0], + -Hx_dxi*tinfo.d[2][0] - Hx_deta*tinfo.d[0][0] + Hy_dxi*tinfo.d[2][1] + Hy_deta*tinfo.d[0][1] + ); + + B /= 2*tinfo.area; +} + +template +void TriangularShellForceField::draw(const core::visual::VisualParams* vparams){ + // Draw arrows from using shell triangle info (tinfo.R) in the center of the triangle + + int nbTriangles=_topology->getNbTriangles(); + auto triangles = _topology->getTriangles(); + const VecCoord& positions =this->mstate->read(sofa::core::vec_id::read_access::position)->getValue(); + type::vector& triangleInf = *(triangleInfo.beginEdit()); + const Real radius = d_arrow_radius.getValue(); + for (unsigned int i=0; i< nbTriangles; i++) + { + const TriangleInformation &tinfo = triangleInf[i]; + Vec3 vx = tinfo.R * Vec3(1, 0, 0); + Vec3 vy = tinfo.R * Vec3(0, 1, 0); + Vec3 vz = tinfo.R * Vec3(0, 0, 1); + + auto triangle = _topology->getTriangle(i); + // get triangle i indices + auto a = triangle[0]; + auto b = triangle[1]; + auto c = triangle[2]; + + // compute the center of the triangle + Vec3 center = (positions[a].getCenter() + positions[b].getCenter() + positions[c].getCenter())/3; + vparams->drawTool()->drawArrow(center, center + vx, radius, type::RGBAColor(1.0, 0.0, 0.0, 1.0)); + vparams->drawTool()->drawArrow(center, center + vy, radius, type::RGBAColor(0.0, 1.0, 0.0, 1.0)); + vparams->drawTool()->drawArrow(center, center + vz, radius, type::RGBAColor(0.0, 0.0, 1.0, 1.0)); + } + triangleInfo.endEdit(); + } + + +} // namespace forcefield + +} // namespace component + +} // namespace sofa + + +#endif diff --git a/src/Shell/mapping/BendingPlateMechanicalMapping.cpp b/src/Shell/mapping/BendingPlateMechanicalMapping.cpp new file mode 100644 index 0000000..1c0eab5 --- /dev/null +++ b/src/Shell/mapping/BendingPlateMechanicalMapping.cpp @@ -0,0 +1,47 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include +#include +#include +#include +#include + + +namespace sofa::component::mapping +{ + +using namespace sofa::defaulttype; +using namespace core; +using namespace core::behavior; + +int BendingPlateMechanicalMappingClass = core::RegisterObject("Mechanical mapping between triangular shell (TriangularBendingFEMForceField) and a cloud of points") +.add< BendingPlateMechanicalMapping< Rigid3Types, Vec3Types > >() +; + +template class SOFA_SHELL_API BendingPlateMechanicalMapping< Rigid3Types, Vec3Types >; + + +} // namespace diff --git a/src/Shell/mapping/BendingPlateMechanicalMapping.h b/src/Shell/mapping/BendingPlateMechanicalMapping.h new file mode 100644 index 0000000..4060c85 --- /dev/null +++ b/src/Shell/mapping/BendingPlateMechanicalMapping.h @@ -0,0 +1,166 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include + + + +#include +#include + +#include +#include + +#include + +#include +#include + + +namespace sofa::component::mapping +{ + +using namespace sofa::type; +using namespace shell::forcefield; +using namespace sofa::core::topology; +using namespace sofa::helper::system::thread; +using namespace core::topology; + + +template +class BendingPlateMechanicalMapping : public core::Mapping +{ +public: + SOFA_CLASS(SOFA_TEMPLATE2(BendingPlateMechanicalMapping,TIn,TOut), SOFA_TEMPLATE2(core::Mapping,TIn,TOut)); + typedef core::Mapping Inherit; + typedef TIn In; + typedef TOut Out; + + typedef typename In::VecCoord InVecCoord; + typedef typename In::VecDeriv InVecDeriv; + //typedef typename In::Coord InCoord; + //typedef typename In::Deriv InDeriv; + typedef typename In::MatrixDeriv InMatrixDeriv; + + typedef typename Out::VecCoord OutVecCoord; + typedef typename Out::VecDeriv OutVecDeriv; + //typedef typename Out::Coord OutCoord; + //typedef typename Out::Deriv OutDeriv; + typedef typename Out::MatrixDeriv OutMatrixDeriv; + typedef typename Out::Real Real; + + typedef Vec<3, Real> Vec3; + + + //typedef BaseMeshTopology::Edge Edge; + typedef BaseMeshTopology::SeqEdges SeqEdges; + typedef BaseMeshTopology::Triangle Triangle; + typedef BaseMeshTopology::SeqTriangles SeqTriangles; + + + + typedef typename TriangularBendingFEMForceField::TriangleInformation TriangleInformation; + + + BendingPlateMechanicalMapping(core::State* from, core::State* to) + : Inherit(from, to) + , inputTopo(NULL) + , outputTopo(NULL) + , measureError(initData(&measureError, false, "measureError","Error with high resolution mesh")) + , targetTopology(initLink("targetTopology","Targeted high resolution topology")) + { + } + + virtual ~BendingPlateMechanicalMapping() + { + } + + void init() override; + void reinit() override; + void draw(const core::visual::VisualParams* vparams) override; + + + void apply(const core::MechanicalParams *mparams, Data& out, const Data& in) override; + void applyJ(const core::MechanicalParams *mparams, Data& out, const Data& in) override; + void applyJT(const core::MechanicalParams *mparams, Data& out, const Data& in) override; + void applyJT(const core::ConstraintParams *cparams, Data& out, const Data& in) override; + +protected: + + BendingPlateMechanicalMapping() + : Inherit() + , inputTopo(NULL) + , outputTopo(NULL) + , measureError(initData(&measureError, false, "measureError","Error with high resolution mesh")) + , targetTopology(initLink("targetTopology","Targeted high resolution topology")) + { + } + + gl::GLSLShader shader; + + BaseMeshTopology* inputTopo; + BaseMeshTopology* outputTopo; + + Data measureError; + SingleLink, + sofa::core::topology::BaseMeshTopology, + BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> targetTopology; + + topology::container::dynamic::TriangleSetTopologyContainer* topologyTarget; + OutVecCoord verticesTarget; + SeqTriangles trianglesTarget; + + type::vector colourMapping; + type::vector coloursPerVertex; + type::vector vectorErrorCoarse; + type::vector vectorErrorTarget; + + // Pointer on the forcefield associated with the in topology + TriangularBendingFEMForceField* triangularBendingForcefield; + + // Pointer on the topological mapping to retrieve the list of edges + // XXX: The edges are no longer there!!! + //TriangleSubdivisionTopologicalMapping* triangleSubdivisionTopologicalMapping; + + void HSL2RGB(Vec3 &rgb, Real h, Real sl, Real l); + void MeasureError(); + Real DistanceHausdorff(BaseMeshTopology *topo1, BaseMeshTopology *topo2, type::vector &vectorError); + void ComputeNormals(type::vector &normals); + void FindTriangleInNormalDirection(const InVecCoord& highResVertices, const SeqTriangles highRestriangles, const type::vector &normals); + + // Computes the barycentric coordinates of a vertex within a triangle + void computeBaryCoefs(Vec3 &baryCoefs, const Vec3 &p, const Vec3 &a, const Vec3 &b, const Vec3 &c); + + Real FindClosestPoints(sofa::type::vector& listClosestVertices, const Vec3& point, const OutVecCoord &inVertices); + Real FindClosestEdges(sofa::type::vector& listClosestEdges, const Vec3& point, const OutVecCoord &inVertices, const SeqEdges &inEdges); + Real FindClosestTriangles(sofa::type::vector& listClosestEdges, const Vec3& point, const OutVecCoord &inVertices, const SeqTriangles &inTriangles); + + // Contains the list of base triangles a vertex belongs to + sofa::type::vector< sofa::type::vector > listBaseTriangles; + // Contains the barycentric coordinates of the same vertex within all base triangles + sofa::type::vector< sofa::type::vector > barycentricCoordinates; +}; +} // namespace diff --git a/src/Shell/mapping/BendingPlateMechanicalMapping.inl b/src/Shell/mapping/BendingPlateMechanicalMapping.inl new file mode 100644 index 0000000..c41f4aa --- /dev/null +++ b/src/Shell/mapping/BendingPlateMechanicalMapping.inl @@ -0,0 +1,1129 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once +#include +#include +// #include +#include + +// #include +// #include + + +namespace sofa::component::mapping +{ + +// using namespace sofa::component::collision; +// + +template +void BendingPlateMechanicalMapping::init() +{ +// std::cout << "BendingPlateMechanicalMapping::init()" << std::endl; + + // Retrieves topology + inputTopo = this->fromModel->getContext()->getMeshTopology(); + outputTopo = this->toModel->getContext()->getMeshTopology(); + + if (inputTopo && outputTopo && inputTopo->getNbTriangles() > 0) + { + const OutVecCoord &outVertices = this->toModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + listBaseTriangles.clear(); + barycentricCoordinates.clear(); + listBaseTriangles.resize(outVertices.size()); + barycentricCoordinates.resize(outVertices.size()); + + // Retrieves 'in' vertices and triangles + const InVecCoord &inVerticesRigid = this->fromModel->read(sofa::core::vec_id::read_access::position)->getValue(); + // Conversion to Vec3Types to be able to call same methods used by Hausdorff distance + OutVecCoord inVertices; + for (unsigned int i=0; igetEdges(); + const SeqTriangles &inTriangles = inputTopo->getTriangles(); + + // Iterates over 'out' vertices + Real minimumDistanceVertices, minimumDistanceEdges, minimumDistanceTriangles, minimumDistance; + int caseToProcess, triangleID; + Vec3 vertexBaryCoord; + for (unsigned int i=0; i listClosestVertices; + minimumDistanceVertices = FindClosestPoints(listClosestVertices, outVertices[i], inVertices); + + // Iterates over 'in' edges + sofa::type::vector listClosestEdges; + minimumDistanceEdges = FindClosestEdges(listClosestEdges, outVertices[i], inVertices, inEdges); + + // Iterates over 'in' triangles + sofa::type::vector listClosestTriangles; + minimumDistanceTriangles = FindClosestTriangles(listClosestTriangles, outVertices[i], inVertices, inTriangles); + + // Finds out which type of primitive is the closest + minimumDistance = std::min(minimumDistanceVertices, std::min(minimumDistanceEdges, minimumDistanceTriangles)); + + + // Adds the list of triangles attached to the primitives found + caseToProcess = 0; + if ( minimumDistance == minimumDistanceVertices ) + caseToProcess = 1; + if ( minimumDistance == minimumDistanceEdges ) + caseToProcess = 2; + if ( minimumDistance == minimumDistanceTriangles ) + caseToProcess = 3; + + switch(caseToProcess) + { + // If it is a vertex, consider the triangles attached to it + case 1 : + for (unsigned int j=0; jgetTrianglesAroundVertex( listClosestVertices[j] ); + for (unsigned int t=0; tgetTrianglesAroundEdge( listClosestEdges[j] ); + for (unsigned int t=0; tgetContext()->get(triangularBendingForcefield); + if (!triangularBendingForcefield) + { + msg_warning() << "WARNING(BendingPlateMechanicalMapping): triangularBendingForcefield was not found" ; + return; + } + +#if 0 + // Retrieves topological mapping to retrieve list of edges (to render in wireframe mode) + triangleSubdivisionTopologicalMapping = NULL; +// this->getContext()->get(triangleSubdivisionTopologicalMapping, nameHighTopology.getValue(), sofa::core::objectmodel::BaseContext::SearchRoot); + this->getContext()->get(triangleSubdivisionTopologicalMapping); + if (!triangleSubdivisionTopologicalMapping) + { + msg_warning() << "WARNING(BendingPlateMechanicalMapping): triangleSubdivisionTopologicalMapping was not found" ; + return; + } +#endif + + // Call of apply() and applyJ() + this->Inherit::init(); + + // Set each colour of each vertex to default + const OutVecCoord &outVertices = this->toModel->read(sofa::core::vec_id::read_access::position)->getValue(); + for (unsigned int i=0; imaximum) + { + maximum = fabs(vectorErrorCoarse[i]); + } + } + Real correctedError; + for (unsigned int i=0; i maximum) + correctedError = maximum; + coloursPerVertex[i] = colourMapping[ (int)((correctedError/maximum)*239) ]; + } + + // Initialises shader + shader.InitShaders("shaders/errorMap.vert", "shaders/errorMap.frag"); + } + +} + + +template +void BendingPlateMechanicalMapping::reinit() +{ + std::cout << "BendingPlateMechanicalMapping::reinit()" << std::endl; + init(); +} + + +// Given H,S,L in range of 0-1 +// Returns a RGB colour in range of 0-255 +// http://www.geekymonkey.com/Programming/CSharp/RGB2HSL_HSL2RGB.htm +template +void BendingPlateMechanicalMapping::HSL2RGB(Vec3 &rgb, Real h, Real sl, Real l) +{ + Real v; + Real r,g,b; + + r = l; // default to gray + g = l; + b = l; + v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl); + if (v > 0) + { + Real m; + Real sv; + int sextant; + Real fract, vsf, mid1, mid2; + + m = l + l - v; + sv = (v - m ) / v; + h *= 6.0; + sextant = (int)h; + fract = h - sextant; + vsf = v * sv * fract; + mid1 = m + vsf; + mid2 = v - vsf; + switch (sextant) + { + case 0: + r = v; + g = mid1; + b = m; + break; + case 1: + r = mid2; + g = v; + b = m; + break; + case 2: + r = m; + g = v; + b = mid1; + break; + case 3: + r = m; + g = mid2; + b = v; + break; + case 4: + r = mid1; + g = m; + b = v; + break; + case 5: + r = v; + g = m; + b = mid2; + break; + } + } + + rgb[0] = r; + rgb[1] = g; + rgb[2] = b; +} + + +template +void BendingPlateMechanicalMapping::MeasureError() +{ + Real distance1; + std::cout << "Computing Hausdorff distance high res->coarse" << std::endl; + distance1 = DistanceHausdorff(targetTopology.get(), outputTopo, vectorErrorTarget); + std::cout << "Hausdorff distance between high res mesh and coarse mesh = " << distance1 << std::endl; + + Real average = 0; + for (unsigned int i=0; ihigh res" << std::endl; + distance2 = DistanceHausdorff(outputTopo, targetTopology.get(), vectorErrorCoarse); + std::cout << "Hausdorff distance between coarse mesh and high res mesh = " << distance2 << std::endl; + + average = 0; + for (unsigned int i=0; igetNbTriangles() <= 0) + { + msg_warning() << "BendingPlateMechanicalMapping apply() requires an input triangular topology" ; + return; + } + + if (!triangularBendingForcefield) + { + msg_warning() << "No TriangularBendingForcefield has been found" ; + this->getContext()->get(triangularBendingForcefield); + return; + } + else + { + type::vector& triangleInf = *(triangularBendingForcefield->getTriangleInfo().beginEdit()); + TriangleInformation *tinfo = NULL; + + // List of in triangles + const SeqTriangles& inTriangles = inputTopo->getTriangles(); + + // Computes the coefficients ci for each triangle + for (unsigned int t=0; tcoefficients = tinfo->invC * (tinfo->u + tinfo->u_rest); + } + + Vec3 a, b, c, baryCoord, vertexLocal; + Real z; + for (unsigned int i=0; igetTopology()->getTriangle(listBaseTriangles[i][t]); + tinfo = &triangleInf[listBaseTriangles[i][t]]; + + // Local coordinates needed to compute deflection + a = in[ triangle[0] ].getCenter(); + vertexLocal = tinfo->Qframe.rotate(out[i]-a); + + // Adds deflection + z = tinfo->coefficients[0] + tinfo->coefficients[1]*vertexLocal[0] + tinfo->coefficients[2]*vertexLocal[1] + tinfo->coefficients[3]*vertexLocal[0]*vertexLocal[0] + tinfo->coefficients[4]*vertexLocal[0]*vertexLocal[1] + tinfo->coefficients[5]*vertexLocal[1]*vertexLocal[1] + tinfo->coefficients[6]*vertexLocal[0]*vertexLocal[0]*vertexLocal[0] + tinfo->coefficients[7]*vertexLocal[0]*vertexLocal[1]*vertexLocal[1] + tinfo->coefficients[8]*vertexLocal[1]*vertexLocal[1]*vertexLocal[1]; + + w += tinfo->Qframe.inverseRotate(Vec3(0, 0, z)); + } + + out[i] += w/listBaseTriangles[i].size(); + } + + triangularBendingForcefield->getTriangleInfo().endEdit(); + } + +// stop = timer.getTime(); +// std::cout << "time apply = " << stop-start << std::endl; +} + + +// Updates velocities of the visual mesh from mechanical vertices +template +void BendingPlateMechanicalMapping::applyJ(const core::MechanicalParams* /*mparams*/, Data& dOut, const Data& dIn) +{ + helper::WriteAccessor< Data > out = dOut; + helper::ReadAccessor< Data > in = dIn; + +// std::cout << "---------------- ApplyJ ----------------------------" << std::endl; + +// sofa::helper::system::thread::ctime_t start, stop; +// sofa::helper::system::thread::CTime timer; +// +// start = timer.getTime(); + + if (!inputTopo || !outputTopo) + { + msg_warning() << "BendingPlateMechanicalMapping applyJ() was called before init()" ; + return; + } + if (inputTopo->getNbTriangles() <= 0) + { + msg_warning() << "BendingPlateMechanicalMapping applyJ() requires an input triangular topology" ; + return; + } + + if (!triangularBendingForcefield) + { + msg_warning() << "No TriangularBendingForcefield has been found" ; + this->getContext()->get(triangularBendingForcefield); + return; + } + else + { + type::vector& triangleInf = *(triangularBendingForcefield->getTriangleInfo().beginEdit()); + TriangleInformation *tinfo = NULL; + + // List of 'in' positions + const InVecCoord &inVertices = this->fromModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + // List of 'out' positions + const OutVecCoord &outVertices = this->toModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + // List of in triangles + const SeqTriangles& inTriangles = inputTopo->getTriangles(); + + // Computes the coefficients ci for each triangle + Vec3 va_a, va_b, va_c, va_a_local, va_b_local, va_c_local; + Vec <9, Real> v_u; + for (unsigned int t=0; tgetTopology()->getTriangle(t); + tinfo = &triangleInf[t]; + + // Gets the angular velocities of each vertex + va_a = getVOrientation(in[ triangle[0] ]); + va_b = getVOrientation(in[ triangle[1] ]); + va_c = getVOrientation(in[ triangle[2] ]); + // In local frame + va_a_local = tinfo->Qframe.rotate(va_a); + va_b_local = tinfo->Qframe.rotate(va_b); + va_c_local = tinfo->Qframe.rotate(va_c); + // Fills in du/dt + v_u.clear(); + v_u[1] = va_a_local[0]; v_u[2] = va_a_local[1]; + v_u[4] = va_b_local[0]; v_u[5] = va_b_local[1]; + v_u[7] = va_c_local[0]; v_u[8] = va_c_local[1]; + + tinfo->coefficients = tinfo->invC * v_u; + } + + + // Iterates over out vertices to update coordinates + Vec3 v_a, v_b, v_c, baryCoord, a, vertexLocal; + Real v_z; + for (unsigned int i=0; igetTopology()->getTriangle(listBaseTriangles[i][t]); + tinfo = &triangleInf[listBaseTriangles[i][t]]; + + // Local coordinates needed to compute deflection + a = inVertices[ triangle[0] ].getCenter(); + vertexLocal = tinfo->Qframe.rotate(outVertices[i]-a); + + // Adds deflection velocity + v_z = tinfo->coefficients[0] + tinfo->coefficients[1]*vertexLocal[0] + tinfo->coefficients[2]*vertexLocal[1] + tinfo->coefficients[3]*vertexLocal[0]*vertexLocal[0] + tinfo->coefficients[4]*vertexLocal[0]*vertexLocal[1] + tinfo->coefficients[5]*vertexLocal[1]*vertexLocal[1] + tinfo->coefficients[6]*vertexLocal[0]*vertexLocal[0]*vertexLocal[0] + tinfo->coefficients[7]*vertexLocal[0]*vertexLocal[1]*vertexLocal[1] + tinfo->coefficients[8]*vertexLocal[1]*vertexLocal[1]*vertexLocal[1]; + w += v_z; + + } + + // Position in Qframe + Vec3 out0 = tinfo->Qframe.rotate(out[i]); + // Computed deflection w in Qframe + out0[2] += w/listBaseTriangles[i].size(); + out[i] = tinfo->Qframe.inverseRotate(out0); + } + + triangularBendingForcefield->getTriangleInfo().endEdit(); + + } + +// stop = timer.getTime(); +// std::cout << "time applyJ = " << stop-start << std::endl; +} + + +// Updates positions of the mechanical vertices from visual f(n-1) = JT * fn +template +void BendingPlateMechanicalMapping::applyJT(const core::MechanicalParams * /*mparams*/, Data& dOut, const Data& dIn) +{ + helper::WriteAccessor< Data > out = dOut; + helper::ReadAccessor< Data > in = dIn; + +// std::cout << "---------------- ApplyJT ----------------------------" << std::endl; + +// sofa::helper::system::thread::ctime_t start, stop; +// sofa::helper::system::thread::CTime timer; +// +// start = timer.getTime(); + + if (!inputTopo || !outputTopo) + { + msg_warning() << "BendingPlateMechanicalMapping applyJT() was called before init()" ; + return; + } + if (inputTopo->getNbTriangles() <= 0) + { + msg_warning() << "BendingPlateMechanicalMapping applyJT() requires an input triangular topology" ; + return; + } + + if (!triangularBendingForcefield) + { + msg_warning() << "No TriangularBendingForcefield has been found" ; + this->getContext()->get(triangularBendingForcefield); + return; + } + else + { + type::vector& triangleInf = *(triangularBendingForcefield->getTriangleInfo().beginEdit()); + TriangleInformation *tinfo = NULL; + + // List of in triangles + const SeqTriangles& inTriangles = inputTopo->getTriangles(); + + sofa::type::vector< Mat<9, 9, Real> > vecTransposedInvC; + for (unsigned int t=0; tinvC.transposed()); + } + + // List of 'in' positions + const OutVecCoord &inVertices = this->toModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + // List of 'out' positions (mechanical points) + const InVecCoord &outVertices = this->fromModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + // Iterates over in vertices + Triangle triangle; + Vec3 baryCoord, inLocal, a, vertexLocal, torqueA, torqueB, torqueC; + Real Fz; + Vec<9, Real> polynomDeflection, a_u; + for (unsigned int i=0; igetTopology()->getTriangle(listBaseTriangles[i][t]); + tinfo = &triangleInf[listBaseTriangles[i][t]]; + + // Applied force into local frame + inLocal = tinfo->Qframe.rotate(in[i]); + Fz = inLocal[2]; + + if (Fz != 0) + { + // Local coordinates needed to compute deflection + a = outVertices[ triangle[0] ].getCenter(); + vertexLocal = tinfo->Qframe.rotate(inVertices[i]-a); // WARNING: SHOULD NOT WE NEED TO PROJECT THE INVERTICES INTO THE TRIANGLE'S PLAN FIRST? + + // Uz = c1 + c2*x+ c3*y + c4*x^2 + c5*x*y + c6*y^2 + c7*x^3 + c8*x*y^2 + c9*y^3 + polynomDeflection[0] = 1; + polynomDeflection[1] = vertexLocal[0]; + polynomDeflection[2] = vertexLocal[1]; + polynomDeflection[3] = vertexLocal[0]*vertexLocal[0]; + polynomDeflection[4] = vertexLocal[0]*vertexLocal[1]; + polynomDeflection[5] = vertexLocal[1]*vertexLocal[1]; + polynomDeflection[6] = vertexLocal[0]*vertexLocal[0]*vertexLocal[0]; + polynomDeflection[7] = vertexLocal[0]*vertexLocal[1]*vertexLocal[1]; + polynomDeflection[8] = vertexLocal[1]*vertexLocal[1]*vertexLocal[1]; + + polynomDeflection *= Fz; + + // Moments at each point + a_u = (vecTransposedInvC[ listBaseTriangles[i][t] ] * polynomDeflection) / listBaseTriangles[i].size(); + + // Moments into global frame + torqueA = tinfo->Qframe.inverseRotate(Vec3(a_u[1], a_u[2], 0)); + torqueB = tinfo->Qframe.inverseRotate(Vec3(a_u[4], a_u[5], 0)); + torqueC = tinfo->Qframe.inverseRotate(Vec3(a_u[7], a_u[8], 0)); + + getVOrientation(out[ triangle[0] ]) += torqueA; + getVOrientation(out[ triangle[1] ]) += torqueB; + getVOrientation(out[ triangle[2] ]) += torqueC; + } + } + } + + } + + +// stop = timer.getTime(); +// std::cout << "time applyJT = " << stop-start << std::endl; +} + + +template +void BendingPlateMechanicalMapping::applyJT(const core::ConstraintParams * /*cparams*/, Data& dOut, const Data& dIn) +{ + SOFA_UNUSED(dIn); + SOFA_UNUSED(dOut); + + // TODO : Unique verification and put the component in "dead mode" if there is missing inoformation + + if (!inputTopo || !outputTopo) + { + msg_warning() << "BendingPlateMechanicalMapping applyJT() was called before init()" ; + return; + } + if (inputTopo->getNbTriangles() <= 0) + { + msg_warning() << "BendingPlateMechanicalMapping applyJT() requires an input triangular topology" ; + return; + } + + if (!triangularBendingForcefield) + { + msg_warning() << "No TriangularBendingForcefield has been found" ; + this->getContext()->get(triangularBendingForcefield); + return; + } + + + /* + helper::WriteAccessor< Data > out = dOut; + helper::ReadAccessor< Data > in = dIn; + + bsInterpolation->applyJTOnBTriangle(projN, projElements, + in.ref(), out); + + + const OutMatrixDeriv& in, InMatrixDeriv &out + const InVecCoord& xSim = this->mState->read(sofa::core::vec_id::read_access::position)->getValue(); + const VecVec3d& x = this->mStateNodes->read(sofa::core::vec_id::read_access::position)->getValue(); + typename Out::MatrixDeriv::RowConstIterator rowItEnd = in.end(); + + for (typename OutMatrixDeriv::RowConstIterator rowIt = in.begin(); + rowIt != rowItEnd; ++rowIt) + { + typename OutMatrixDeriv::ColConstIterator colItEnd = rowIt.end(); + typename OutMatrixDeriv::ColConstIterator colIt = rowIt.begin(); + + if (colIt != colItEnd) + { + typename InMatrixDeriv::RowIterator o = out.writeLine(rowIt.index()); + for ( ; colIt != colItEnd; ++colIt) + { + Vec3 f1, f2, f3; // resulting linear velocities on corner nodes + Vec3 f1r, f2r, f3r; // resulting angular velocities + + Index ptId = colIt.index(); + applyJTCore(xSim, x, projElements[ptId], projN[ptId], colIt.val(), + f1, f2, f3, f1r, f2r, f3r); + + sofa::core::topology::Triangle tri = this->inputTopology->getTriangle(projElements[ptId]); + o.addCol(tri[0], InDeriv(f1, f1r)); + o.addCol(tri[1], InDeriv(f2, f2r)); + o.addCol(tri[2], InDeriv(f3, f3r)); + } + } + } + + */ + + +} + + +template +void BendingPlateMechanicalMapping::draw(const core::visual::VisualParams* vparams) +{ + const OutVecCoord &outVertices = this->toModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + if(vparams->displayFlags().getShowVisualModels()) + { + glDisable(GL_LIGHTING); + + if (measureError.getValue()) + { + shader.TurnOn(); + + + const SeqTriangles &outTriangles = outputTopo->getTriangles(); + unsigned int index; + + glEnable(GL_DEPTH_TEST); + glPolygonOffset(1.0, 1.0); + + if(vparams->displayFlags().getShowWireFrame()) + { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glEnable(GL_POLYGON_OFFSET_LINE); + glColor4f(0.0, 0.0, 1.0, 1.0); + glBegin(GL_TRIANGLES); + for (unsigned int i=0; igetSubEdges(); +// const SeqEdges &outEdges = outputTopo->getEdges(); + + // Render shells' contours (subdivision of edges) + glColor4f(1.0, 1.0, 1.0, 1.0); +// glColor4f(0.0, 0.0, 0.0, 1.0); + glLineWidth(0.5); + glBegin(GL_LINES); + for (unsigned int i=0; i +#include +#include +#include +#include + +namespace sofa +{ + +namespace component +{ + +namespace mapping +{ + +using namespace sofa::defaulttype; +using namespace core; +using namespace core::behavior; + +int BezierTriangleMechanicalMappingClass = core::RegisterObject("Mechanical mapping between triangular shell and a cloud of points") +.add< BezierTriangleMechanicalMapping< Rigid3Types, Vec3Types > >() +; + +template class SOFA_SHELL_API BezierTriangleMechanicalMapping< Rigid3Types, Vec3Types >; + +} // namespace mapping + +} // namespace component + +} // namespace sofa diff --git a/src/Shell/mapping/BezierTriangleMechanicalMapping.h b/src/Shell/mapping/BezierTriangleMechanicalMapping.h new file mode 100644 index 0000000..714a3fc --- /dev/null +++ b/src/Shell/mapping/BezierTriangleMechanicalMapping.h @@ -0,0 +1,274 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#ifndef SOFA_COMPONENT_MAPPING_BEZIERTRIANGLEMAPPING_INL +#define SOFA_COMPONENT_MAPPING_BEZIERTRIANGLEMAPPING_INL + + +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#include + +// Use quaternions for rotations +//#define ROTQ +// We have own code to check the getJ() because checkJacobian sucks (at this +// point in time). +//#define CHECK_J + +namespace sofa +{ + +namespace component +{ + +namespace mapping +{ + +using namespace sofa::type; +using namespace sofa::core::topology; +using namespace sofa::helper::system::thread; +using namespace core::topology; +using namespace sofa::core::behavior; + +template +class BezierTriangleMechanicalMapping : public core::Mapping +{ +public: + SOFA_CLASS(SOFA_TEMPLATE2(BezierTriangleMechanicalMapping,TIn,TOut), SOFA_TEMPLATE2(core::Mapping,TIn,TOut)); + typedef core::Mapping Inherit; + typedef TIn In; + typedef TOut Out; + + typedef typename In::VecCoord InVecCoord; + typedef typename In::VecDeriv InVecDeriv; + typedef typename In::Coord InCoord; + typedef typename In::Deriv InDeriv; + typedef typename In::MatrixDeriv InMatrixDeriv; + typedef typename In::Real InReal; + + typedef typename Out::VecCoord OutVecCoord; + typedef typename Out::VecDeriv OutVecDeriv; + typedef typename Out::Coord OutCoord; + typedef typename Out::Deriv OutDeriv; + typedef typename Out::MatrixDeriv OutMatrixDeriv; + typedef typename Out::Real OutReal; + + typedef typename sofa::component::forcefield::BezierTriangularBendingFEMForceField BezierFF; + + typedef InReal Real; + + typedef Vec<3, Real> Vec3; + typedef Mat<3, 3, Real> Mat33; + + typedef sofa::type::Quat Quat; + + + typedef sofa::Index Index; + //typedef BaseMeshTopology::Edge Edge; + typedef BaseMeshTopology::SeqEdges SeqEdges; + typedef BaseMeshTopology::Triangle Triangle; + typedef BaseMeshTopology::SeqTriangles SeqTriangles; + + enum { NIn = sofa::defaulttype::DataTypeInfo::Size }; + enum { NOut = sofa::defaulttype::DataTypeInfo::Size }; + typedef type::Mat MBloc; + typedef sofa::linearalgebra::CompressedRowSparseMatrix MatrixType; + + BezierTriangleMechanicalMapping(core::State* from, core::State* to) + : Inherit(from, to) + , inputTopo(NULL) + , outputTopo(NULL) + , bezierForcefield(NULL) + , normals(initData(&normals, "normals","Node normals at the rest shape")) + , measureError(initData(&measureError, false, "measureError","Error with high resolution mesh")) + , targetTopology(initLink("targetTopology","Targeted high resolution topology")) + , verticesTarget(OutVecCoord()) // dummy initialization + , trianglesTarget(SeqTriangles()) // dummy initialization + , matrixJ() + , updateJ(false) + { + } + + virtual ~BezierTriangleMechanicalMapping() + { + } + + void init() override; + void reinit() override; + void draw(const core::visual::VisualParams* vparams) override; + + + void apply(const core::MechanicalParams *mparams, Data& out, const Data& in) override; + const sofa::linearalgebra::BaseMatrix* getJ(const core::MechanicalParams * mparams) override; + void applyJ(const core::MechanicalParams *mparams, Data& out, const Data& in) override; + void applyJT(const core::MechanicalParams *mparams, Data& out, const Data& in) override; + void applyJT(const core::ConstraintParams *cparams, Data& out, const Data& in) override; + + +#if 0 + /// For checkJacobian and to hide some deprecation warnings + const sofa::linearalgebra::BaseMatrix* getJ() { return getJ(NULL); } + void applyJ(Data& out, const Data& in) + { applyJ(NULL, out, in); } + void applyJ( OutVecDeriv& out, const InVecDeriv& in) + { + Data dout; + Data din; + *din.beginEdit() = in; + din.endEdit(); + dout.beginEdit()->resize(out.size()); + dout.endEdit(); + applyJ(NULL, dout, din); + out = dout.getValue(); + } + void applyJT(InVecDeriv& out, const OutVecDeriv& in) + { + Data din; + Data dout; + *din.beginEdit() = in; + din.endEdit(); + dout.beginEdit()->resize(out.size()); + dout.endEdit(); + applyJT(NULL, dout, din); + out = dout.getValue(); + } + /// +#endif + + + void handleEvent(sofa::core::objectmodel::Event *event) override { + if (dynamic_cast(event)) + { + //std::cout << "begin\n"; + // We have to update the matrix at every step + updateJ = true; + } + } + +protected: + + BezierTriangleMechanicalMapping() + : Inherit() + , inputTopo(NULL) + , outputTopo(NULL) + , bezierForcefield(NULL) + , normals(initData(&normals, "normals","Node normals at the rest shape")) + , measureError(initData(&measureError, false, "measureError","Error with high resolution mesh")) + , targetTopology(initLink("targetTopology","Targeted high resolution topology")) + , verticesTarget(OutVecCoord()) // dummy initialization + , trianglesTarget(SeqTriangles()) // dummy initialization + , matrixJ() + , updateJ(false) + { + } + + typedef struct { + + // Nodes of the Bézier triangle + sofa::type::fixed_array bezierNodes; + sofa::type::fixed_array bezierNodesV; + + // Segments in the reference frame + Vec3 P0_P1; + Vec3 P0_P2; + Vec3 P1_P2; + Vec3 P1_P0; + Vec3 P2_P0; + Vec3 P2_P1; + + // Contains the list of poinst connected to this triangle + sofa::type::vector attachedPoints; + + } TriangleInformation; + + gl::GLSLShader shader; + + BaseMeshTopology* inputTopo; + BaseMeshTopology* outputTopo; + + // Pointer to the forcefield associated with the input topology + BezierFF* bezierForcefield; + + Data< type::vector > normals; + Data measureError; + SingleLink, + sofa::core::topology::BaseMeshTopology, + BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> targetTopology; + + topology::container::dynamic::TriangleSetTopologyContainer* topologyTarget; + const OutVecCoord verticesTarget; + const SeqTriangles trianglesTarget; + + type::vector colourMapping; + type::vector coloursPerVertex; + type::vector vectorErrorCoarse; + type::vector vectorErrorTarget; + + type::vector triangleInfo; + + std::unique_ptr matrixJ; + bool updateJ; + + // Pointer on the topological mapping to retrieve the list of edges + // XXX: The edges are no longer there!!! + //TriangleSubdivisionTopologicalMapping* triangleSubdivisionTopologicalMapping; + + void HSL2RGB(Vec3 &rgb, Real h, Real sl, Real l); + void MeasureError(); + Real DistanceHausdorff(BaseMeshTopology *topo1, BaseMeshTopology *topo2, type::vector &vectorError); + void ComputeNormals(type::vector &normals); + void FindTriangleInNormalDirection(const InVecCoord& highResVertices, const SeqTriangles highRestriangles, const type::vector &normals); + + // Computes the barycentric coordinates of a vertex within a triangle + void computeBaryCoefs(Vec3 &baryCoefs, const Vec3 &p, const Vec3 &a, const Vec3 &b, const Vec3 &c, bool bConstraint = true); + + // NOTE: The following funcitons return *square* of the distance! + Real FindClosestPoint(unsigned int& closestVerticex, const Vec3& point, const OutVecCoord &inVertices); + Real FindClosestEdge(unsigned int& closestEdge, const Vec3& point, const OutVecCoord &inVertices, const SeqEdges &inEdges); + Real FindClosestTriangle(unsigned int& closestEdge, const Vec3& point, const OutVecCoord &inVertices, const SeqTriangles &inTriangles); + + // Contains the barycentric coordinates of the point within a triangle + sofa::type::vector barycentricCoordinates; +}; + +} // namespace mapping + +} // namespace component + +} // namespace sofa + +#endif diff --git a/src/Shell/mapping/BezierTriangleMechanicalMapping.inl b/src/Shell/mapping/BezierTriangleMechanicalMapping.inl new file mode 100644 index 0000000..80849e2 --- /dev/null +++ b/src/Shell/mapping/BezierTriangleMechanicalMapping.inl @@ -0,0 +1,1478 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#ifndef SOFA_COMPONENT_MAPPING_MESH2POINTMAPPING_INL +#define SOFA_COMPONENT_MAPPING_MESH2POINTMAPPING_INL + +#include +#include +#include + +namespace sofa +{ + +namespace component +{ + +namespace mapping +{ + +using namespace sofa::helper; + +// Returns the skew-symetric matrix for computing a cross-product with the +// vector @x +template +inline void crossMatrix(const Vec<3, Real>& x, Mat<3,3, Real>& m) +{ + m[0][0] = 0; + m[0][1] = -x[2]; + m[0][2] = x[1]; + + m[1][0] = x[2]; + m[1][1] = 0; + m[1][2] = -x[0]; + + m[2][0] = -x[1]; + m[2][1] = x[0]; + m[2][2] = 0; +} + + + +template +void BezierTriangleMechanicalMapping::init() +{ +// std::cout << "BezierTriangleMechanicalMapping::init()" << std::endl; + + *this->f_listening.beginEdit() = true; + this->f_listening.endEdit(); + + if (this->fromModel == NULL) + { + msg_warning() << "Missing input Mechanical state!" ; + return; + } + + if (this->toModel == NULL) + { + msg_warning() << "Missing output Mechanical state!" ; + return; + } + + // Retrieves topology + inputTopo = this->fromModel->getContext()->getMeshTopology(); + outputTopo = this->toModel->getContext()->getMeshTopology(); + + if (!inputTopo || (inputTopo->getNbTriangles() <= 0)) + { + msg_warning() << "BezierTriangleMechanicalMapping requires an input triangular topology" ; + return; + } + + if (!outputTopo || (outputTopo->getNbTriangles() <= 0)) + { + msg_warning() << "BezierTriangleMechanicalMapping requires an output triangular topology" ; + return; + } + + // Retrieve associated Force Field (if available) + this->getContext()->get(bezierForcefield); + if (bezierForcefield) + { + msg_info() << "TriangularBendingForcefield was found" ; + if (normals.getValue().size() != 0) { + msg_warning() << "Ignoring normals, using configuration from force field"; + } + } + else + { + msg_info() << "TriangularBendingForcefield was NOT found" ; + + if (normals.getValue().size() == 0) { + msg_warning() << "No normals defined, assuming flat triangles" ; + } else if (normals.getValue().size() != this->fromModel->read(sofa::core::vec_id::read_access::position)->getValue().size()) { + msg_warning() << "Normals count doesn't correspond with nodes count" ; + return; + } + } + + const OutVecCoord &outVertices = this->toModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + barycentricCoordinates.clear(); + barycentricCoordinates.resize(outVertices.size()); + + // Retrieves 'in' vertices and triangles + const InVecCoord &inVerticesRigid = this->fromModel->read(sofa::core::vec_id::read_access::position)->getValue(); + const InVecCoord &inVerticesRigid0 = this->fromModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + // Conversion to Vec3Types to be able to call same methods used by Hausdorff distance + OutVecCoord inVertices; + for (unsigned int i=0; igetEdges(); + const SeqTriangles &inTriangles = inputTopo->getTriangles(); + + const type::vector& norms = normals.getValue(); + + // Iterates over 'in' triangles + triangleInfo.resize(inTriangles.size()); + for (unsigned int t=0; tgetTrianglesAroundVertex(closestVertex); + if (trianglesAroundVertex.size() <= 0) + { + msg_warning() << "No triangles attached to vertex " << closestVertex ; + which = (minEdge <= minTriangle) ? 1 : 2; + } + else + { + triangleID = trianglesAroundVertex[0]; + triangleInfo[triangleID].attachedPoints.push_back(i); + + // Computes barycentric coordinates within the triangle + computeBaryCoefs(vertexBaryCoord, outVertices[i], + inVertices[ inTriangles[triangleID][0] ], + inVertices[ inTriangles[triangleID][1] ], + inVertices[ inTriangles[triangleID][2] ]); + + // Adds the barycentric coordinates to the list + barycentricCoordinates[i] = vertexBaryCoord; + } + } + + if (which == 1) + { + // If it is an edge, consider one of the triangles attached to it + BaseMeshTopology::TrianglesAroundEdge trianglesAroundEdge = inputTopo->getTrianglesAroundEdge(closestEdge); + if (trianglesAroundEdge.size() <= 0) + { + msg_warning() << "No triangles attached to edge " << closestEdge ; + which = 3; + } + else + { + triangleID = trianglesAroundEdge[0]; + triangleInfo[triangleID].attachedPoints.push_back(i); + + // Computes barycentric coordinates within the triangle + computeBaryCoefs(vertexBaryCoord, outVertices[i], + inVertices[ inTriangles[triangleID][0] ], + inVertices[ inTriangles[triangleID][1] ], + inVertices[ inTriangles[triangleID][2] ]); + + // Adds the barycentric coordinates to the list + barycentricCoordinates[i] = vertexBaryCoord; + } + } + + if (which == 2) + { + // If it is a triangle, consider it + triangleInfo[closestTriangle].attachedPoints.push_back(i); + + // Computes barycentric coordinates within each triangles + computeBaryCoefs(vertexBaryCoord, outVertices[i], + inVertices[ inTriangles[closestTriangle][0] ], + inVertices[ inTriangles[closestTriangle][1] ], + inVertices[ inTriangles[closestTriangle][2] ]); + + // Adds the barycentric coordinates to the list + barycentricCoordinates[i] = vertexBaryCoord; + } + } + + +#if 0 + // Retrieves topological mapping to get list of edges (for contour rendering) + triangleSubdivisionTopologicalMapping = NULL; + // this->getContext()->get(triangleSubdivisionTopologicalMapping, nameHighTopology.getValue(), sofa::core::objectmodel::BaseContext::SearchRoot); + this->getContext()->get(triangleSubdivisionTopologicalMapping); + if (!triangleSubdivisionTopologicalMapping) + { + // This is not fatal + msg_warning() << "triangleSubdivisionTopologicalMapping was not found" ; + } +#endif + + // Call of apply() and applyJ() + this->Inherit::init(); + + // Set each colour of each vertex to default + for (unsigned int i=0; imaximum) + { + maximum = fabs(vectorErrorCoarse[i]); + } + } + Real correctedError; + for (unsigned int i=0; i maximum) + correctedError = maximum; + coloursPerVertex[i] = colourMapping[ (int)((correctedError/maximum)*239) ]; + } + } +} + + +template +void BezierTriangleMechanicalMapping::reinit() +{ + msg_info() << "reinit()" ; + init(); +} + + +// Given H,S,L in range of 0-1 +// Returns a RGB colour in range of 0-255 +// http://www.geekymonkey.com/Programming/CSharp/RGB2HSL_HSL2RGB.htm +template +void BezierTriangleMechanicalMapping::HSL2RGB(Vec3 &rgb, Real h, Real sl, Real l) +{ + Real v; + Real r,g,b; + + r = l; // default to gray + g = l; + b = l; + v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl); + if (v > 0) + { + Real m; + Real sv; + int sextant; + Real fract, vsf, mid1, mid2; + + m = l + l - v; + sv = (v - m ) / v; + h *= 6.0; + sextant = (int)h; + fract = h - sextant; + vsf = v * sv * fract; + mid1 = m + vsf; + mid2 = v - vsf; + switch (sextant) + { + case 0: + r = v; + g = mid1; + b = m; + break; + case 1: + r = mid2; + g = v; + b = m; + break; + case 2: + r = m; + g = v; + b = mid1; + break; + case 3: + r = m; + g = mid2; + b = v; + break; + case 4: + r = mid1; + g = m; + b = v; + break; + case 5: + r = v; + g = m; + b = mid2; + break; + } + } + + rgb[0] = r; + rgb[1] = g; + rgb[2] = b; +} + + +template +void BezierTriangleMechanicalMapping::MeasureError() +{ + Real distance1; + msg_info() << "Computing Hausdorff distance high res->coarse" ; + distance1 = DistanceHausdorff(targetTopology.get(), outputTopo, vectorErrorTarget); + msg_info() << "Hausdorff distance between high res mesh and coarse mesh = " << distance1 ; + + Real average = 0; + for (unsigned int i=0; ihigh res" ; + distance2 = DistanceHausdorff(outputTopo, targetTopology.get(), vectorErrorCoarse); + msg_info() << "Hausdorff distance between coarse mesh and high res mesh = " << distance2 ; + + average = 0; + for (unsigned int i=0; igetNbTriangles() <= 0) + { + msg_warning() << "apply() requires an input triangular topology" ; + return; + } + + // List of in triangles + const SeqTriangles& inTriangles = inputTopo->getTriangles(); + + for (unsigned int t=0; tgetTriangleInfo(t); + tinfo.bezierNodes = fftinfo.bezierNodes; + + tinfo.P0_P1 = fftinfo.P0_P1_inFrame0; + tinfo.P0_P2 = fftinfo.P0_P2_inFrame0; + tinfo.P1_P2 = fftinfo.P1_P2_inFrame1; + tinfo.P1_P0 = fftinfo.P1_P0_inFrame1; + tinfo.P2_P0 = fftinfo.P2_P0_inFrame2; + tinfo.P2_P1 = fftinfo.P2_P1_inFrame2; + + } + else + { + // Compute nodes of the Bézier triangle + + // Corners + tinfo.bezierNodes[0] = in[ triangle[0] ].getCenter(); + tinfo.bezierNodes[1] = in[ triangle[1] ].getCenter(); + tinfo.bezierNodes[2] = in[ triangle[2] ].getCenter(); + +#ifdef ROTQ + Quaternion q[3] = { + in[ triangle[0] ].getOrientation(), + in[ triangle[1] ].getOrientation(), + in[ triangle[2] ].getOrientation() }; + +#define BN(i, p, seg) do { \ + tinfo.bezierNodes[(i)] = tinfo.bezierNodes[(p)] + \ + q[(p)].rotate(tinfo.seg); \ +} while (0) + +#else + // Rotation matrices at corner nodes + Mat33 R[3]; + in[ triangle[0] ].getOrientation().toMatrix(R[0]); + in[ triangle[1] ].getOrientation().toMatrix(R[1]); + in[ triangle[2] ].getOrientation().toMatrix(R[2]); + +#define BN(i, p, seg) do { \ + tinfo.bezierNodes[(i)] = tinfo.bezierNodes[(p)] + \ + R[(p)] * tinfo.seg; \ +} while (0) + +#endif + BN(3, 0, P0_P1); + BN(4, 0, P0_P2); + BN(5, 1, P1_P2); + BN(6, 1, P1_P0); + BN(7, 2, P2_P0); + BN(8, 2, P2_P1); + +#undef BN + +#ifdef ROTQ + // Center + tinfo.bezierNodes[9] = + (tinfo.bezierNodes[0] + q[0].rotate( tinfo.P0_P1 + tinfo.P0_P2 ))/3.0 + + (tinfo.bezierNodes[1] + q[1].rotate( tinfo.P1_P0 + tinfo.P1_P2 ))/3.0 + + (tinfo.bezierNodes[2] + q[2].rotate( tinfo.P2_P0 + tinfo.P2_P1 ))/3.0; +#else + // Center + tinfo.bezierNodes[9] = + (tinfo.bezierNodes[0] + R[0]*( tinfo.P0_P1 + tinfo.P0_P2 ))/3.0 + + (tinfo.bezierNodes[1] + R[1]*( tinfo.P1_P0 + tinfo.P1_P2 ))/3.0 + + (tinfo.bezierNodes[2] + R[2]*( tinfo.P2_P0 + tinfo.P2_P1 ))/3.0; +#endif + + } + + // Go through the attached points + for (unsigned int i=0; i > rout = dOut; + // std::cout << "| Out[" << rout.size() << "]: "; + // for (unsigned int i=0; igetNbTriangles() <= 0) + { + msg_warning() << "applyJ() requires an input triangular topology" ; + return; + } + + // List of in triangles + const SeqTriangles& inTriangles = inputTopo->getTriangles(); + //const type::vector& inVertices = *this->fromModel->getX(); + const InVecCoord& inVertices = this->fromModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + // Compute nodes of the Bézier triangle for each input triangle + for (unsigned int t=0; t(in[ triangle[0] ].getVOrientation(), Omega0); + crossMatrix(in[ triangle[1] ].getVOrientation(), Omega1); + crossMatrix(in[ triangle[2] ].getVOrientation(), Omega2); + + Mat33 dR0, dR1, dR2; + + // Rotation matrices at corner nodes + inVertices[ triangle[0] ].getOrientation().toMatrix(dR0); + inVertices[ triangle[1] ].getOrientation().toMatrix(dR1); + inVertices[ triangle[2] ].getOrientation().toMatrix(dR2); + + // Derivatives of the rotation matrix + dR0 = Omega0*dR0; + dR1 = Omega1*dR1; + dR2 = Omega2*dR2; + + // Velocities at other nodes + tinfo.bezierNodesV[3] = tinfo.bezierNodesV[0] + dR0*tinfo.P0_P1; + tinfo.bezierNodesV[4] = tinfo.bezierNodesV[0] + dR0*tinfo.P0_P2; + + tinfo.bezierNodesV[5] = tinfo.bezierNodesV[1] + dR1*tinfo.P1_P2; + tinfo.bezierNodesV[6] = tinfo.bezierNodesV[1] + dR1*tinfo.P1_P0; + + tinfo.bezierNodesV[7] = tinfo.bezierNodesV[2] + dR2*tinfo.P2_P0; + tinfo.bezierNodesV[8] = tinfo.bezierNodesV[2] + dR2*tinfo.P2_P1; + + tinfo.bezierNodesV[9] = ( + (tinfo.bezierNodesV[0] + dR0*(tinfo.P0_P1 + tinfo.P0_P2)) + + (tinfo.bezierNodesV[1] + dR1*(tinfo.P1_P0 + tinfo.P1_P2)) + + (tinfo.bezierNodesV[2] + dR2*(tinfo.P2_P0 + tinfo.P2_P1)) + )/3.0; + + for (unsigned int i=0; iopMulV(out_alloc, in_alloc); + + // Compare results + Real amax = 0; Index maxi=0; + //std::cout << "Delta with getJ():"; + Real dif; + for (unsigned int i=0;i amax) { amax = rabs(dif); maxi = i; } + } + //std::cout << "\n"; + if (amax > 1e-9) + std::cout << "check J: amax=" << amax << " i=" << maxi << " phi=" << + barycentricCoordinates[maxi][0] << "/" << + barycentricCoordinates[maxi][1] << "/" << + barycentricCoordinates[maxi][2] << "\n"; + + // Cleanup + delete[] in_alloc; + delete[] out_alloc; + } +#endif + + // Dump input and output vectors + //{ + // std::cout << "In[" << in.size() << "] : "; + // for (unsigned int i=0; i > rout = dOut; + // std::cout << "Out[" << rout.size() << "]: "; + // for (unsigned int i=0; i + // dOutVertices(*this->toModel->getX()), + // dOutVertices2(*this->toModel->getX()); + + //Data + // dInVertices(*this->fromModel->getX()), + // dInVertices2(*this->fromModel->getX()); + + //helper::WriteAccessor< Data > iv = dInVertices; + //helper::WriteAccessor< Data > iv2 = dInVertices2; + + //for (unsigned int i=0; i > ov = dOutVertices; + //helper::ReadAccessor< Data > ov2 = dOutVertices2; + + //Real amax = 0; Index maxi=0; + ////std::cout << "Dif[" << ov.size() << "]: "; + //for (unsigned int i=0; i amax) { amax = rabs(dif[k]); maxi = i; } } + // //if (i != 0) std::cout << ", "; + // //std::cout << dif; + // //std::cout << out[i]; + //} + ////std::cout << std::endl; + //if (amax > 1e-9) + //std::cout << "amax=" << amax << " i=" << maxi << " phi=" << + // barycentricCoordinates[maxi][0] << "/" << + // barycentricCoordinates[maxi][1] << "/" << + // barycentricCoordinates[maxi][2] << "\n"; + +// stop = timer.getTime(); +// std::cout << "time applyJ = " << stop-start << std::endl; +} + +template +const linearalgebra::BaseMatrix* BezierTriangleMechanicalMapping::getJ(const core::MechanicalParams * /*mparams*/) +{ + //std::cout << "---------------- getJ ----------------------------" << std::endl; + + if (matrixJ.get() == NULL || updateJ) + { + if (!inputTopo || !outputTopo) + { + msg_warning() << "getJ() was called before init()" ; + return NULL; + } + if (inputTopo->getNbTriangles() <= 0) + { + msg_warning() << "getJ() requires an input triangular topology" ; + return NULL; + } + + const OutVecCoord& out = this->toModel->read(sofa::core::vec_id::read_access::position)->getValue(); + const InVecCoord& in = this->fromModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + // Initialize the matrix + if (matrixJ.get() == 0 || + sofa::Size(matrixJ->rowBSize()) != out.size() || + sofa::Size(matrixJ->colBSize()) != in.size()) + { + matrixJ.reset(new MatrixType( + out.size() * NOut, + in.size() * NIn)); + } + else + { + matrixJ->clear(); + } + + Mat33 I(Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)); + + const SeqTriangles& inTriangles = inputTopo->getTriangles(); + const InVecCoord& inVertices = this->fromModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + // Go through all input triangles + for (unsigned int t=0; twblock(pt, triangle[k], true); + Mat33 trans, ang; + block.getsub(0, 0, trans); // Translational DOFS + block.getsub(0, 3, ang); // Angular DOFS + + // Corner node + trans += I * bc[k]*bc[k]*bc[k]; + + //// 3 / 5 / 7 + int l = (k + 1) % 3; + trans += I * 3*bc[k]*bc[k]*bc[l]; + ang += Ap1[k] * 3*bc[k]*bc[k]*bc[l]; + + // 4 / 6 / 8 + l = (k + 2) % 3; + trans += I * 3*bc[k]*bc[k]*bc[l]; + ang += Ap2[k] * 3*bc[k]*bc[k]*bc[l]; + + // Central node + trans += I * 2*bc[0]*bc[1]*bc[2]; // <-- 6/3 = 2 + ang += (Ap1[k] + Ap2[k]) * 2*bc[0]*bc[1]*bc[2]; // <-- 6/3 = 2 + + block.setsub(0, 0, trans); + block.setsub(0, 3, ang); + } + } + } + + } // if (matrixJ.get() == NULL || updateJ) + + return matrixJ.get(); +} + +// Updates positions of the mechanical vertices from visual f(n-1) = JT * fn +template +void BezierTriangleMechanicalMapping::applyJT(const core::MechanicalParams * /*mparams*/, Data& dOut, const Data& dIn) +{ + helper::WriteAccessor< Data > out = dOut; + helper::ReadAccessor< Data > in = dIn; + + //std::cout << "---------------- ApplyJT ----------------------------" << std::endl; + +// sofa::helper::system::thread::ctime_t start, stop; +// sofa::helper::system::thread::CTime timer; +// +// start = timer.getTime(); + + if (!inputTopo || !outputTopo) + { + msg_warning() << "applyJT() was called before init()" ; + return; + } + if (inputTopo->getNbTriangles() <= 0) + { + msg_warning() << "applyJT() requires an input triangular topology" ; + return; + } + +#ifdef CHECK_J + const sofa::linearalgebra::BaseMatrix* J = getJ(NULL); + Real* in_alloc = NULL; + Real* out_alloc = NULL; + if (J != NULL) { + // Prepare in vector + in_alloc = new Real[in.size()*NOut]; + for (unsigned int i=0;igetTriangles(); + const InVecCoord& inVertices = this->fromModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + // Compute nodes of the Bézier triangle for each input triangle + for (unsigned int t=0; t &bn = tinfo.bezierNodes; + + fn = in[pt] * (3*bc[0]*bc[0]*bc[1]); + if (fn != Vec3(0,0,0)) + { + f1 += fn; + f1r += cross((bn[3]-bn[0]), fn); + } + + fn = in[pt] * (3*bc[0]*bc[0]*bc[2]); + if (fn != Vec3(0,0,0)) + { + f1 += fn; + f1r += cross((bn[4]-bn[0]), fn); + } + + fn = in[pt] * (3*bc[1]*bc[1]*bc[2]); + if (fn != Vec3(0,0,0)) + { + f2 += fn; + f2r += cross((bn[5]-bn[1]), fn); + } + + fn = in[pt] * (3*bc[0]*bc[1]*bc[1]); + if (fn != Vec3(0,0,0)) + { + f2 += fn; + f2r += cross((bn[6]-bn[1]), fn); + } + + fn = in[pt] * (3*bc[0]*bc[2]*bc[2]); + if (fn != Vec3(0,0,0)) + { + f3 += fn; + f3r += cross((bn[7]-bn[2]), fn); + } + + fn = in[pt] * (3*bc[1]*bc[2]*bc[2]); + if (fn != Vec3(0,0,0)) + { + f3 += fn; + f3r += cross((bn[8]-bn[2]), fn); + } + + fn = in[pt] * (2*bc[0]*bc[1]*bc[2]); + if (fn != Vec3(0,0,0)) + { + f1 += fn; + f2 += fn; + f3 += fn; + f1r += cross(R[0]*(tinfo.P0_P1 + tinfo.P0_P2), fn); + f2r += cross(R[1]*(tinfo.P1_P2 + tinfo.P1_P0), fn); + f3r += cross(R[2]*(tinfo.P2_P0 + tinfo.P2_P1), fn); + } + + getVCenter(out[ triangle[0] ]) += f1; + getVCenter(out[ triangle[1] ]) += f2; + getVCenter(out[ triangle[2] ]) += f3; + + getVOrientation(out[ triangle[0] ]) += f1r; + getVOrientation(out[ triangle[1] ]) += f2r; + getVOrientation(out[ triangle[2] ]) += f3r; + } + } + + // The following code compares the result with results obtained using + // getJ() because checkJacobian sucks (at this point in time). +#ifdef CHECK_J + if (J != NULL) { + J->opPMulTV(out_alloc, in_alloc); + + // Compare results + Real amax = 0; Index maxi=0; + //std::cout << "Delta with getJT():"; + Real dif; + for (unsigned int i=0;i amax) { amax = rabs(dif); maxi = i; } + } + //std::cout << "\n"; + if (amax > 1e-9) + std::cout << "check JT: amax=" << amax << " i=" << maxi << " phi=" << + barycentricCoordinates[maxi][0] << "/" << + barycentricCoordinates[maxi][1] << "/" << + barycentricCoordinates[maxi][2] << + //" val= " << out_alloc[maxi*NIn+0] << " " << + //out_alloc[maxi*NIn+1] << " " << + //out_alloc[maxi*NIn+2] << " " << + //out_alloc[maxi*NIn+3] << " " << + //out_alloc[maxi*NIn+4] << " " << + //out_alloc[maxi*NIn+5] << " / " << + //out[maxi] << + "\n"; + + // Cleanup + delete[] in_alloc; + delete[] out_alloc; + } +#endif + +// stop = timer.getTime(); +// std::cout << "time applyJT = " << stop-start << std::endl; +} + + +template +void BezierTriangleMechanicalMapping::applyJT(const core::ConstraintParams * /*cparams*/, Data& /*dOut*/, const Data& /*dIn*/) +{ + //msg_warning() << "applyJT(const core::ConstraintParams*, Data&, const Data&) NOT implemented" ; +} + + +template +void BezierTriangleMechanicalMapping::draw(const core::visual::VisualParams* vparams) +{ + if (!inputTopo || !outputTopo) + { + msg_warning() << "draw() was called before init()" ; + return; + } + if (inputTopo->getNbTriangles() <= 0) + { + msg_warning() << "draw() requires an input triangular topology" ; + return; + } + if (outputTopo->getNbTriangles() <= 0) + { + msg_warning() << "draw() requires an output triangular topology" ; + return; + } + + const OutVecCoord &outVertices = this->toModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + if(vparams->displayFlags().getShowVisualModels()) + { + glDisable(GL_LIGHTING); + + const SeqTriangles &outTriangles = outputTopo->getTriangles(); + unsigned int index; + + glEnable(GL_DEPTH_TEST); + glPolygonOffset(1.0, 1.0); + + if(vparams->displayFlags().getShowWireFrame()) + { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glEnable(GL_POLYGON_OFFSET_LINE); + glColor4f(0.0, 0.0, 1.0, 1.0); + glBegin(GL_TRIANGLES); + for (unsigned int i=0; igetSubEdges(); + //const SeqEdges &outEdges = outputTopo->getEdges(); + glColor4f(1.0, 1.0, 1.0, 1.0); + glLineWidth(0.5); + glBegin(GL_LINES); + for (unsigned int i=0; idisplayFlags().getShowMechanicalMappings()) + { + // Render nodes of the Bézier triangles + glPointSize(8); + glDisable(GL_LIGHTING); + glBegin(GL_POINTS); + //for (unsigned int i=0; igetTriangles().size(); i++) + unsigned int i=3; + { + sofa::type::fixed_array &bn = triangleInfo[i].bezierNodes; + for (int j=0; j<10; j++) + { + //glColor4f(0.0, 0.5, 0.3, 1.0); + glColor4f(0.5, 1.0, 0.5, 1.0); + glVertex3f(bn[j][0], bn[j][1], bn[j][2]); + } + } + glEnd(); + glPointSize(1); + + } + // TODO: visualise the mesh +} + + +} // namespace mapping + +} // namespace component + +} // namespace sofa + +#endif diff --git a/src/Shell/misc/Optimize2DSurface.cpp b/src/Shell/misc/Optimize2DSurface.cpp new file mode 100644 index 0000000..3ac6588 --- /dev/null +++ b/src/Shell/misc/Optimize2DSurface.cpp @@ -0,0 +1,21 @@ +// +// Class for optimizing 2D function serving as a core for smoothing +// triangular networks. +// + +#include +#include + +namespace sofa +{ + +#ifndef SOFA_FLOAT +//template class SOFA_SHELLS_API Optimize2DSurface; +template class SOFA_SHELLS_API Optimize2DSurface; +#endif //SOFA_FLOAT +#ifndef SOFA_DOUBLE +//template class SOFA_SHELLS_API Optimize2DSurface; +template class SOFA_SHELLS_API Optimize2DSurface; +#endif //SOFA_DOUBLE + +} diff --git a/src/Shell/misc/Optimize2DSurface.h b/src/Shell/misc/Optimize2DSurface.h new file mode 100644 index 0000000..e8b6aa3 --- /dev/null +++ b/src/Shell/misc/Optimize2DSurface.h @@ -0,0 +1,464 @@ +// +// Class for optimizing 2D function serving as a core for smoothing +// triangular networks. +// + +#ifndef OPTIMIZE2DFUNCTION_H +#define OPTIMIZE2DFUNCTION_H + +#include + +#include +#include + +#include + +#include + +// Return non-zero if triangle with points (a,b,c) is defined in +// counter-clockwise direction. (2D case) +#define CCW(a,b,c) (\ + cross(Vec2(b[0]-a[0], b[1]-a[1]), \ + Vec2(c[0]-a[0], c[1]-a[1])) > 1e-15) + +namespace sofa +{ + +namespace component { namespace controller { template class Test2DAdapter; } } + +/** + * @brief Optimization of real function on 2D surface. + * + * @tparam DataTypes Associated DataType. + */ +template +class Optimize2DSurface +{ + public: + + typedef typename DataTypes::VecCoord VecCoord; + typedef typename DataTypes::VecDeriv VecDeriv; + typedef typename DataTypes::Coord Coord; + typedef typename DataTypes::Deriv Deriv; + typedef typename Coord::value_type Real; + + + typedef sofa::type::Vec<2, Real> Vec2; + typedef sofa::type::Vec<3, Real> Vec3; + + typedef sofa::type::Mat<2,2, Real> Mat22; + typedef sofa::type::Mat<3,3, Real> Mat33; + + typedef sofa::component::topology::EdgeSetTopologyContainer::Edge Edge; + typedef sofa::component::topology::EdgeSetTopologyContainer::EdgesAroundVertex EdgesAroundVertex; + typedef sofa::component::topology::TriangleSetTopologyContainer::TriangleID Index; + typedef sofa::component::topology::TriangleSetTopologyContainer::Triangle Triangle; + typedef sofa::component::topology::TriangleSetTopologyContainer::TrianglesAroundVertex TrianglesAroundVertex; + + typedef sofa::type::vector VecReal; + typedef sofa::type::vector VecVec2; + typedef sofa::type::vector VecVec3; + //typedef sofa::type::vector VecIndex; + + enum { InvalidID = sofa::core::topology::Topology::InvalidID }; + + /** + * @brief Class initialization. + * + * @param adapter Associated Adapter object. + * @param surface Surface parametrization to use for 3D->2D transformation. + */ + Optimize2DSurface( + sofa::component::controller::Test2DAdapter *adapter, + SurfaceParametrization &surface) + : m_adapter(adapter) + , m_topology(NULL) + , m_surf(surface) + {} + + /** + * @brief Compute inital function values. + * + * @param metrics Output vector of values. + * @param topology Associated triangular topology. + */ + void initValues(VecReal &metrics, + sofa::component::topology::TriangleSetTopologyContainer *topology); + + /** + * @brief Perform smoothing step for single point. + * + * @param v Point to move. + * @param newPosition Contains resulting position on return. + * @param tId Triangle in which the new position lies. + * @param metrics Vector of metrics for each triangle. + * @param sigma Minimal improvement in metric to accept a + * change. + * @param precision Precision of optimization based smoothing. + * + * @return True if step leads to an improvement. + */ + bool smooth(Index v, Vec2 &newPosition, Index &tId, VecReal &metrics, + const Real sigma, const Real precision) { + m_sigma = sigma; + m_precision = precision; + //return smoothLaplacian(v, metrics, newPosition, tId); + return smoothOptimizeMax(v, metrics, newPosition, tId); + } + + /** + * @brief Computes distortion metric for a triangle. + * + * @param id Triangle to compute the metric for. + */ + Real funcTriangle(Index id) const { + if (m_topology == NULL) return 0.0; + return funcTriangle(m_topology->getTriangle(id), + m_surf.getPositions(), + m_orientation[id]); + } + + /** + * @brief Computes distortion metric for a triangle. + * + * @param t Triangle to compute the metric for. + * @param x Vertices. + * @param orientation Orientation of the triangle. + */ + Real funcTriangle(const Triangle &t, const VecVec2 &x, bool orientation) const { + return metricInverted(t, x, orientation) * metricGeom(t, x, orientation); + + // TODO: Simple sum is not good enough. Geometrical functionals + // use negative value to designate inverted triangle. This + // information may be lost in the sumation although any inverted + // triangle is worse + // than any non-inverted triangle. + //return metricInverted(t, x, normal) * ( + // 0.05*helper::rsqrt(metricGeom(t, x, normal)) + 0.95*metricDistance(t, x, normal)); + } + + /** + * @brief Distortion metric for a triangle. + * + * @param t Triangle to compute the metric for. + * @param x Vertices. + * @param triID Triangle to relate computation to. + */ + Real metricGeom(const Triangle &t, const Index triID) const { + + return metricGeom(t, m_surf.getPositions(), m_orientation[triID]); + } + + /** + * @brief Distortion metric for a triangle. + * + * @param t Triangle to compute the metric for. + * @param x Vertices. + * @param orientation Orientation of the triangle. + */ + Real metricGeom(const Triangle &t, const VecVec2 &x, + bool /*orientation*/) const { + + //return metricGeomCTS(t, x); + //return metricGeomVL(t, x); + //return metricGeomCTSM(t, x); + return metricGeomVLM(t, x); + } + + + private: + + sofa::component::controller::Test2DAdapter *m_adapter; + sofa::component::topology::TriangleSetTopologyContainer *m_topology; + SurfaceParametrization &m_surf; + + /// Orientation of each triangle. + type::vector m_orientation; + + /// Minimal increase in functional to accept the change + Real m_sigma; + /// Amount of precision that is acceptable for us. + Real m_precision; + + VecVec3 normals; // TODO: to be removed!!! + + // TODO: only for tuning parameters, can be removed later + Real sumgamma, mingamma, maxgamma; + int ngamma; + + + /** + * @brief Constrained Laplacian smoothing. + * + * @param v Vertex to move. + * @param metrics Current metric values for elements. + * @param newPosition New position of the point. + * @param tId Triangle in which the new position lies. + * + * @returns True if new position was computed, in which case + * newPosition contains the new position. + */ + bool smoothLaplacian(Index v, VecReal &metrics, Vec2 &newPosition, + Index &tId); + + /** + * @brief Optimization based smoothing + * + * Optimization based smoothing, + * optimizes towards maximum of the functional. + * + * @param v Vertex to move. + * @param metrics Current metric values for elements. + * @param newPosition New position of the point. + * @param tId Triangle in which the new position lies. + * + * @returns True if new position was computed, in which case + * newPosition contains the new position. + */ + bool smoothOptimizeMax(Index v, VecReal &metrics, Vec2 &newPosition, + Index &tId); + + /** + * @brief Optimization based smoothing + * + * Optimization based smoothing, + * optimizes towards minimum of the functional. + * + * @param v Vertex to move + * @param x Current positions + * @param metrics Current metrice values for elements + * @param normals Original normals (to check for inversion) + */ + bool smoothOptimizeMin(Index v, VecVec3 &x, VecReal &metrics, sofa::type::vector normals); + + /** + * @brief Smoothing based on method of Pain et al. [PUdOG01] + * + * Smoothing based on method of Pain et al. [PUdOG01]. When using this + * method metricGeom3 should also be used. + * + * @param v Vertex to move + * @param x Current positions + * @param metrics Current metrice values for elements + * @param normals Original normals (to check for inversion) + */ + bool smoothPain2D(Index v, VecVec3 &x, VecReal &metrics, sofa::type::vector normals); + + /** + * @brief Detects triangle inversion + * + * @param t Triangle to compute the metric for. + * @param x Vertices. + * @param orientation Orientation of the triangle. + * + * @returns Returns 1 if triangle is OK, -1 if it is inverted. + */ + Real metricInverted(const Triangle &t, const VecVec2 &x, bool orientation) const { + // Is triangle inverted? + bool no = CCW(x[t[0]], x[t[1]], x[t[2]]); + return (orientation == no) ? 1.0 : -1.0; + } + + /** + * @brief Distortion metric for a triangle from [CTS98] (but probably due + * to somebody else) + * + * @param t Triangle to compute the metric for. + * @param x Vertices. + */ + Real metricGeomCTS(const Triangle &t, const VecVec2 &x) const { + // TODO: we can precompute these + Vec2 ab = x[ t[1] ] - x[ t[0] ]; + Vec2 ca = x[ t[0] ] - x[ t[2] ]; + Vec2 cb = x[ t[1] ] - x[ t[2] ]; + + // Normalizing factor so that the matric is 1 in maximum + Real m = 2 * sqrt(3); + m *= helper::rabs(cross(ca, cb)); // || CA × CB || + m /= ca.norm2() + ab.norm2() + cb.norm2(); + + return m; + } + + /** + * @brief Distortion metric for a triangle from [CTS98] with metric tensor + * for anisotropy. + * + * @param t Triangle to compute the metric for. + * @param x Vertices. + */ + Real metricGeomCTSM(const Triangle &t, const VecVec2 &/*x*/) const { + + Real la2 = m_surf.dist2(Edge(t[0], t[1])), + lb2 = m_surf.dist2(Edge(t[1], t[2])), + lc2 = m_surf.dist2(Edge(t[2], t[0])); + + // Normalizing factor so that the matric is 1 in maximum + Real m = 4 * sqrt(3); + + m *= m_surf.area(t); + m /= la2 + lb2 + lc2; + + if (std::isnan(m)) { + std::cerr << "got NaN\n" + << la2 << " " << lb2 << " " << lc2 + << " :: " << m_surf.area(t) << "\n"; + + } + + return m; + } + + /** + * @brief Distortion metric for a triangle from [VL99]. + * + * @param t Triangle to compute the metric for. + * @param x Vertices. + */ + Real metricGeomVL(const Triangle &t, const VecVec2 &x) const { + // TODO: we can precompute these + Vec2 ab = x[ t[1] ] - x[ t[0] ]; + Vec2 ca = x[ t[0] ] - x[ t[2] ]; + Vec2 cb = x[ t[1] ] - x[ t[2] ]; + + Real p = ca.norm() + ab.norm() + cb.norm(); // perimeter + // Normalizing factor so that the matric is 1 in maximum + Real m = 18.0 / helper::rsqrt(3.0); + + m *= helper::rabs(cross(ca, cb)); // || CA × CB || = 2A + m /= p*p; + + Real f; + Real pp = p/3.0, ipp = 1.0/pp; + if (pp < ipp) { + f = (pp * (2.0-pp)); + } else { + f = (ipp * (2.0-ipp)); + } + m *= f * f * f; + + return m; + } + + /** + * @brief Distortion metric for a triangle from [VL99] with metric tensor + * for anisotropy. + * + * @param t Triangle to compute the metric for. + * @param x Vertices. + */ + Real metricGeomVLM(const Triangle &t, const VecVec2 &/*x*/) const { + + Real la = m_surf.dist(Edge(t[0], t[1])), + lb = m_surf.dist(Edge(t[1], t[2])), + lc = m_surf.dist(Edge(t[2], t[0])); + + Real p = la + lb + lc; // perimeter + + // Normalizing factor so that the matric is 1 in maximum + Real m = 36.0 / helper::rsqrt(3.0); + + m *= m_surf.area(t); + m /= p*p; + + Real f; + Real pp = p/3.0, ipp = 1.0/pp; + if (pp < ipp) { + f = (pp * (2.0-pp)); + } else { + f = (ipp * (2.0-ipp)); + } + m *= f * f * f; + + return m; + } + + /** + * @brief Distortion metric for a triangle adapted from [PUdOG01]. + * + * Distortion metric for a triangle adapted from [PUdOG01]. The original + * functional has minimum at 0. We invert it and set maximum to 1. + * + * NOTE: Strangely the minimum (resp. maximum) is not the geometry of + * equilateral triangle. I.e. for triangle with two nodes at (0,0) and + * (1,0) the ideal position of third node is not at (0.5,~0.866) but at + * (0.5,~0.665). Also long flat triangles (with obutese angle) are + * penalized less than small compressed triangles. All in all use of the + * functional is discouraged. + * + * NOTE: The inverted function is not direct equivalent of the original + * version, on the plus side it produces slightly better results. + * + * @param t Triangle to compute the metric for. + * @param x Vertices. + * @param normal Surface normal for the triangle. + */ + Real metricGeomPU(const Triangle &t, const VecVec3 &x, const Vec3 &/*normal*/) const { + // TODO: we can precompute these + Vec3 vab = x[ t[1] ] - x[ t[0] ]; + Vec3 vca = x[ t[0] ] - x[ t[2] ]; + Vec3 vcb = x[ t[1] ] - x[ t[2] ]; + Real ab = vab.norm(); + Real ca = vca.norm(); + Real cb = vcb.norm(); + + // The original functional is defined as: + // F = 1/2 Σ_e (l_e-1)^2 + q^2 + // where l_e is edge length, and + // q = 1/(2 * sqrt(6) * rho) - 1 + // where rho = 2*A/p is radius of incircle. + + Real p = ca + ab + cb; // perimeter + Real area2 = vca.cross(vcb).norm(); // || CA × CB || = 2 * area + + Real m = p / (2.0 * sqrt(6.0) * area2); + m -= 1.0; + m *= m; + + m += ((ab-1.0)*(ab-1.0) + (ca-1.0)*(ca-1.0) + (cb-1.0)*(cb-1.0))/2.0; + + // Transform into function with max at 1 + m = 1.0/(m + 1.0); + + return m; + } + + // TODO: temporary, remove this!!! + //void computeTriangleNormal(const Triangle &t, const VecVec3 &x, Vec3 &normal) const + //{ + // Vec3 A, B; + // A = x[ t[1] ] - x[ t[0] ]; + // B = x[ t[2] ] - x[ t[0] ]; + + // Real An = A.norm(), Bn = B.norm(); + // if (An < 1e-20 || Bn < 1e-20) { + // std::cerr << "Found degenerated triangle: " + // << x[ t[0] ] << " / " << x[ t[1] ] << " / " << x[ t[2] ] + // << " :: " << An << ", " << Bn << "\n"; + + // normal = Vec3(0,0,0); + // return; + // } + + // A /= An; + // B /= Bn; + // normal = cross(A, B); + // normal.normalize(); + //} + + /** + * @brief Get the triangle in a given direction from a point. + * + * @param pId Origin. + * @param dir Direction. + * + * @return Triangle index or InvalidID if not found. + */ + Index getTriangleInDirection(Index pId, const Vec2& dir) const; +}; + +} + +#undef CCW + +#endif // #ifndef OPTIMIZE2DFUNCTION_H diff --git a/src/Shell/misc/Optimize2DSurface.inl b/src/Shell/misc/Optimize2DSurface.inl new file mode 100644 index 0000000..5eafef0 --- /dev/null +++ b/src/Shell/misc/Optimize2DSurface.inl @@ -0,0 +1,686 @@ +// +// Class for optimizing 2D function serving as a core for smoothing +// triangular networks. +// +// NOTE: +// * To track element inversion we either need a priori information about +// orienation, or assume the triangle was originaly not inverted. Now we +// do the latter. +// + +#include +#include + +#include +#include + +#define OTHER(x, a, b) ((x == a) ? b : a) + +// Return non-zero if triangle with points (a,b,c) is defined in +// counter-clockwise direction. +#define CCW(a,b,c) (cross(b-a, c-a) > 1e-15) + +namespace sofa +{ + +template +void Optimize2DSurface::initValues(VecReal &metrics, + sofa::component::topology::TriangleSetTopologyContainer *topology) +{ + m_topology = topology; + + const VecVec2 &x = m_surf.getPositions(); + + normals.resize(m_topology->getNbTriangles()); + m_orientation.resize(m_topology->getNbTriangles()); + + // Compute initial metrics and orientations + for (Index i=0; i < (unsigned int)m_topology->getNbTriangles(); i++) { + const Triangle &t = m_topology->getTriangle(i); + m_orientation[i] = CCW(x[t[0]], x[t[1]], x[t[2]]); + metrics[i] = funcTriangle(t, x, m_orientation[i]); + if (std::isnan(metrics[i])) { + std::cerr << "NaN value for triangle " << i << "\n"; + } + + } +} + +template +bool Optimize2DSurface::smoothLaplacian(Index v, VecReal &metrics, + Vec2 &newPosition, Index &tId) +{ + if (m_topology == NULL) return false; + if (m_adapter == NULL) return false; + + tId = InvalidID; + + const VecVec2 &x = m_surf.getPositions(); + m_surf.storePoint(v); + Vec2 xold = x[v]; + + // Compute new position + EdgesAroundVertex N1e = m_topology->getEdgesAroundVertex(v); + + // Compute centroid of polygon from 1-ring around the vertex + Vec2 xnew(0,0); + for (Index ie=0; iegetEdge(N1e[ie]); + Index id = OTHER(v, e[0], e[1]); + xnew += x[id]; + } + + xnew /= N1e.size(); + + // If it's boundary node project it onto the boundary line + if (m_adapter->isPointBoundary(v)) { // && !pt.isFixed() + // TODO: needs fixing + const Vec3 &bd = m_adapter->getPointBoundary(v); + Vec2 boundary; + boundary = bd; // TODO: keep this info in parameter space + xnew = boundary * (boundary*xnew); + } + + Index targetTri = getTriangleInDirection(v, xnew-x[v]); + if (targetTri == InvalidID) { + std::cerr << __FUNCTION__ << ": Failed to get triangle in direction!" + " Origin: " << v << " at " << x[v] << " ;; target=" << xnew << + "\n"; + return false; + } + m_surf.movePoint(v, xnew, targetTri); + + TrianglesAroundVertex N1 = m_topology->getTrianglesAroundVertex(v); + + // Check if this improves the mesh. + // We accept any change that doesn't decreas worst metric for the + // triangle set. + + bool bAccepted = false; + for (int iter=10; iter>0 && !bAccepted; iter--) { + + if ((xold - x[v]).norm2() < 1e-8) { + // No change in position + //std::cout << "No change in position for " << v << "\n"; + break; + } + + Real oldworst = DBL_MAX, newworst = DBL_MAX; + for (Index it=0; it " << x[v] << "\n"; + // The correct step size is best found empiricaly + //x[v] = (x[v] + xold)/2.0; + m_surf.movePoint(v, (x[v] + xold)*2.0/3.0, targetTri); + } else { + //std::cout << " --accepted: " << xold << " -> " << x[v] << + // " f=" << newworst << "\n"; + bAccepted = true; + } + } + + if (bAccepted) { + // Update metrics + for (Index it=0; it +bool Optimize2DSurface::smoothOptimizeMax(Index v, VecReal &metrics, + Vec2 &newPosition, Index &tId) +{ + if (m_topology == NULL) return false; + if (m_adapter == NULL) return false; + + tId = InvalidID; + + const VecVec2 &x = m_surf.getPositions(); + m_surf.storePoint(v); + Vec2 xold = x[v]; + +#if 1 + // Compute gradients + TrianglesAroundVertex N1 = m_topology->getTrianglesAroundVertex(v); + VecVec2 grad(N1.size()); + Real delta = m_precision/10.0; + for (int component=0; component<2; component++) { + Vec2 xnew = xold; + xnew[component] += delta; + Index targetTri = getTriangleInDirection(v, xnew-x[v]); + if (targetTri == InvalidID) { + // Try opposite direction + delta = -delta; + xnew[component] = xold[component] + delta; + targetTri = getTriangleInDirection(v, xnew-x[v]); + if (targetTri == InvalidID) { + std::cerr << "Problems computing gradients for point " << + v << " (dir=" << component << ")\n"; + + for (Index it=0; it 1e-15) { + imin = it; + mmin = metrics[ N1[it] ]; + } + //std::cout << metrics[ N1[it] ] << "(" << grad[it].norm() << "/" + // << grad[it].norm2()<< "), "; + } + if (imin == InvalidID) { + //std::cout << " doing nothing" << "\n"; + return false; + //} else { + // std::cout << " using " << imin << "\n"; + } + + Vec2 step = grad[imin]; +#else + // + // Minimaze the mean, i.e.: F = 1/n Σ_i f_i + // + // TODO + + Vec3 grad(0.0, 0.0, 0.0); // F = 1/n Σ_i f_i + + // Compute gradients + TrianglesAroundVertex N1 = m_topology->getTrianglesAroundVertex(v); + type::vector grad(N1.size()); + Real delta = m_precision/10.0; + // NOTE: Constrained to 2D! + for (int component=0; component<2; component++) { + x[v] = xold; + x[v][component] += delta; + for (Index it=0; itgetTriangle(N1[it]), x, + triInfo.getValue()[ N1[it] ].normal); + grad[it][component] = (m - metrics[ N1[it] ])/delta; + } + } + Vec3 step = ...; +#endif + + // Find out step size + Real gamma = 0.01; // ≈ m_precision * 2^10 + + //gamma *= step.norm(); + step.normalize(); + + // Note: The following method from [CTS98] underestimates the value and + // leads to slow convergence. It is ok to start with large value, we + // verify the benefit later anyway. + //for (Index it=0; it 0) + // continue; + // Real tmp = (metrics[ N1[it] ] - metrics[ N1[imin] ]) / ( + // 1.0 - dot(grad[it], step)); // dot(step, step) == 1 for unit step vector + // assert(tmp > 0.0); + // //if (tmp < 0.0) { + // // std::cout << "Eeeks! gamma=" << tmp << " partials:\n" + // // << "grad[imin] = " << grad[imin] << "\n" + // // << "grad[it] = " << grad[it] << "\n" + // // << "m = " << metrics[ N1[it] ] << "\n" + // // << "m' = " << metrics[ N1[imin] ] << "\n"; + // //} + // if (tmp < gamma) { + // gamma = tmp; + // } + //} + // Fixed the previous + //for (Index it=0; it 0) + // continue; + // Real tmp = (metrics[ N1[imin] ] - metrics[ N1[it] ]) / + // dot(grad[it], step); + // assert(tmp > 0.0); + // //if (tmp < 0.0) { + // // std::cout << "Eeeks! gamma=" << tmp << " partials:\n" + // // << "grad[imin] = " << grad[imin] << "\n" + // // << "grad[it] = " << grad[it] << "\n" + // // << "m = " << metrics[ N1[it] ] << "\n" + // // << "m' = " << metrics[ N1[imin] ] << "\n"; + // //} + // if (tmp < gamma) { + // gamma = tmp; + // } + //} + //std::cout << "gamma=" << gamma << " grad=" << step << "\n"; + + // If it's boundary node project it onto the boundary line + if (m_adapter->isPointBoundary(v)) return false; + //if (m_adapter->isPointBoundary(v)) { // && !pt.isFixed() + // // TODO: needs fixing + // const Vec3 &bd = m_adapter->getPointBoundary(v); + // Vec2 boundary; + // boundary = bd; // TODO: keep this info in parameter space + // step = boundary * (boundary*step); + //} + + + Vec2 xnew = xold + gamma*step; + Index targetTri = getTriangleInDirection(v, xnew-x[v]); + tId = targetTri; + m_surf.movePoint(v, xnew, targetTri); + // Find out the target triangle to avoid repeted checks + targetTri = getTriangleInDirection(v, -step); + + // Check if this improves the mesh + // + // Note: To track element inversion we either need a normal computed + // from vertex normals, or assume the triangle was originaly not + // inverted. Now we do the latter. + // + // We accept any change that doesn't decrease worst metric for the + // triangle set. + + bool bAccepted = false; + for (int iter=10; iter>0 && !bAccepted; iter--) { + + if ((xold - x[v]).norm2() < m_precision) { + // No change in position + //std::cout << "No change in position for " << v << "\n"; + break; + } + + Real oldworst = DBL_MAX, newworst = DBL_MAX; + for (Index it=0; it " << x[v] + // << " worst: " << oldworst << " -> " << newworst + // << " (" << (newworst-oldworst) << ")\n"; + //x[v] = (x[v] + xold)/2.0; + gamma *= 2.0/3.0; + //gamma /= 2.0; + xnew = xold + gamma*step; + m_surf.movePoint(v, xnew, targetTri); + } else { + //std::cout << " --accepted: " << xold << " -> " << x[v] + // << " gamma=" << gamma << "\n" + // << " worst: " << oldworst << " -> " << newworst + // << " (" << (newworst-oldworst) << ")\n"; + sumgamma += gamma; ngamma++; + if (gamma < mingamma) mingamma = gamma; + if (gamma > maxgamma) maxgamma = gamma; + bAccepted = true; + } + } + + if (bAccepted) { + // Update metrics + for (Index it=0; it +bool Optimize2DSurface::smoothOptimizeMin(Index /*v*/, VecVec3 &/*x*/, VecReal &/*metrics*/, sofa::type::vector /*normals*/) +{ + return false; +#if 0 // Needs fixing! + if (m_topology == NULL) return false; + + Vec3 xold = x[v]; + + // Compute gradients + TrianglesAroundVertex N1 = m_topology->getTrianglesAroundVertex(v); + type::vector grad(N1.size()); + Real delta = 1e-5; + // NOTE: Constrained to 2D! + for (int component=0; component<2; component++) { + x[v] = xold; + x[v][component] += delta; + for (Index it=0; itgetTriangle(N1[it]), x, + normals[N1[it]]); + grad[it][component] = (m - metrics[ N1[it] ])/delta; + } + } + //std::cout << "grads: " << grad << "\n"; + + // Find largest metric with non-zero gradient + Index imax = 0; + Real mmax = 0.0; + //std::cout << "metrics: "; + for (Index it=0; it mmax && grad[it].norm2() > 1e-15) { + imax = it; + mmax = metrics[ N1[it] ]; + } + //std::cout << metrics[ N1[it] ] << "(" << grad[it].norm() << "/" + // << grad[it].norm2()<< "), "; + } + //std::cout << "\n"; + + Vec3 step = grad[imax]; + + // Find out step size + Real gamma = -0.05; + + //gamma *= step.norm(); + step.normalize(); + + // Note: The following method from [CTS98] underestimates the value and + // leads to slow convergence. It is ok to start with large value, we + // verify the benefit later anyway. + //for (Index it=0; it 0) + // continue; + // Real tmp = (metrics[ N1[it] ] - metrics[ N1[imax] ]) / ( + // 1.0 - dot(grad[it], step)); // dot(step, step) == 1 for unit step vector + // assert(tmp > 0.0); + // //if (tmp < 0.0) { + // // std::cout << "Eeeks! gamma=" << tmp << " partials:\n" + // // << "grad[imax] = " << grad[imax] << "\n" + // // << "grad[it] = " << grad[it] << "\n" + // // << "m = " << metrics[ N1[it] ] << "\n" + // // << "m' = " << metrics[ N1[imax] ] << "\n"; + // //} + // if (tmp < gamma) { + // gamma = tmp; + // } + //} + // Fixed the previous + //for (Index it=0; it 0) + // continue; + // Real tmp = (metrics[ N1[imax] ] - metrics[ N1[it] ]) / + // dot(grad[it], step); + // assert(tmp > 0.0); + // //if (tmp < 0.0) { + // // std::cout << "Eeeks! gamma=" << tmp << " partials:\n" + // // << "grad[imax] = " << grad[imax] << "\n" + // // << "grad[it] = " << grad[it] << "\n" + // // << "m = " << metrics[ N1[it] ] << "\n" + // // << "m' = " << metrics[ N1[imax] ] << "\n"; + // //} + // if (tmp < gamma) { + // gamma = tmp; + // } + //} + //std::cout << "gamma=" << gamma << " grad=" << step << "\n"; + x[v] = xold + gamma*step; + + // Check if this improves the mesh + // + // Note: To track element inversion we either need a normal computed + // from vertex normals, or assume the triangle was originaly not + // inverted. Now we do the latter. + // + // We accept any change that doesn't decreas worst metric for the + // triangle set. + + bool bAccepted = false; + for (int iter=10; iter>0 && !bAccepted; iter--) { + + if ((xold - x[v]).norm2() < 1e-8) { + // No change in position + //std::cout << "No change in position for " << v << "\n"; + break; + } + + //Real oldworst = 1.0, newworst = 1.0; + Real oldworst = 0.0, newworst = 0.0; + for (Index it=0; itgetTriangle(N1[it]), x, + normals[ N1[it] ]); + + if (metrics[N1[it]] > oldworst) { + oldworst = metrics[N1[it]]; + } + + if (newmetric > newworst) { + newworst = newmetric; + } + } + //std::cout << "cmp: " << newworst << " vs. " << oldworst << "\n"; + if (newworst > (oldworst - m_sigma)) { + //std::cout << " --rejected " << xold << " -> " << x[v] + // << " worst: " << oldworst << " -> " << newworst + // << " (" << (newworst-oldworst) << ")\n"; + //x[v] = (x[v] + xold)/2.0; + gamma *= 2.0/3.0; + //gamma /= 2.0; + x[v] = xold + gamma*step; + } else { + //std::cout << " --accepted: " << xold << " -> " << x[v] + // << " gamma=" << gamma << "\n" + // << " worst: " << oldworst << " -> " << newworst + // << " (" << (newworst-oldworst) << ")\n"; + sumgamma += gamma; ngamma++; + if (gamma < mingamma) mingamma = gamma; + if (gamma > maxgamma) maxgamma = gamma; + bAccepted = true; + } + } + + if (bAccepted) { + // Update metrics + for (Index it=0; itgetTriangle(N1[it]), x, + normals[ N1[it] ]); + } + } + // NOTE: Old position restore by caller (if needed). + + return bAccepted; +#endif +} + +template +bool Optimize2DSurface::smoothPain2D(Index /*v*/, VecVec3 &/*x*/, VecReal &/*metrics*/, sofa::type::vector /*normals*/) +{ + return false; +#if 0 // Needs fixing! + if (m_topology == NULL) return false; + + Real w = 0.5, m_sigma = 0.01; + + Vec3 xold = x[v]; + Vec2 old2 = Vec2(xold[0], xold[1]); + + Mat22 A; + Vec2 q; + + EdgesAroundVertex N1e = m_topology->getEdgesAroundVertex(v); + for (Index ie=0; iegetEdge(N1e[ie]); + + Mat22 M(Vec2(1.0,0.0), Vec2(0.0,1.0)); + //Mat22 M = Mlist[ N1e[ie] ]; + + Vec3 other = x[ OTHER(v, e[0], e[1]) ]; + + A += M; + q += M * Vec2(other[0], other[1]); + } + + Mat22 D; + for (int n=0; n<2; n++) { + // D_jj = max { A_jj , (1+s) ¿_m!=j |A_jm| } + Real offdiag = (1.0+m_sigma) * helper::rabs(A[n][(n+1)%2]); + if (A[n][n] < offdiag) { + D[n][n] = offdiag; + } else { + D[n][n] = A[n][n]; + } + } + + // Solve: (D + A)(xnew - old2) = w(q - A old2) + // ==> new = (D + A)^{-1} w (q - A old2) + old2 + Mat22 DAi; + DAi.invert(D+A); + Vec2 xnew = (q - A * old2) * w; + xnew = DAi * xnew; + xnew += old2; + + x[v] = Vec3(xnew[0], xnew[1], 0.0); + + // Check if this improves the mesh + // + // Note: To track element inversion we either need a normal computed + // from vertex normals, or assume the triangle was originaly not + // inverted. Now we do the latter. + // + // We accept any change that doesn't decreas worst metric for the + // triangle set. + + TrianglesAroundVertex N1 = m_topology->getTrianglesAroundVertex(v); + + bool bAccepted = false; + for (int iter=1; iter>0 && !bAccepted; iter--) { + + if ((xold - x[v]).norm2() < 1e-8) { + // No change in position + //std::cout << "No change in position for " << v << "\n"; + break; + } + + Real oldworst = 1.0, newworst = 1.0; + for (Index it=0; itgetTriangle(N1[it]), x, + normals[ N1[it] ]); + + if (metrics[N1[it]] < oldworst) { + oldworst = metrics[N1[it]]; + } + + if (newmetric < newworst) { + newworst = newmetric; + } + } + //std::cout << "cmp: " << newworst << " vs. " << oldworst << "\n"; + if (newworst < (oldworst + m_sigma)) { + //std::cout << " --rejected " << xold << " -> " << x[v] + // << " worst: " << oldworst << " -> " << newworst + // << " (" << (newworst-oldworst) << ")\n"; + x[v] = (x[v] + xold)/2.0; + } else { + //std::cout << " --accepted: " << xold << " -> " << x[v] + // << " gamma=" << gamma << "\n"; + // << " worst: " << oldworst << " -> " << newworst + // << " (" << (newworst-oldworst) << ")\n"; + bAccepted = true; + } + } + + if (bAccepted) { + // Update metrics + for (Index it=0; itgetTriangle(N1[it]), x, + normals[ N1[it] ]); + } + } + // NOTE: Old position restore by caller (if needed). + + return bAccepted; +#endif +} + +template +typename Optimize2DSurface::Index +Optimize2DSurface::getTriangleInDirection(Index pId, + const Vec2& dir) const +{ + const VecVec2 &x = m_surf.getPositions(); + + const TrianglesAroundVertex &N1 = m_topology->getTrianglesAroundVertex(pId); + for (unsigned int i=0; i < N1.size(); i++) + { + Index tId = N1[i]; + const Triangle &t = this->m_topology->getTriangle(tId); + + Vec2 e1, e2; + if (t[0] == pId) { + e1 = x[t[1]]-x[t[0]]; + e2 = x[t[2]]-x[t[0]]; + } else if (t[1] == pId) { + e1 = x[t[2]]-x[t[1]]; + e2 = x[t[0]]-x[t[1]]; + } else { + e1 = x[t[0]]-x[t[2]]; + e2 = x[t[1]]-x[t[2]]; + } + + Vec2 n1, n2; + if (CCW(x[t[0]], x[t[1]], x[t[2]])) { + // 90° left + n1[0] = -e1[1]; n1[1] = e1[0]; + n2[0] = -e2[1]; n2[1] = e2[0]; + } else { + // 90° right + n1[0] = e1[1]; n1[1] = -e1[0]; + n2[0] = e2[1]; n2[1] = -e2[0]; + } + + if (((dir*n1) >= -1e-15) && ((dir*n2) < 1e-15)) { + return tId; + } + } + return InvalidID; +} + +} // namespace sofa + +#undef OTHER +#undef CCW diff --git a/src/Shell/misc/PointProjection.cpp b/src/Shell/misc/PointProjection.cpp new file mode 100644 index 0000000..841cd1c --- /dev/null +++ b/src/Shell/misc/PointProjection.cpp @@ -0,0 +1,14 @@ +// +// Class for performing projection of points onto triangular surface +// + +#include +#include + +namespace sofa +{ + + +template class SOFA_SHELL_API PointProjection; + +} diff --git a/src/Shell/misc/PointProjection.h b/src/Shell/misc/PointProjection.h new file mode 100644 index 0000000..5eaecf0 --- /dev/null +++ b/src/Shell/misc/PointProjection.h @@ -0,0 +1,178 @@ +// +// Class for performing projection of points onto triangular surface +// + +#ifndef POINTPROJECTION_H +#define POINTPROJECTION_H + +#include + +#include +#include + +#include +#include + + +namespace sofa +{ + +/** + * @brief Projection of points onto 3D triangular surface. + * + * @tparam Real Real type to use. + */ +template +class PointProjection +{ + + public: + typedef sofa::type::Vec<2, Real> Vec2; + typedef sofa::type::Vec<3, Real> Vec3; + + typedef sofa::type::Mat<3,3, Real> Mat33; + + typedef sofa::component::topology::container::dynamic::TriangleSetTopologyContainer::Edge Edge; + typedef sofa::component::topology::container::dynamic::TriangleSetTopologyContainer::TriangleID Index; + typedef sofa::component::topology::container::dynamic::TriangleSetTopologyContainer::Triangle Triangle; + typedef sofa::component::topology::container::dynamic::TriangleSetTopologyContainer::TrianglesAroundVertex TrianglesAroundVertex; + typedef sofa::component::topology::container::dynamic::TriangleSetTopologyContainer::TrianglesAroundEdge TrianglesAroundEdge; + typedef sofa::component::topology::container::dynamic::TriangleSetTopologyContainer::EdgesInTriangle EdgesInTriangle; + typedef sofa::component::topology::container::dynamic::TriangleSetTopologyContainer::SeqEdges SeqEdges; + typedef sofa::component::topology::container::dynamic::TriangleSetTopologyContainer::SeqTriangles SeqTriangles; + + typedef sofa::type::vector VecVec3; + typedef sofa::type::vector VecIndex; + + enum { InvalidID = sofa::core::topology::Topology::InvalidID }; + + /** + * @brief Class initialization. + * + * @param _topology Associated triangular topology. + */ + PointProjection(sofa::component::topology::container::dynamic::TriangleSetTopologyContainer &_topology) : + topology(_topology) {} + + + /** + * @brief Find a projection of a point on the surface. + * + * @param baryCoords Barycentric coordinates of projected point. + * @param triangleID Triangle on which the point is projected. Or + * InvalidID if projection fails. + * @param point Point to project. + * @param x Current positions of points in the topology. + */ + void ProjectPoint(Vec3 &baryCoords, Index &triangleID, + const Vec3 &point, const VecVec3 &x); + + /** + * @brief Find a projection of a point on the surface of defined set of + * triangles. + * + * @param baryCoords Barycentric coordinates of projected point. + * @param triangleID Triangle on which the point is projected. Or + * InvalidID if projection fails. + * @param point Point to project. + * @param x Current positions of points in the topology. + * @param triangleList Indices of triangle to consider for projection. + */ + void ProjectPoint(Vec3 &baryCoords, Index &triangleID, + const Vec3 &point, const VecVec3 &x, const VecIndex &triangleList); + + /** + * + * Compute barycentric coordinates of a point. + * + * Compute barycentric coordinates of point p in triangle whose + * vertices are a, b and c. If bConstraint is true constraint the + * coordinates to lie inside the triangle. + * + * @param baryCoords The barycentric coordinates. + * @param a Position of first triangle point. + * @param b Position of second triangle point. + * @param c Position of third triangle point. + * @param bConstraint Constraint coordinates to lie inside triangle. + */ + static void ComputeBaryCoords( + Vec3 &baryCoords, const Vec3 &p, + const Vec3 &a, const Vec3 &b, const Vec3 &c, bool bConstraint=true); + + /** + * + * Compute barycentric coordinates of a point (2D case). + * + * Compute barycentric coordinates of point p in triangle whose + * vertices are a, b and c. If bConstraint is true constraint the + * coordinates to lie inside the triangle. + * + * @param baryCoords The barycentric coordinates. + * @param a Position of first triangle point. + * @param b Position of second triangle point. + * @param c Position of third triangle point. + * @param bConstraint Constraint coordinates to lie inside triangle. + */ + static void ComputeBaryCoords( + Vec3 &baryCoords, const Vec2 &p, + const Vec2 &a, const Vec2 &b, const Vec2 &c, bool bConstraint=true); + + private: + + sofa::component::topology::container::dynamic::TriangleSetTopologyContainer &topology; + + public: + + /** + * @brief Finds the closest point to a point. + * + * @param closestVertex Index of the closest point. + * @param point Position of the point. + * @param inVertices Positions of points in input set. + * + * @return Square of the distance to the closest vertex. + */ + Real FindClosestPoint(Index& closestVertex, + const Vec3& point, const VecVec3 &inVertices); + + /** + * @brief Finds the closest edge to a point. + * + * @param closestEdge Index of the closest edge. + * @param point Position of the point. + * @param inVertices Positions of points in input set. + * @param inEdges LIst of edges. + * + * @return Square of the distance to the closest edge. + */ + Real FindClosestEdge(Index& closestEdge, + const Vec3& point, const VecVec3 &inVertices, + const SeqEdges &inEdges); + + /** + * @brief Finds the closest triangle to a point. + * + * @param closestTriangle Index of the closest triangle. + * @param point Position of the point. + * @param inVertices Positions of points in input set. + * @param inEdges LIst of triangles. + * + * @return Square of the distance to the closest triangle. + */ + Real FindClosestTriangle(Index& closestTriangle, + const Vec3& point, const VecVec3 &inVertices, + const SeqTriangles &inTriangles); + + private: + void ProjectPoint( + Vec3 &baryCoords, Index &triangleID, + const Vec3 &point, const VecVec3 &x, + Index closestVertex, Index closestEdge, Index closestTriangle, + Real minVertex, Real minEdge, Real minTriangle); + + +}; + +} + +#endif // #ifndef POINTPROJECTION_H diff --git a/src/Shell/misc/PointProjection.inl b/src/Shell/misc/PointProjection.inl new file mode 100644 index 0000000..61e9932 --- /dev/null +++ b/src/Shell/misc/PointProjection.inl @@ -0,0 +1,387 @@ +// +// Class for performing projection of points onto triangular surface +// + +#include + +#include + +namespace sofa +{ + +template +void PointProjection::ProjectPoint(Vec3 &baryCoords, Index &triangleID, + const Vec3 &point, const VecVec3 &x) +{ + Index closestVertex, closestEdge, closestTriangle; + Real minVertex, minEdge, minTriangle; + triangleID = InvalidID; + + const SeqEdges &edges = topology.getEdges(); + const SeqTriangles &triangles = topology.getTriangles(); + + // Go over vertices + minVertex = FindClosestPoint(closestVertex, point, x); + + // Go over edges + minEdge = FindClosestEdge(closestEdge, point, x, edges); + + // Go over triangles + minTriangle = FindClosestTriangle(closestTriangle, point, x, triangles); + + ProjectPoint(baryCoords, triangleID, point, x, + closestVertex, closestEdge, closestTriangle, + minVertex, minEdge, minTriangle); +} + +// -------------------------------------------------------------------------------------- +template +void PointProjection::ProjectPoint(Vec3 &baryCoords, Index &triangleID, + const Vec3 &point, const VecVec3 &x, const VecIndex &triangleList) +{ + Index closestVertex=InvalidID, closestEdge=InvalidID, closestTriangle=InvalidID; + Real minVertex, minEdge=1e12, minTriangle; + triangleID = InvalidID; + + std::map subPointsMap; + VecIndex edgesID; + SeqEdges edges; + SeqTriangles triangles(triangleList.size()); + + for (unsigned int i=0; i < triangleList.size(); i++) { + triangles[i] = topology.getTriangle(triangleList[i]); + + EdgesInTriangle elist = topology.getEdgesInTriangle(triangleList[i]); + + edgesID.push_back(elist[0]); + Edge e = topology.getEdge(elist[0]); + edges.push_back(e); + subPointsMap[e[0]] = true; subPointsMap[e[1]] = true; + + edgesID.push_back(elist[1]); + e = topology.getEdge(elist[1]); + edges.push_back(e); + subPointsMap[e[0]] = true; subPointsMap[e[1]] = true; + + edgesID.push_back(elist[2]); + e = topology.getEdge(elist[2]); + edges.push_back(e); + subPointsMap[e[0]] = true; subPointsMap[e[1]] = true; + } + + VecVec3 subPoints; + VecIndex subPointsID; + for (std::map::const_iterator i=subPointsMap.begin(); + i != subPointsMap.end(); i++) { + subPoints.push_back(x[i->first]); + subPointsID.push_back(i->first); + } + + // Go over vertices + minVertex = FindClosestPoint(closestVertex, point, subPoints); + if (closestVertex != InvalidID) { + closestVertex = subPointsID[closestVertex]; + } + + // Go over edges + minEdge = FindClosestEdge(closestEdge, point, x, edges); + if (closestEdge != InvalidID) { + closestEdge = edgesID[closestEdge]; + } + + // Go over triangles + minTriangle = FindClosestTriangle(closestTriangle, point, x, triangles); + if (closestTriangle != InvalidID) { + closestTriangle = triangleList[closestTriangle]; + } + + ProjectPoint(baryCoords, triangleID, point, x, + closestVertex, closestEdge, closestTriangle, + minVertex, minEdge, minTriangle); +} + +// ----------------------------------------------------------------------------- + +// Do some magic to constraint the coordinates inside the triangle +// the requirements are: +// coef_a, coef_b, coef_c ≥ 0 +// coef_a + coef_b + coef_c = 1 +template +//void ConstraintBaryCoords(Real &coef_a, Real &coef_b, Real &coef_c) +void ConstraintBaryCoords(sofa::type::Vec<3, Real> &baryCoords) + +{ + if (baryCoords[0] < 0.0) baryCoords[0] = 0.0; + if (baryCoords[1] < 0.0) baryCoords[1] = 0.0; + baryCoords[2] = 1.0 - (baryCoords[0] + baryCoords[1]); + if (baryCoords[2] < 0.0) + { + // We have to be carefull so as not to overshoot some other + // coefficient + if (baryCoords[0] < -baryCoords[2]/2.0) { + baryCoords[2] += baryCoords[0]; + baryCoords[1] += baryCoords[2]; + baryCoords[0] = 0.0; + } else if (baryCoords[1] < -baryCoords[2]/2.0) { + baryCoords[2] += baryCoords[1]; + baryCoords[0] += baryCoords[2]; + baryCoords[1] = 0.0; + } else { + baryCoords[0] += baryCoords[2]/2.0; + baryCoords[1] += baryCoords[2]/2.0; + } + + baryCoords[2] = 0.0; + } +} + + +// -------------------------------------------------------------------------------------- +template +void PointProjection::ComputeBaryCoords( + Vec3 &baryCoords, + const Vec3 &p, const Vec3 &a, const Vec3 &b, const Vec3 &c, bool bConstraint) +{ + const double ZERO = 1e-20; + + Vec3 M = (Vec3) (b-a).cross(c-a); + double norm2_M = M*(M); + + double coef_a, coef_b, coef_c; + + if(norm2_M < ZERO) // triangle (a,b,c) is flat + { + coef_a = (double) (1.0/3.0); + coef_b = (double) (1.0/3.0); + coef_c = (double) (1.0 - (coef_a + coef_b)); + } + else + { + Vec3 N = M/norm2_M; + + coef_a = N*((b-p).cross(c-p)); + coef_b = N*((c-p).cross(a-p)); + if (bConstraint) + { + coef_c = 1.0 - (coef_a + coef_b); + } + else + { + coef_c = N*((a-p).cross(b-p)); + } + } + + baryCoords[0] = coef_a; + baryCoords[1] = coef_b; + baryCoords[2] = coef_c; + + if (bConstraint) { + ConstraintBaryCoords(baryCoords); + } +} + +// ----------------------------------------------------------------------------- +template +void PointProjection::ComputeBaryCoords( + Vec3 &baryCoords, + const Vec2 &p, const Vec2 &a, const Vec2 &b, const Vec2 &c, bool bConstraint) +{ + Mat33 m; + m(0,0) = 1; m(0,1) = 1; m(0,2) = 1; + m(1,0) = a[0]; m(1,1) = b[0]; m(1,2) = c[0]; + m(2,0) = a[1]; m(2,1) = b[1]; m(2,2) = c[1]; + + Mat33 mi; + mi.invert(m); + + baryCoords = mi * Vec3(1, p[0], p[1]); + if (bConstraint) { + ConstraintBaryCoords(baryCoords); + } +} + +// ----------------------------------------------------------------------------- +template +Real PointProjection::FindClosestPoint(Index& closestVertex, + const Vec3& point, const VecVec3 &inVertices) +{ + Real minimumDistance = 10e12; + for (unsigned int v=0; v +Real PointProjection::FindClosestEdge(Index& closestEdge, + const Vec3& point, const VecVec3 &inVertices, const SeqEdges &inEdges) +{ + Real minimumDistance = 10e12; + for (unsigned int e=0; e= 0 && alpha <= 1) + { + Vec3 P, Q, PQ; + P = point; + Q = pointEdge1 + AB * alpha; + PQ = Q-P; + + Real distance = PQ.norm2(); + if (distance < minimumDistance) + { + // Store the new closest edge + closestEdge = e; + + // Updates the minimum's value + minimumDistance = distance; + } + } + } + + return minimumDistance; +} + + +// ----------------------------------------------------------------------------- +template +Real PointProjection::FindClosestTriangle(Index& closestTriangle, + const Vec3& point, const VecVec3 &inVertices, + const SeqTriangles &inTriangles) +{ + Real minimumDistance = 10e12; + for (unsigned int t=0; t 1e-10)) { + // Point projected onto the plane of the triangle lies outside + // of the triangle. Some vertex or edge will be more + // appropriate. + continue; + } + + Vec3 N = cross(AB, AC); + //Real distance = N*point - N*pointTriangle1; + Real distance = N*(point - pointTriangle1); + distance = distance*distance / N.norm2(); + + if (distance < minimumDistance) + { + // Store the new closest triangle + closestTriangle = t; + + // Updates the minimum's value + minimumDistance = distance; + } + } + + return minimumDistance; +} + +// ----------------------------------------------------------------------------- +template +void PointProjection::ProjectPoint( + Vec3 &baryCoords, Index &triangleID, + const Vec3 &point, const VecVec3 &x, + Index closestVertex, Index closestEdge, Index closestTriangle, + Real minVertex, Real minEdge, Real minTriangle) +{ + const SeqTriangles &triangles = topology.getTriangles(); + + int which = 2; /* 0 vertex, 1 edge, 2 triangle, 3 nothign*/ + + if ((minVertex <= minEdge) && (minVertex <= minTriangle)) + { + which = 0; + } + else if ((minEdge <= minTriangle) && (minEdge <= minVertex)) + { + which = 1; + } + + if (which == 0) + { + // If it is a vertex, consider one of the triangles attached to it + TrianglesAroundVertex trianglesAroundVertex = topology.getTrianglesAroundVertex(closestVertex); + if (trianglesAroundVertex.size() <= 0) + { + std::cerr << "No triangles attached to vertex " << closestVertex << std::endl; + triangleID = InvalidID; + which = (minEdge <= minTriangle) ? 1 : 2; + } + else + { + triangleID = trianglesAroundVertex[0]; + } + } + + if (which == 1) + { + // If it is an edge, consider one of the triangles attached to it + TrianglesAroundEdge trianglesAroundEdge = topology.getTrianglesAroundEdge(closestEdge); + if (trianglesAroundEdge.size() <= 0) + { + std::cerr << "No triangles attached to edge " << closestEdge << std::endl; + triangleID = InvalidID; + } + else + { + triangleID = trianglesAroundEdge[0]; + } + } + + if (which == 2) + { + // If it is a triangle, consider it + triangleID = closestTriangle; + } + + if (triangleID != InvalidID) + { + // Computes barycentric coordinates within the triangle + ComputeBaryCoords(baryCoords, point, + x[ triangles[triangleID][0] ], + x[ triangles[triangleID][1] ], + x[ triangles[triangleID][2] ]); + } +} + + +} diff --git a/src/Shell/misc/SurfaceParametrization.cpp b/src/Shell/misc/SurfaceParametrization.cpp new file mode 100644 index 0000000..ca6eed4 --- /dev/null +++ b/src/Shell/misc/SurfaceParametrization.cpp @@ -0,0 +1,18 @@ +// +// Class for parametrizing 3D surface in 2D domain. +// + +#include +#include + +namespace sofa +{ + +#ifndef SOFA_FLOAT +template class SOFA_SHELLS_API SurfaceParametrization; +#endif //SOFA_FLOAT +#ifndef SOFA_DOUBLE +template class SOFA_SHELLS_API SurfaceParametrization; +#endif //SOFA_DOUBLE + +} diff --git a/src/Shell/misc/SurfaceParametrization.h b/src/Shell/misc/SurfaceParametrization.h new file mode 100644 index 0000000..6338d75 --- /dev/null +++ b/src/Shell/misc/SurfaceParametrization.h @@ -0,0 +1,270 @@ +// +// Class for parametrizing 3D surface in 2D domain. +// +// WARNING: Right now we assume surface of genus 1 (i.e. single boundary). +// The method is very naive and may not work for all topologies. +// + +#ifndef SURFACEPARAMETRIZATION_H +#define SURFACEPARAMETRIZATION_H + +#include +#include + +#include + +#include +#include + +#include + +namespace sofa +{ + +/** + * @brief Parametrization of 3D surface in 2D domain. + * + * Parametrization of 3D surface in 2D domain. Serves also as a base class for + * more specialized projections. + * + * @tparam Real Real type to use. + */ +template +class SurfaceParametrization +{ + public: + + typedef sofa::type::Vec<2, Real> Vec2; + typedef sofa::type::Vec<3, Real> Vec3; + + typedef sofa::type::Mat<2,2, Real> Mat22; + typedef sofa::type::Mat<3,3, Real> Mat33; + + typedef sofa::component::topology::TriangleSetTopologyContainer::Edge Edge; + typedef sofa::component::topology::TriangleSetTopologyContainer::TriangleID Index; + typedef sofa::component::topology::TriangleSetTopologyContainer::Triangle Triangle; + typedef sofa::component::topology::TriangleSetTopologyContainer::TrianglesAroundVertex TrianglesAroundVertex; + typedef sofa::component::topology::TriangleSetTopologyContainer::TrianglesAroundEdge TrianglesAroundEdge; + typedef sofa::component::topology::TriangleSetTopologyContainer::EdgesInTriangle EdgesInTriangle; + typedef sofa::component::topology::TriangleSetTopologyContainer::SeqEdges SeqEdges; + typedef sofa::component::topology::TriangleSetTopologyContainer::SeqTriangles SeqTriangles; + + typedef sofa::type::vector VecVec2; + typedef sofa::type::vector VecVec3; + typedef sofa::type::vector VecIndex; + typedef sofa::type::vector VecMat22; + + enum { InvalidID = sofa::core::topology::Topology::InvalidID }; + + SurfaceParametrization() : m_topology(NULL), m_storedId(InvalidID) {} + + /** + * @brief Initialize parametrization. + * + * @param _topology Topology defining the surface. + * @param x Position of points of the surface. + */ + void init( + sofa::component::topology::TriangleSetTopologyContainer *_topology, + const VecVec3 &x); + + void draw(const core::visual::VisualParams* vparams); + + /** + * @name Hadling of topological changes (also handles point relocation). + * @{ + */ + void pointAdd(unsigned int pointIndex, const sofa::core::topology::Point &elem, + const sofa::type::vector< unsigned int > &ancestors, + const sofa::type::vector< double > &coeffs); + void pointRemove(unsigned int pointIndex); + void pointSwap(unsigned int i1, unsigned int i2); + + /** @} */ + + /** + * @brief Store point parameters for later use. + * + * Store point parameters so they can later be restored. Only one + * point can be stored at a time. + * + * @param pt Index of a point to store. + */ + void storePoint(Index pt) { + m_storedId = pt; + m_storedPos = m_points[pt]; + } + + /** + * @brief Restore previously stored point parameters. + */ + void restorePoint() { + if (m_storedId == InvalidID) return; + m_points[m_storedId] = m_storedPos; + m_storedId = InvalidID; + } + + /** + * @brief Returns position of a point in 3D. + * + * @param position Position in parameter space. + * @param tId Triangle in which the point is. + * @param x Position of surface points in real space. Note + * that it should be same as used in init(). + * + * @return Coordinates of the point in real space. + */ + Vec3 getPointPosition(Vec2 position, Index tId, const VecVec3 &x) const; + + /** + * @brief Positions of points in parameter space. + */ + const VecVec2& getPositions() { return m_points; } + + /** + * @brief Moves point in parameter spac. + * + * @param p Point to move. + * @param newPos Target position. + * @param targetTri Triangle in which the new position lies. + */ + void movePoint(Index p, const Vec2 &newPos, Index targetTri); + + /** + * @name Distances and areas. + * @{ */ + + // NOTE: Big note! The following is (in a strict point of view) invalid + // because it assumes that the metric tensor varies linearly over the + // edge/triangle. For our barycentric mapping however the resulting + // metric space is piecewise-linear (for original metric) or it is + // constant over each triangle (for updated metric). But since + // piecewise-linear space would be difficult to integrate we use linear + // interpolation because it is more versatile than constant metric. + + /** + * @brief Compute length of an edge. + * + * @param e Edge. + */ + Real dist(const Edge &e) const { + return helper::rsqrt(dist2(e)); + } + + /** + * @brief Compute square of the length of an edge. + * + * @param e Edge. + */ + Real dist2(const Edge &e) const { + Vec2 v = m_points[ e[1] ] - m_points[ e[0] ]; + Mat22 M = (m_metrics[ e[1] ] + m_metrics[ e[0] ])/2.0; + return v*(M*v); + } + + /** + * @brief Compute area of the triangle. + * + * @param t Triangle. + */ + Real area(const Triangle &t) const { + + Real area = helper::rabs(cross( + m_points[t[1]] - m_points[t[0]], + m_points[t[2]] - m_points[t[0]]))/2.0; + + // === Reddy. Introduction to the Finite Element Method. 1993 + // n = 1, d = 1 + //const double GX[] = { 1.0/3.0 }; + //const double GY[] = { 1.0/3.0 }; + //const double GW[] = { 1.0 }; + // n = 3, d = 2 + const double GX[] = { 0.5, 0.5, 0.0 }; + const double GY[] = { 0.5, 0.0, 0.5 }; + const double GW[] = { 1.0/3.0, 1.0/3.0, 1.0/3.0 }; + // n = 4, d = 3 + //const double GX[] = { 1.0/3.0, 0.6, 0.2, 0.2 }; + //const double GY[] = { 1.0/3.0, 0.2, 0.6, 0.2 }; + //const double GW[] = { -27.0/48.0, 25.0/48.0, 25.0/48.0, 25.0/48.0 }; + // n = 7, d = 5 + //const double GX[] = { 1.0/3.0, 0.797426985353, 0.101286507323, 0.101286507323, 0.059715871789, 0.470142064105, 0.470142064105 }; + //const double GY[] = { 1.0/3.0, 0.101286507323, 0.797426985353, 0.101286507323, 0.470142064105, 0.059715871789, 0.470142064105 }; + //const double GW[] = { 0.225, 0.125939180544, 0.125939180544, 0.125939180544, 0.132394152788, 0.132394152788, 0.132394152788 }; + // === http://www.electromagnetics.biz/integration.htm + // NOTE: The follwing assume 2A*I ... the weights sum to 0.5 + // n = 6, d = 4 + //const double GX[] = { 0.8168476, 0.09157621, 0.09157621, 0.1081030, 0.4459485, 0.4459485 }; + //const double GY[] = { 0.09157621, 0.8168476, 0.09157621, 0.4459485, 0.1081030, 0.4459485 }; + //const double GW[] = { 0.05497587, 0.05497587, 0.05497587, 0.1116908, 0.1116908, 0.1116908 }; + // n = 12, d = 6 + //const double GX[] = { 0.5014265, 0.2492867, 0.2492867, 0.8738220, 0.06308901, 0.06308901, 0.6365025, 0.6365025, 0.05314505, 0.05314505, 0.3103525, 0.3103525 }; + //const double GY[] = { 0.2492867, 0.5014265, 0.2492867, 0.06308901, 0.8738220, 0.06308901, 0.05314505, 0.3103525, 0.6365025, 0.3103525, 0.6365025, 0.05314505 }; + //const double GW[] = { 0.05839314, 0.05839314, 0.05839314, 0.02542245, 0.02542245, 0.02542245, 0.04142554, 0.04142554, 0.04142554, 0.04142554, 0.04142554, 0.04142554 }; + // n = 27, d = 11 + //const double GX[] = { 0.9352701, 0.03236495, 0.03236495, 0.7612982, 0.1193509, 0.1193509, 0.06922210, 0.5346110, 0.5346110, 0.5933802, 0.2033099, 0.2033099, 0.2020614, 0.3989693, 0.3989693, 0.05017814, 0.05017814, 0.5932012, 0.5932012, 0.3566206, 0.3566206, 0.02102202, 0.02102202, 0.8074890, 0.8074890, 0.1714890, 0.1714890 }; + //const double GY[] = { 0.03236495, 0.9352701, 0.03236495, 0.1193509, 0.7612982, 0.1193509, 0.5346110, 0.06922210, 0.5346110, 0.2033099, 0.5933802, 0.2033099, 0.3989693, 0.2020614, 0.3989693, 0.5932012, 0.3566206, 0.05017814, 0.3566206, 0.05017814, 0.5932012, 0.8074890, 0.1714890, 0.02102202, 0.1714890, 0.02102202, 0.8074890 }; + //const double GW[] = { 0.006829866, 0.006829866, 0.006829866, 0.01809227, 0.01809227, 0.01809227, 0.0004635032, 0.0004635032, 0.0004635032, 0.02966149, 0.02966149, 0.02966149, 0.03857477, 0.03857477, 0.03857477, 0.02616856, 0.02616856, 0.02616856, 0.02616856, 0.02616856, 0.02616856, 0.01035383, 0.01035383, 0.01035383, 0.01035383, 0.01035383, 0.01035383 }; + + const int N=3; + Real I = 0.0; + //std::cout << + // determinant(M1b) << " " << + // determinant(M2b) << " " << + // determinant(M3b) << "\n :: "; + for (int i = 0; i < N; i++) { + Mat22 M = m_metrics[t[0]]*GX[i] + + m_metrics[t[1]]*GY[i] + + m_metrics[t[2]]*(1.0-GX[i]-GY[i]); + I += GW[i] * sqrt(determinant(M)); + //std::cout << determinant(M) << " "; + } + //std::cout << "\n :: " << I << "\n"; + return area*I; + } + + /** + * @brief Return metric tensor of a point inside the triangle. + * + * @param tId Triangle ID. + * @param bary Barycentric coordinates of the point. + * @param M Computed metric tensor. + */ + virtual void getMetricTensor(Index tId, const Vec3 &bary, Mat22 &M) const; + + /** @} */ + + private: + + enum ElementState { + FREE, + BOUNDARY, + FIXED + }; + + /// Topology defining the surface. + sofa::component::topology::TriangleSetTopologyContainer *m_topology; + + /// Positions in the parameter space. + VecVec2 m_points; + + /// Metric tensors (first fundamental form). + VecMat22 m_metrics; + + // Data for initialization. + type::vector ptDone, triDone, triBoundary; + + Index m_storedId; + Vec2 m_storedPos; + //Mat22 m_storedMetric; + + virtual void initMetricTensors(); + void projectPoint(const Index pId, Vec2 &pt, const VecVec3 &x) const; + void computeFrame(Mat33 &frame, const Triangle &t, const VecVec3 &x) const; + ElementState getEdgeState(Index edgeId) const; + ElementState getTriangleState(Index triId) const; + void getAngle(const Vec2 &u, const Vec2 &v, Real &alpha, Real &calpha) const; +}; + +} + +#endif // #ifndef SURFACEPARAMETRIZATION_H diff --git a/src/Shell/misc/SurfaceParametrization.inl b/src/Shell/misc/SurfaceParametrization.inl new file mode 100644 index 0000000..d4c9ac2 --- /dev/null +++ b/src/Shell/misc/SurfaceParametrization.inl @@ -0,0 +1,398 @@ +// +// Class for parametrizing 3D surface in 2D domain. +// +// TODO: +// -- Can we use edge swapping at all? It seems that because of a varying +// metric tensor the topology can become non-conforming. + +#include +#include + +#include + +namespace sofa +{ + +template +void SurfaceParametrization::init( + sofa::component::topology::TriangleSetTopologyContainer *topology, + const VecVec3 &x) +{ + m_topology = topology; + m_points.resize(x.size()); + m_metrics.resize(x.size()); + +#if 0 + for (unsigned int i = 0; i < x.size(); i++) { + m_points[i] = x[i]; + } +#else // Needs fixing! + + if (m_topology == NULL) return; + + ptDone.resize(x.size(), false); + triDone.resize(m_topology->getNbTriangles(), false); + triBoundary.resize(m_topology->getNbTriangles(), false); + type::vector boundary; + Mat33 R; + Triangle t; + + // Pick first triangle arbitrarily and unfold it. + // Let's start with triangle number 0. + Index tId = 0; + t = m_topology->getTriangle(tId); + computeFrame(R, t, x); + //std::cout << "first: " << t << "\n"; + + for (int i = 0; i < 3; i++) { + m_points[t[i]] = R * x[t[i]]; + //std::cout << "point[" << t[i] << "] = " << m_points[t[i]] << "\n"; + ptDone[t[i]] = true; + } + triDone[tId] = true; + // Mark boundary triangles (those that already have two points fixed) + const EdgesInTriangle &elist = m_topology->getEdgesInTriangle(tId); + for (unsigned int i = 0; i < elist.size(); i++) { + boundary.push_back(elist[i]); // Add edges into the queue. + const TrianglesAroundEdge &tpair = m_topology->getTrianglesAroundEdge(elist[i]); + for (unsigned int j = 0; j < tpair.size(); j++) { + triBoundary[tpair[j]] = true; + } + } + triBoundary[tId] = false; + + // Go through the topology + while (boundary.size() > 0) { + //std::cout << "b: " << boundary << "\n"; + //std::cout << "t: " << triBoundary << "\n"; + //std::cout << "p: " << ptDone << "\n"; + + Index eId = boundary[0]; + boundary.erase(boundary.begin()); + + const TrianglesAroundEdge &tpair = m_topology->getTrianglesAroundEdge(eId); + // At most one triangle should be free. + if (tpair.size() > 0 && !triDone[tpair[0]]) { + tId = tpair[0]; + } else if (tpair.size() > 1 && !triDone[tpair[1]]) { + tId = tpair[1]; + } else { + continue; + } + + // Find the free node. + t = m_topology->getTriangle(tId); + Index pId = InvalidID; + for (int i = 0; i < 3; i++) { + if (!ptDone[t[i]]) { + pId = t[i]; + break; + } + } + if (pId == InvalidID) continue; + + projectPoint(pId, m_points[pId], x); + //std::cout << "point[" << pId << "] = " << m_points[pId] << "\n"; + ptDone[pId] = true; + + // Update boundary info. + const TrianglesAroundVertex &N1 = m_topology->getTrianglesAroundVertex(pId); + for (unsigned int i = 0; i < N1.size(); i++) { + tId = N1[i]; + // Check triangle state. + ElementState triState = getTriangleState(tId); + triBoundary[tId] = (triState == BOUNDARY); + triDone[tId] = (triState == FIXED); + if (!triDone[tId]) { + // Check edge state. + EdgesInTriangle elist = m_topology->getEdgesInTriangle(tId); + for (int i = 0; i < 3; i++) { + if (getEdgeState(elist[i]) == FIXED) { + boundary.push_back(elist[i]); + } + } + } + } + + } +#endif + + initMetricTensors(); +} + +template +void SurfaceParametrization::initMetricTensors() +{ + for (unsigned int i = 0; i < m_metrics.size(); i++) { + m_metrics[i].identity(); + } +} + +template +void SurfaceParametrization::projectPoint(const Index pId, Vec2 &outPos, const VecVec3 &x) const +{ + if (m_topology == NULL) return; + + const TrianglesAroundVertex &N1 = m_topology->getTrianglesAroundVertex(pId); + int nTriangles = 0; + outPos = Vec2(0,0); + for (unsigned int i = 0; i < N1.size(); i++) { + if (!triBoundary[N1[i]]) continue; + + Index tId = N1[i]; + const Triangle &t = m_topology->getTriangle(tId); + //std::cout << "t=" << t << "\n"; + Mat33 R; + computeFrame(R, t, x); + Vec2 pts[3]; + for (int j = 0; j < 3; j++) { + pts[j] = R * x[t[j]]; + //std::cout << "1: " << pts[j] << " -- " << R * x[t[j]] << " -- " << x[t[j]] << "\n"; + } + + // Find fixed edge + Index edge = InvalidID; + for (int j = 0; j < 3; j++) { + if (ptDone[t[j]] && ptDone[t[(j+1)%3]]) { + edge = j; + break; + } + } + if (edge == InvalidID) continue; // this shouldn't happen + + // Scale triangle down and center on first node + Real elen1 = (m_points[t[edge]]-m_points[t[(edge+1)%3]]).norm(); + Real elen2 = (pts[edge]-pts[(edge+1)%3]).norm(); + //std::cout << "elen1: " << elen1 << " = " << m_points[t[edge]] << " -- " << m_points[t[(edge+1)%3]] << "\n"; + //std::cout << "elen2: " << elen2 << " = " << pts[edge] << " -- " << pts[(edge+1)%3] << "\n"; + Real scale = elen1/elen2; + for (int j = 0; j < 3; j++) { + //std::cout << "2: " << pts[j] << " -- " << scale * pts[j] << "\n"; + pts[j] *= scale; + } + + // Rotate to match the fixed edge + Vec2 e1 = m_points[t[(edge+1)%3]]-m_points[t[edge]]; + Vec2 e2 = pts[(edge+1)%3]-pts[edge]; + //std::cout << " " << e1 << " x " << e2 << "\n"; + e1.normalize(); + e2.normalize(); + + Real alpha, calpha; + getAngle(e1, e2, alpha, calpha); + Real salpha = sin(alpha); + Mat22 R2 = Mat22(Vec2(calpha, salpha), Vec2(-salpha, calpha)); + + for (int j = 0; j < 3; j++) { + //std::cout << "3: " << pts[j] << " -- " << R2 * pts[j] << "\n"; + pts[j] = R2 * pts[j]; + } + + Vec2 shift = m_points[t[edge]] - pts[edge]; + for (int j = 0; j < 3; j++) { + //std::cout << "4: " << pts[j] << " -- " << shift + pts[j] << "\n"; + pts[j] += shift; + } + + outPos += pts[(edge+2)%3]; + nTriangles++; + break; + } + + outPos /= nTriangles; +} + +template +void SurfaceParametrization::computeFrame(Mat33 &frame, const Triangle &t, const VecVec3 &x) const +{ + Vec3 X1 = x[t[1]] - x[t[0]], + Y1 = x[t[2]] - x[t[0]]; + + // Compute the orthogonal frame directions + Vec3 Y,Z; + Real X1n = X1.norm(), Y1n = Y1.norm(); + if (X1n > 1e-20 && Y1n > 1e-20 && fabs(1-dot(X1,Y1)/(X1n*Y1n)) >1e-20 ) + { + X1.normalize(); + //Y1.normalize(); + Z=cross(X1,Y1); + Z.normalize(); + Y=cross(Z,X1); + } + else + { + std::cerr<<"WARNING : can not compute the rotation frame of the element: " + << x[t[0]] << ", " << x[t[1]] << ", " << x[t[2]] << + " tangent: " << X1 << ", " << Y1 << "\n"; + X1=Vec3(1.0,0.0,0.0); + Y =Vec3(0.0,1.0,0.0); + Z =Vec3(0.0,0.0,1.0); + } + + // Compute the corresponding rotation + frame = Mat33(X1,Y,Z); +} + +template +typename SurfaceParametrization::ElementState SurfaceParametrization::getEdgeState(Index edgeId) const +{ + if (m_topology == NULL) return FREE; + + Edge e = m_topology->getEdge(edgeId); + if (ptDone[e[0]] && ptDone[e[1]]) { + return FIXED; + } + return FREE; +} + +template +typename SurfaceParametrization::ElementState SurfaceParametrization::getTriangleState(Index triId) const +{ + if (m_topology == NULL) return FREE; + + Triangle t = m_topology->getTriangle(triId); + int fixed = 0; + for (int i = 0; i < 3; i++) { + if (ptDone[t[i]]) fixed++; + } + + switch (fixed) { + case 3: return FIXED; + case 2: return BOUNDARY; + default: return FREE; + } +} + +template +void SurfaceParametrization::getAngle(const Vec2 &u, const Vec2 &v, Real &alpha, Real &calpha) const +{ + calpha = u*v; + if (calpha > (Real)1.0 ) { + calpha = 1.0; + alpha = 0.0; + } else if (calpha < (Real)-1.0 ) { + calpha = -1.0; + alpha = M_PI; + } else { + alpha = acos(calpha); + } + + Vec2 w = Vec2(-u[1], u[0]); // Rotated 90° left + Real calpha2 = w*v; + + if (calpha2 < (Real)0.0) { + alpha *= -1.0; + } + + //std::cout << "α = " << alpha << " (" << calpha << "/" << calpha2 << ") || " << u << " x " << v << "\n"; +} + + +template +void SurfaceParametrization::pointAdd(unsigned int pointIndex, const sofa::core::topology::Point &/*elem*/, + const sofa::type::vector< unsigned int > &ancestors, + const sofa::type::vector< double > &coeffs) +{ + //std::cout << "pointAdd(" << pointIndex << ", " << elem << ")\n"; + Vec2 newPt; + for (unsigned int i = 0; i < ancestors.size(); i++) { + newPt += m_points[ ancestors[i] ] * coeffs[i]; + } + + if (m_points.size() <= pointIndex) { + m_points.resize(pointIndex+1); + } + + m_points[pointIndex] = newPt; +} + +template +void SurfaceParametrization::pointRemove(unsigned int pointIndex) +{ + //std::cout << "pointRemove(" << pointIndex << ")\n"; + if (pointIndex == m_storedId) { + m_storedId = InvalidID; + } +} + +template +void SurfaceParametrization::pointSwap(unsigned int i1, unsigned int i2) +{ + //std::cout << "pointSwap(" << i1 << ", " << i2 << ")\n"; + Vec2 tmp = m_points[i1]; + m_points[i1] = m_points[i2]; + m_points[i2] = tmp; + + if (i1 == m_storedId) { + m_storedId = i2; + } else if (i2 == m_storedId) { + m_storedId = i1; + } +} + +template +typename SurfaceParametrization::Vec3 +SurfaceParametrization::getPointPosition(Vec2 position, Index tId, + const VecVec3 &x) const +{ + // Compute barycentric coordinates + Vec3 bary; + const Triangle &t = m_topology->getTriangle(tId); + PointProjection::ComputeBaryCoords(bary, position, + m_points[t[0]], m_points[t[1]], m_points[t[2]]); + + // Compute new position + return (bary[0] * x[t[0]] + + bary[1] * x[t[1]] + + bary[2] * x[t[2]]); +} + +template +void SurfaceParametrization::getMetricTensor(Index /*tId*/, const Vec3 &/*bary*/, Mat22 &M) const +{ + M.identity(); +} + +template +void SurfaceParametrization::movePoint(Index p, const Vec2 &newPos, + Index targetTri) +{ + m_points[p] = newPos; + + // Compute barycentric coordinates + Vec3 bary; + const Triangle &t = m_topology->getTriangle(targetTri); + PointProjection::ComputeBaryCoords(bary, newPos, + m_points[t[0]], m_points[t[1]], m_points[t[2]]); + + // Estimate new metric tensor + getMetricTensor(targetTri, bary, m_metrics[p]); +} + + +template +void SurfaceParametrization::draw(const core::visual::VisualParams* vparams) +{ + if (m_topology == NULL) return; + + vparams->drawTool()->saveLastState(); + vparams->drawTool()->disableLighting(); + + std::vector< sofa::type::Vector3 > points; + sofa::type::Vec4f color(1.0, 1.0, 1.0, 1.0); + + for (int i=0; igetNbTriangles(); ++i) + { + const Triangle &t = m_topology->getTriangle(i); + for (int j=0; j<3; j++) + { + points.push_back(sofa::type::Vector3(m_points[t[j]][0], m_points[t[j]][1], 1.0f)); + points.push_back(sofa::type::Vector3(m_points[t[(j+1)%3]][0], m_points[t[(j+1)%3]][1], 1.0f)); + } + } + vparams->drawTool()->drawLines(points, 1.0, color); + + vparams->drawTool()->restoreLastState(); +} + +} // namespace sofa diff --git a/src/Shell/shells2/fem/BezierShellInterpolation.cpp b/src/Shell/shells2/fem/BezierShellInterpolation.cpp new file mode 100644 index 0000000..1f000de --- /dev/null +++ b/src/Shell/shells2/fem/BezierShellInterpolation.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include + + +namespace sofa +{ + +namespace component +{ + +namespace fem +{ + +using namespace sofa::defaulttype; + + +// Register in the Factory +int BezierShellInterpolationClass = core::RegisterObject("Bezier Shell Interpolation") +.add< BezierShellInterpolation >() +; + +template class SOFA_SHELL_API BezierShellInterpolation; + +} // namespace fem + +} // namespace component + +} // namespace sofa + diff --git a/src/Shell/shells2/fem/BezierShellInterpolation.h b/src/Shell/shells2/fem/BezierShellInterpolation.h new file mode 100644 index 0000000..6f9ca32 --- /dev/null +++ b/src/Shell/shells2/fem/BezierShellInterpolation.h @@ -0,0 +1,334 @@ +// +// Interpolation of Bézier triangles +// +// Author: Tomáš Golembiovský +// +// Copyright: +// +#ifndef SOFA_COMPONENT_FEM_BEZIERSHELLINTERPOLATION_H +#define SOFA_COMPONENT_FEM_BEZIERSHELLINTERPOLATION_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + + +namespace sofa +{ + +namespace component +{ + +namespace fem +{ + +template +class BezierShellInterpolation : public virtual sofa::core::objectmodel::BaseObject +{ + public: + + SOFA_CLASS(SOFA_TEMPLATE(BezierShellInterpolation,DataTypes), sofa::core::objectmodel::BaseObject); + + typedef typename DataTypes::VecCoord VecCoord; + typedef typename DataTypes::VecDeriv VecDeriv; + typedef typename DataTypes::VecReal VecReal; + typedef typename DataTypes::MatrixDeriv MatrixDeriv; + typedef typename DataTypes::Coord Coord; + typedef typename DataTypes::Deriv Deriv; + typedef typename Coord::value_type Real; + + typedef sofa::Index Index; + typedef sofa::core::topology::BaseMeshTopology::TriangleID ElementID; + typedef type::vector VecIndex; + + typedef typename sofa::defaulttype::SolidTypes::Transform Transform; + typedef typename sofa::defaulttype::SolidTypes::SpatialVector SpatialVector; + + typedef sofa::type::Vec<2, Real> Vec2; + typedef sofa::type::Vec<3, Real> Vec3; + typedef sofa::type::Mat<3,3,Real> Mat33; + typedef type::vector VecVec3; + + // These vector is related to Bézier nodes + typedef type::vector > VecVec3d; + + typedef sofa::type::Vec<10,Real> ShapeFunctions; + typedef sofa::type::Vec<10,Index> BTri; + typedef type::vector VecShapeFunctions; + typedef type::vector VecBTri; + + class PointInformation + { + public: + // How are the internal bezier nodes attached to the corners of the triangle + // (valid only for internal edge nodes) + Vec3 segment; + + PointInformation() {} + + /// Output stream + inline friend std::ostream& operator<< ( std::ostream& os, const PointInformation& /*pi*/ ) { return os; } + /// Input stream + inline friend std::istream& operator>> ( std::istream& in, PointInformation& /*pi*/ ) { return in; } + }; + + class PointInfoHandler : public core::topology::TopologyDataHandler > + { + typedef core::topology::TopologyDataHandler > Inherited; + + public: + PointInfoHandler(BezierShellInterpolation *_bsi, core::topology::PointData >* _data) : Inherited(_data), bsi(_bsi) {} + + //void applyCreateFunction( + // unsigned int pointIndex, + // PointInformation &pInfo, + // const sofa::type::vector< unsigned int > &ancestors, + // const sofa::type::vector< double > &coeffs) + //{ applyCreateFunction(pointIndex, pInfo, topology::BaseMeshTopology::InvalidID, ancestors, coeffs); } + + //void applyCreateFunction( + // unsigned int pointIndex, + // PointInformation &pInfo, + // const topology::Point &elem, + // const sofa::type::vector< unsigned int > &ancestors, + // const sofa::type::vector< double > &coeffs); + + void swap( unsigned int i1, unsigned int i2 ); + + protected: + BezierShellInterpolation *bsi; + }; + + class TriangleInformation + { + public: + BTri btri; + + TriangleInformation() {} + + /// Output stream + inline friend std::ostream& operator<< ( std::ostream& os, const TriangleInformation& /*ti*/ ) { return os; } + /// Input stream + inline friend std::istream& operator>> ( std::istream& in, TriangleInformation& /*ti*/ ) { return in; } + }; + + class TriangleInfoHandler : public core::topology::TopologyDataHandler > + { + typedef core::topology::TopologyDataHandler > Inherited; + + public: + TriangleInfoHandler(BezierShellInterpolation *_bsi, core::topology::TriangleData >* _data) : Inherited(_data), bsi(_bsi) {} + + void applyCreateFunction( + unsigned int triIndex, + TriangleInformation &tInfo, + const sofa::type::vector< unsigned int > &ancestors, + const sofa::type::vector< double > &coeffs) + { + applyCreateFunction(triIndex, tInfo, + core::topology::BaseMeshTopology::Triangle(core::topology::BaseMeshTopology::InvalidID, core::topology::BaseMeshTopology::InvalidID, core::topology::BaseMeshTopology::InvalidID), + ancestors, coeffs); + } + + void applyCreateFunction( + unsigned int triIndex, + TriangleInformation &tInfo, + const core::topology::BaseMeshTopology::Triangle &elem, + const sofa::type::vector< unsigned int > &ancestors, + const sofa::type::vector< double > &coeffs); + + protected: + BezierShellInterpolation *bsi; + }; + + Data< sofa::type::vector > > f_interpolationIndices; //output interpolation indices + Data< sofa::type::vector > > f_interpolationValues; //output interpolation values + + BezierShellInterpolation(); + ~BezierShellInterpolation() + { + if(pointHandler) delete pointHandler; + if(triHandler) delete triHandler; + } + + /** + * @brief SceneGraph callback initialization method. + */ + void init() override; + + /** + * @brief SceneGraph callback backward initialization method. + */ + void bwdInit() override; + + void reinit() override { init(); bwdInit(); } + + /** + * @brief SceneGraph callback backward reset method. + */ + //void reset() { bwdInit(); } + + /** + * @brief SceneGraph callback to handle event + * Update the positions of Bézier points + */ + void handleEvent(core::objectmodel::Event *event) override + { + if (dynamic_cast< sofa::simulation::AnimateBeginEvent *>(event)) + { + this->updateBezierPoints(); + } + } + + void draw(const core::visual::VisualParams* vparams) override; + + // + // Accessors + // + + sofa::core::topology::BaseMeshTopology* getInputTopology() + { + return this->inputTopology; + } + + const BTri& getBezierTriangle(unsigned int t) const + { + return this->triInfo.getValue()[t].btri; + } + + // + // Interface + // + + void getBezierNodes(ElementID elemID, sofa::type::fixed_array& bn) + { + const BTri& btri = getBezierTriangle(elemID); + const VecVec3d& x = mStateNodes->read(sofa::core::vec_id::read_access::position)->getValue(); + for (int i=0; i<10; i++) { + bn[i] = x[ btri[i] ]; + } + } + + void getDOFtoLocalTransform(sofa::core::topology::Triangle tri, + Transform DOF0_H_local0, Transform DOF1_H_local1, Transform DOF2_H_local2); + + void computeShapeFunctions(const Vec3& baryCoord, ShapeFunctions &N); + + // + // Simple interpolation of a point: + // + void interpolateOnBTriangle(Index triID, const VecVec3d& nodes, const ShapeFunctions& N, + Vec3& point); + + void interpolateOnBTriangle(Index triID, const VecVec3d& nodes, const Vec3& baryCoord, + Vec3& point) + { + ShapeFunctions N; + computeShapeFunctions(baryCoord, N); + interpolateOnBTriangle(triID, nodes, N, point); + } + + void interpolateOnBTriangle(Index triID, const ShapeFunctions& N, Vec3& point) { + interpolateOnBTriangle(triID, mStateNodes->read(sofa::core::vec_id::read_access::position)->getValue(), N, point); + } + + void interpolateOnBTriangle(Index triID, const Vec3& baryCoord, Vec3& point) { + interpolateOnBTriangle(triID, mStateNodes->read(sofa::core::vec_id::read_access::position)->getValue(), baryCoord, point); + } + + // + // Interpolation + normal + // + void interpolateOnBTriangle(Index triID, const VecVec3d& nodes, const Vec3& baryCoord, + Vec3& point, Vec3& normal, Vec3& t0, Vec3 &t1); + void interpolateOnBTriangle(Index triID, const Vec3& baryCoord, + Vec3& point, Vec3& normal, Vec3& t0, Vec3 &t1) { + interpolateOnBTriangle(triID, mStateNodes->read(sofa::core::vec_id::read_access::position)->getValue(), baryCoord, + point, normal, t0, t1); + } + + // interpolation + normal + second derivatives + //void interpolateOnBTriangle(Index triID, const VecVec3d& nodes, const Vec3& baryCoord, + // Vec3& point, Vec3& normal, Vec3& t0, Vec3 &t1, + // Vec3& D2t0, Vec3& D2t01, Vec3& D2t1); + + protected: + // pointer on mechanical state of the simulation + sofa::core::behavior::MechanicalState *mState; + // pointer to the topology + sofa::core::topology::BaseMeshTopology* inputTopology; + sofa::component::mapping::linear::Mesh2PointTopologicalMapping *bezierM2P; + + // Mapping that creates bezier points and MO to store positions of the points + //sofa::component::topology::Mesh2PointTopologicalMapping::SPtr bezierM2P; + // Mechanical state holding the Bézier points and their velocities + sofa::component::statecontainer::MechanicalObject::SPtr mStateNodes; + + Data< VecVec3 > inputNormals; + + core::topology::PointData< sofa::type::vector > pointInfo; + PointInfoHandler* pointHandler; + + core::topology::TriangleData< sofa::type::vector > triInfo; + TriangleInfoHandler* triHandler; + + // init process=> computes the position of the bezier point given the positions and the normals + void computeBezierPointsUsingNormals(const Index& inputTri, VecVec3d& x, const VecVec3& normals); + void updateBezierPoints(); + void updateBezierPoints(Index triIndex); + + /////// projection of points + //Data< VecVec3> pointsToProject; // input : position of the points to project of the surface + //Data< sofa::type::vector > > f_interpolationIndices; //output interpolation indices + //Data< sofa::type::vector > > f_interpolationValues; //output interpolation values + + //type::vector< Index > triangleProjectionBuf; // buffer of the triangle on which was the previous projection of the point + //VecDeriv pointBaryCoordBuf; // buffer of barycoordinates to store the projection of point + + + //struct SegOfTriInfo { + // Index edgeA; //edge that correspond to Alpha<0 (between nodes 1 and 2 of the triangle) + // Index edgeB; //edge that correspond to Beta<0 (between nodes 0 and 2 of the triangle) + // Index edgeG; //edge that correspond to Gamma<0 (between nodes 0 and 1 of the triangle) + //}; + + //type::vector SegOfTriInfoVector; + + //// store for the points inof on the border (especially the id of the borderEdge) + //struct BorderPointInfo{ + // Index IdPoint; + // type::vector< Index> borderEdge; + //}; + + //type::vector< BorderPointInfo > PointsOnBorderInfo; + + void initTriangle(Index triIndex, TriangleInformation &tInfo); + + const Vec3& getSegment(Index point) + { + return this->pointInfo.getValue()[point].segment; + } + + +}; + +} // namespace fem + +} // namespace component + +} // namespace sofa + +#endif // SOFA_COMPONENT_FEM_BEZIERSHELLINTERPOLATION_H diff --git a/src/Shell/shells2/fem/BezierShellInterpolation.inl b/src/Shell/shells2/fem/BezierShellInterpolation.inl new file mode 100644 index 0000000..cd13352 --- /dev/null +++ b/src/Shell/shells2/fem/BezierShellInterpolation.inl @@ -0,0 +1,755 @@ +#ifndef SOFA_COMPONENT_FEM_BEZIERSHELLINTERPOLATION_INL +#define SOFA_COMPONENT_FEM_BEZIERSHELLINTERPOLATION_INL + +#include +#include + +#include +#include +#include + +#include + + +#include +#include +#include + +//#include +#include + +#include +#include +#include +#include + + +// +// TODO: don't use MO but use PointSetTopologyContainer directly. The content +// of MO grows uncontrolably when topology changes happen. +// +// TODO: Bézier points and Bézier nodes are used interchangeably, choose just one! +// + + +using namespace sofa::core::behavior; + + +namespace sofa +{ + +namespace component +{ + +namespace fem +{ + +// NOTE: The following functions assume that M2P is updated before us. This +// means all the points in M2P are already created. + +//template +//void BezierShellInterpolation::PointInfoHandler::applyCreateFunction( +// unsigned int pointIndex, PointInformation &/*pInfo*/, const topology::Point& /*elem*/, const sofa::type::vector< unsigned int > &/*ancestors*/, const sofa::type::vector< double > &/*coeffs*/) +//{ +// std::cout << __FUNCTION__ << " pt " << pointIndex << std::endl; +//} + + +/* +template +void BezierShellInterpolation::PointInfoHandler::swap(unsigned int i1, unsigned int i2) +{ + // Renumber in bTri + const std::pair& src1 = bsi->bezierM2P->getPointSource()[i1]; + const std::pair& src2 = bsi->bezierM2P->getPointSource()[i2]; + + type::vector tris; + + if (src1.second != (int)core::topology::BaseMeshTopology::InvalidID) + { + switch(src1.first) + { + case topology::Mesh2PointTopologicalMapping::POINT: + { + const type::vector lst = bsi->inputTopology->getTrianglesAroundVertex(src1.second); + tris.resize(tris.size() + lst.size()); + copy_backward(lst.begin(), lst.end(), tris.end()); + break; + } + case topology::Mesh2PointTopologicalMapping::EDGE: + { + const type::vector lst = bsi->inputTopology->getTrianglesAroundEdge(src1.second); + tris.resize(tris.size() + lst.size()); + copy_backward(lst.begin(), lst.end(), tris.end()); + break; + } + case topology::Mesh2PointTopologicalMapping::TRIANGLE: + tris.push_back(src1.second); + break; + default: + break; + } + } + + if (src2.second != (int)core::topology::BaseMeshTopology::InvalidID) + { + switch(src2.first) + { + case topology::Mesh2PointTopologicalMapping::POINT: + { + const type::vector lst = bsi->inputTopology->getTrianglesAroundVertex(src2.second); + tris.resize(tris.size() + lst.size()); + copy_backward(lst.begin(), lst.end(), tris.end()); + break; + } + case topology::Mesh2PointTopologicalMapping::EDGE: + { + const type::vector lst = bsi->inputTopology->getTrianglesAroundEdge(src2.second); + tris.resize(tris.size() + lst.size()); + copy_backward(lst.begin(), lst.end(), tris.end()); + break; + } + case topology::Mesh2PointTopologicalMapping::TRIANGLE: + tris.push_back(src2.second); + break; + default: + break; + } + } + + + type::vector& bezTris = *bsi->triInfo.beginEdit(); + + for (Index i = 0; i < tris.size(); i++) { + for (Index j = 0; j < 10; j++) { + if (bezTris[ tris[i] ].btri[j] == i1) + bezTris[ tris[i] ].btri[j] = i2; + else if (bezTris[ tris[i] ].btri[j] == i2) + bezTris[ tris[i] ].btri[j] = i1; + } + } + + bsi->triInfo.endEdit(); + + Inherited::swap(i1, i2); + + bsi->updateBezierPoints(); +}*/ + +template +void BezierShellInterpolation::TriangleInfoHandler::applyCreateFunction( + unsigned int triIndex, TriangleInformation &tInfo, + const core::topology::BaseMeshTopology::Triangle &/*elem*/, + const sofa::type::vector< unsigned int > &/*ancestors*/, + const sofa::type::vector< double > &/*coeffs*/) +{ + bsi->initTriangle(triIndex, tInfo); +} + +template +BezierShellInterpolation::BezierShellInterpolation() + : mState(NULL) + , inputTopology(NULL) + , mStateNodes(sofa::core::objectmodel::New< sofa::component::statecontainer::MechanicalObject >()) + , inputNormals(initData(&inputNormals, "normals", "normal defined on the source topology")) + , pointInfo(initData(&pointInfo, "pointInfo", "Internal point data")) + , triInfo(initData(&triInfo, "triInfo", "Internal triangle data")) +{ + this->f_listening.setValue(true); + /* + * If M2P is a slave topological changes are not propagated! + bezierM2P->setName("bezierNodeMapper"); + bezierM2P->pointBaryCoords.beginEdit()->push_back(Vec3(0, 0, 0)); + bezierM2P->pointBaryCoords.endEdit(); + //edgeBaryCoords="0.333 0.6666 0 0.6666 0.3333 0" + //triangleBaryCoords="0.3333 0.3333 0" + this->addSlave(bezierM2P); + */ + + mStateNodes->setName("bezierNodes"); + this->addSlave(mStateNodes); + + pointHandler = new PointInfoHandler(this, &pointInfo); + triHandler = new TriangleInfoHandler(this, &triInfo); +} + +//////////////////////////////////////////////////////////// +// init +//=> links with the topology and the mechanical State +//=> computes the indices of the bezier triangles / segments + +template +void BezierShellInterpolation::init() +{ + this->getContext()->get(mState, sofa::core::objectmodel::BaseContext::SearchUp); + if (!mState) { + msg_warning() << "No MechanicalState for the simulation found" ; + return; + } + + // Get the topological mapping + this->getContext()->get(bezierM2P); + if(bezierM2P == NULL) + { + msg_warning()<<"No Mesh2PointTopologicalMapping... cannot construct nodes of Bézier triangles" ; + return; + } + + // Get the input topology (topological support of bezier functions) + inputTopology = bezierM2P->getFrom(); + if(inputTopology == NULL) + { + msg_warning()<<"No input topology of the Mesh2PointTopologicalMapping found" + " (this provide the topological support of bezier functions)"; + return; + } + + pointInfo.createTopologyHandler(bezierM2P->getTo()); + pointInfo.beginEdit()->resize(dynamic_cast(bezierM2P->getTo())->getNumberOfElements()); + pointInfo.endEdit(); + + triInfo.createTopologyHandler(inputTopology); + triInfo.beginEdit()->resize(dynamic_cast(inputTopology)->getNumberOfElements()); + triInfo.endEdit(); + + // Verify that the inputs are coherents + const type::vector< type::vector >& mapEdge = bezierM2P->getPointsMappedFromEdge(); + if( (int) mapEdge.size() != inputTopology->getNbEdges() ) + { + msg_warning()<<"Problem in Mesh2PointTopologicalMapping:mapEdge.size() != inputTopology->getNbEdges()"; + return; + } + + type::vector& bezTris = *triInfo.beginEdit(); + + for (int i=0; igetNbTriangles(); i++) + { + triHandler->applyCreateFunction( + i, + bezTris[i], + inputTopology->getTriangle(i), + (const sofa::type::vector< unsigned int > )0, + (const sofa::type::vector< double >)0); + } + + triInfo.endEdit(); + + // Update the current position of Bézier points + this->updateBezierPoints(); +} + +//////////////////////////////////////////////////////////// +// bwdInit +template +void BezierShellInterpolation::bwdInit() +{ +} + +template +void BezierShellInterpolation::initTriangle(Index triIndex, + TriangleInformation &tInfo) +{ + sofa::core::topology::Triangle tri = inputTopology->getTriangle(triIndex); + const type::fixed_array& edgesInTriangle = inputTopology->getEdgesInTriangle(triIndex); + + unsigned int j; // j= seg0 find segment between tri[0] and tri[1] + unsigned int inverseSeg0=false; + for (j=0; j<3; j++) + { + sofa::core::topology::Edge edge = inputTopology->getEdge(edgesInTriangle[j]); + if(edge[0]==tri[0] && edge[1]==tri[1] ){ + inverseSeg0=false; break; + } + if(edge[1]==tri[0] && edge[0]==tri[1]){ + inverseSeg0=true; break; + } + } + + unsigned int k; // k= seg1 find segment between tri[1] and tri[2] + unsigned int inverseSeg1=false; + for (k=0; k<3; k++) + { + sofa::core::topology::Edge edge = inputTopology->getEdge(edgesInTriangle[k]); + if(edge[0]==tri[1] && edge[1]==tri[2] ){ + inverseSeg1=false; break; + } + if(edge[1]==tri[1] && edge[0]==tri[2]){ + inverseSeg1=true; break; + } + } + + unsigned int l; // l= seg1 find segment between tri[0] and tri[2] + bool inverseSeg2=false; + for (l=0; l<3; l++) + { + sofa::core::topology::Edge edge = inputTopology->getEdge(edgesInTriangle[l]); + if(edge[0]==tri[0] && edge[1]==tri[2] ){ + inverseSeg2=false; break; + } + if(edge[1]==tri[0] && edge[0]==tri[2]){ + inverseSeg2=true; break; + } + } + + // Create the array of node indices + + BTri &btri = tInfo.btri; + + const type::vector< type::vector >& mapPoint = bezierM2P->getPointsMappedFromPoint(); + const type::vector< type::vector >& mapEdge = bezierM2P->getPointsMappedFromEdge(); + const type::vector< type::vector >& mapTri = bezierM2P->getPointsMappedFromTriangle(); + + btri[0] = mapPoint[tri[0]][0]; + btri[1] = mapPoint[tri[1]][0]; + btri[2] = mapPoint[tri[2]][0]; + btri[3] = mapEdge[edgesInTriangle[j]][(inverseSeg0?1:0)]; + btri[4] = mapEdge[edgesInTriangle[l]][(inverseSeg2?1:0)]; + btri[5] = mapEdge[edgesInTriangle[k]][(inverseSeg1?1:0)]; + btri[6] = mapEdge[edgesInTriangle[j]][(inverseSeg0?0:1)]; + btri[7] = mapEdge[edgesInTriangle[l]][(inverseSeg2?0:1)]; + btri[8] = mapEdge[edgesInTriangle[k]][(inverseSeg1?0:1)]; + btri[9] = mapTri[triIndex][0]; + + // Compute the initial position of Bézier points + Data* dataxRest = mStateNodes->write(sofa::core::vec_id::write_access::position); + VecVec3d& xRest = *dataxRest->beginEdit(); + + const VecCoord& inPoints = mState->read(sofa::core::vec_id::read_access::restPosition)->getValue(); + VecVec3 normals = inputNormals.getValue(); + + xRest.resize(bezierM2P->getTo()->getNbPoints()); + type::vector& pInfo = *pointInfo.beginEdit(); + + // Compute the nodes + xRest[ btri[0] ] = inPoints[ tri[0] ].getCenter(); + xRest[ btri[1] ] = inPoints[ tri[1] ].getCenter(); + xRest[ btri[2] ] = inPoints[ tri[2] ].getCenter(); + + computeBezierPointsUsingNormals(triIndex, xRest, normals); + + // Compute the segments in the reference frames of the rest-shape. + // I.e. how are the internal bezier nodes attached to the corners of + // the triangle. + pInfo[ btri[3] ].segment = inPoints[ tri[0] ].getOrientation().inverseRotate( xRest[ btri[3] ] - xRest[ btri[0] ] ); + pInfo[ btri[4] ].segment = inPoints[ tri[0] ].getOrientation().inverseRotate( xRest[ btri[4] ] - xRest[ btri[0] ] ); + pInfo[ btri[5] ].segment = inPoints[ tri[1] ].getOrientation().inverseRotate( xRest[ btri[5] ] - xRest[ btri[1] ] ); + pInfo[ btri[6] ].segment = inPoints[ tri[1] ].getOrientation().inverseRotate( xRest[ btri[6] ] - xRest[ btri[1] ] ); + pInfo[ btri[7] ].segment = inPoints[ tri[2] ].getOrientation().inverseRotate( xRest[ btri[7] ] - xRest[ btri[2] ] ); + pInfo[ btri[8] ].segment = inPoints[ tri[2] ].getOrientation().inverseRotate( xRest[ btri[8] ] - xRest[ btri[2] ] ); + + pointInfo.endEdit(); + dataxRest->endEdit(); + + updateBezierPoints(triIndex); +} + +// ------------------------ +// --- Compute the position of the Bézier points situated at the edges based on +// --- the normals at triangle nodes. +// --- +// --- +// --- To maintain C^0 continuity the nodes have to satisfy a few conditions. +// --- We use the conditions outlined in [Ubach and Oñate 2010]. The node has +// --- to lie on the: +// --- +// --- (1) plane perpendicular to the normal at the normal at triangle node +// --- (2) plane that contains the curve of triangle's contour +// --- - we us the plane defined by the edge (director between nodes of the +// --- flat triangle) and the average between normals at the triangle nodes +// --- connected by the edge +// --- (3) plane perpendicular to the edge of the flat triangle placed at 1/3 +// --- of it's length +// --- +// --- TODO: no C^1 continuity? +// ------------------------ + +template +void BezierShellInterpolation::computeBezierPointsUsingNormals(const Index& inputTri, VecVec3d& x, const VecVec3& normals) +{ + + if ((int) normals.size() != inputTopology->getNbPoints()) + { + if (normals.size() == 0) + msg_warning() << "No normals defined, assuming flat triangles!" ; + else + msg_warning() << "Number of input normals does not match number of points of the input topology" ; + + // No normals, assume flat triangles + + const BTri& bTri = getBezierTriangle(inputTri); + + // Edge A-B + x[ bTri[3] ] = x[ bTri[0] ] + (x[ bTri[1] ] - x[ bTri[0] ])/3.0; + x[ bTri[6] ] = x[ bTri[1] ] + (x[ bTri[0] ] - x[ bTri[1] ])/3.0; + + // Edge B-C + x[ bTri[5] ] = x[ bTri[1] ] + (x[ bTri[2] ] - x[ bTri[1] ])/3.0; + x[ bTri[8] ] = x[ bTri[2] ] + (x[ bTri[1] ] - x[ bTri[2] ])/3.0; + + // Edge C-A + x[ bTri[7] ] = x[ bTri[2] ] + (x[ bTri[0] ] - x[ bTri[2] ])/3.0; + x[ bTri[4] ] = x[ bTri[0] ] + (x[ bTri[2] ] - x[ bTri[0] ])/3.0; + + x[ bTri[9] ] = (x[ bTri[3] ] + x[ bTri[4] ] - x[ bTri[0] ] + + x[ bTri[5] ] + x[ bTri[6] ] - x[ bTri[1] ] + + x[ bTri[7] ] + x[ bTri[8] ] - x[ bTri[2] ])/3; + + return; + } + + sofa::core::topology::Triangle tri= inputTopology->getTriangle(inputTri); + Index a = tri[0]; + Index b = tri[1]; + Index c = tri[2]; + + const BTri& bTri = getBezierTriangle(inputTri); + + // Edge A-B + Vec3 n = (normals[a] + normals[b]) / 2.0; + n.normalize(); + + Vec3 e = x[ bTri[1] ] - x[ bTri[0] ]; + + + Real elen = e.norm()/3.0; + e.normalize(); + + Mat33 M, MI; + + // (1) (2) (3) + M = Mat33(normals[a], cross(e,n), e); + + // Solve M*x = (0, 0, |e|/3) + MI.invert(M); + x[ bTri[3] ] = x[ bTri[0] ] + MI * Vec3(0, 0, elen); + + M = Mat33(normals[b], cross(-e,n), -e); + MI.invert(M); + x[ bTri[6] ] = x[ bTri[1] ] + MI * Vec3(0, 0, elen); + + // Edge B-C + n = (normals[b] + normals[c]) / 2.0; + n.normalize(); + + e = x[bTri[2] ]- x[bTri[1]]; + elen = e.norm()/3.0; + e.normalize(); + + M = Mat33(normals[b], cross(e,n), e); + MI.invert(M); + x[ bTri[5] ] = x[ bTri[1] ] + MI * Vec3(0, 0, elen); + + M = Mat33(normals[c], cross(-e,n), -e); + MI.invert(M); + x[ bTri[8] ] = x[ bTri[2] ] + MI * Vec3(0, 0, elen); + + + // Edge C-A + n = (normals[c] + normals[a]) / 2.0; + n.normalize(); + + e = x[bTri[0] ] - x[ bTri[2] ]; + elen = e.norm()/3.0; + e.normalize(); + + M = Mat33(normals[c], cross(e,n), e); + MI.invert(M); + x[ bTri[7] ] = x[ bTri[2] ] + MI * Vec3(0, 0, elen); + + M = Mat33(normals[a], cross(-e,n), -e); + MI.invert(M); + x[ bTri[4] ] = x[ bTri[0] ] + MI * Vec3(0, 0, elen); + + // Computation of the central point + + x[ bTri[9] ] = (x[ bTri[3] ] + x[ bTri[4] ] - x[ bTri[0] ] + + x[ bTri[5] ] + x[ bTri[6] ] - x[ bTri[1] ] + + x[ bTri[7] ] + x[ bTri[8] ] - x[ bTri[2] ])/3; +} + +template +void BezierShellInterpolation::updateBezierPoints() +{ + for (Index i=0; i<(Index)inputTopology->getNbTriangles(); i++) + { + updateBezierPoints(i); + } +} + +template +void BezierShellInterpolation::updateBezierPoints(Index triIndex) +{ + + // Nodes of the simulation + const VecCoord& xSim = mState->read(sofa::core::vec_id::read_access::position)->getValue(); + + Data* datax = mStateNodes->write(sofa::core::vec_id::write_access::position); + VecVec3d& x = *datax->beginEdit(); + + x.resize(dynamic_cast(bezierM2P->getTo())->getNumberOfElements()); + + sofa::core::topology::Triangle tri = inputTopology->getTriangle(triIndex); + const Index a = tri[0]; + const Index b = tri[1]; + const Index c = tri[2]; + + const BTri& bTri = getBezierTriangle(triIndex); + + // Combine global and optional local transformation for the DOFs + + Transform global_H_DOF0(xSim[a].getCenter(), xSim[a].getOrientation()); + Transform global_H_DOF1(xSim[b].getCenter(), xSim[b].getOrientation()); + Transform global_H_DOF2(xSim[c].getCenter(), xSim[c].getOrientation()); + + Transform DOF0_H_local0, DOF1_H_local1, DOF2_H_local2; + getDOFtoLocalTransform(tri, DOF0_H_local0, DOF1_H_local1, DOF2_H_local2); + + Transform global_H_local0 = global_H_DOF0 * DOF0_H_local0; + Transform global_H_local1 = global_H_DOF1 * DOF1_H_local1; + Transform global_H_local2 = global_H_DOF2 * DOF2_H_local2; + + // Update the positions + + x[ bTri[0] ] = global_H_local0.getOrigin(); + x[ bTri[1] ] = global_H_local1.getOrigin(); + x[ bTri[2] ] = global_H_local2.getOrigin(); + + x[ bTri[3] ] = global_H_local0.projectPoint( getSegment(bTri[3]) ); + x[ bTri[4] ] = global_H_local0.projectPoint( getSegment(bTri[4]) ); + x[ bTri[5] ] = global_H_local1.projectPoint( getSegment(bTri[5]) ); + x[ bTri[6] ] = global_H_local1.projectPoint( getSegment(bTri[6]) ); + x[ bTri[7] ] = global_H_local2.projectPoint( getSegment(bTri[7]) ); + x[ bTri[8] ] = global_H_local2.projectPoint( getSegment(bTri[8]) ); + + x[ bTri[9] ] = ( + x[ bTri[3] ] + x[ bTri[4] ] - x[ bTri[0] ] + + x[ bTri[5] ] + x[ bTri[6] ] - x[ bTri[1] ] + + x[ bTri[7] ] + x[ bTri[8] ] - x[ bTri[2] ])/3; + + datax->endEdit(); +} + +template +void BezierShellInterpolation::getDOFtoLocalTransform( + sofa::core::topology::Triangle /*tri*/, + Transform DOF0_H_local0, Transform DOF1_H_local1, Transform DOF2_H_local2) +{ + // TODO + DOF0_H_local0.clear(); + DOF1_H_local1.clear(); + DOF2_H_local2.clear(); +} + +template +void BezierShellInterpolation::computeShapeFunctions(const Vec3& baryCoord, ShapeFunctions &N) +{ + N[0] = baryCoord[0]*baryCoord[0]*baryCoord[0]; + N[1] = baryCoord[1]*baryCoord[1]*baryCoord[1]; + N[2] = baryCoord[2]*baryCoord[2]*baryCoord[2]; + N[3] = 3*baryCoord[0]*baryCoord[0]*baryCoord[1]; + N[4] = 3*baryCoord[0]*baryCoord[0]*baryCoord[2]; + N[5] = 3*baryCoord[1]*baryCoord[1]*baryCoord[2]; + N[6] = 3*baryCoord[0]*baryCoord[1]*baryCoord[1]; + N[7] = 3*baryCoord[0]*baryCoord[2]*baryCoord[2]; + N[8] = 3*baryCoord[1]*baryCoord[2]*baryCoord[2]; + N[9] = 6*baryCoord[0]*baryCoord[1]*baryCoord[2]; +} + +// +// Interpolate point on Bézier triangle +// +template +void BezierShellInterpolation::interpolateOnBTriangle( + Index triID, const VecVec3d& nodes, + const ShapeFunctions& N, + Vec3& point) +{ + const BTri& btri = getBezierTriangle(triID); + point = + nodes[btri[0]] * N[0] + + nodes[btri[1]] * N[1] + + nodes[btri[2]] * N[2] + + nodes[btri[3]] * N[3] + + nodes[btri[4]] * N[4] + + nodes[btri[5]] * N[5] + + nodes[btri[6]] * N[6] + + nodes[btri[7]] * N[7] + + nodes[btri[8]] * N[8] + + nodes[btri[9]] * N[9]; +} + +// +// Interpolate point on Bézier triangle and get the normal +// +template +void BezierShellInterpolation::interpolateOnBTriangle( + Index triID, const VecVec3d& nodes, + const Vec3& baryCoord, + Vec3& point, Vec3& normal, Vec3& t0, Vec3 &t1) +{ + interpolateOnBTriangle(triID, nodes, baryCoord, point); + + const BTri& btri = getBezierTriangle(triID); + + t0 = + nodes[btri[0]] * 3*baryCoord[0]*baryCoord[0] + + nodes[btri[3]] * 6*baryCoord[0]*baryCoord[1] + + nodes[btri[4]] * 6*baryCoord[0]*baryCoord[2] + + nodes[btri[6]] * 3*baryCoord[1]*baryCoord[1] + + nodes[btri[7]] * 3*baryCoord[2]*baryCoord[2] + + nodes[btri[9]] * 6*baryCoord[1]*baryCoord[2]; + + t1 = + nodes[btri[1]] * 3*baryCoord[1]*baryCoord[1] + + nodes[btri[3]] * 3*baryCoord[0]*baryCoord[0] + + nodes[btri[5]] * 6*baryCoord[1]*baryCoord[2] + + nodes[btri[6]] * 6*baryCoord[0]*baryCoord[1] + + nodes[btri[8]] * 3*baryCoord[2]*baryCoord[2] + + nodes[btri[9]] * 6*baryCoord[0]*baryCoord[2]; + + Vec3 t2 = + nodes[btri[2]] * 3*baryCoord[2]*baryCoord[2] + + nodes[btri[4]] * 3*baryCoord[0]*baryCoord[0] + + nodes[btri[5]] * 3*baryCoord[1]*baryCoord[1] + + nodes[btri[7]] * 6*baryCoord[0]*baryCoord[2] + + nodes[btri[8]] * 6*baryCoord[1]*baryCoord[2] + + nodes[btri[9]] * 6*baryCoord[0]*baryCoord[1]; + + t0 -= t2; + t1 -= t2; + + t0.normalize(); + t1.normalize(); + + normal = cross(t0,t1); + normal.normalize(); +} + +#if 0 +// +// Interpolate point on Bézier triangle and get the normal and the second +// derivatives +// +template +void BezierShellInterpolation::interpolateOnBTriangle( + Index triID, const VecVec3d& nodes, + const Vec3& baryCoord, + Vec3& point, Vec3& normal, Vec3& t0, Vec3 &t1, + Vec3& D2t0, Vec3& D2t01, Vec3& D2t1) +{ + interpolateOnBTriangle(triID, nodes, baryCoord, point, t0, t1); + + const BTri& btri = getBezierTriangle(triID); + + D2t0 = + nodes[btri[0]] * 6*baryCoord[0] + + nodes[btri[3]] * 6*baryCoord[1] + + nodes[btri[4]] * 6*baryCoord[2]; + + D2t01 = + nodes[btri[3]] * 6*baryCoord[0] + + nodes[btri[6]] * 6*baryCoord[1] + + nodes[btri[9]] * 6*baryCoord[2]; + + D2t1 = + nodes[btri[1]] * 6*baryCoord[1] + + nodes[btri[5]] * 6*baryCoord[2] + + nodes[btri[6]] * 6*baryCoord[0]; + + Vec3 D2t02, D2t12, D2t22; + + D2t02 = + nodes[btri[4]] * 6*baryCoord[0] + + nodes[btri[7]] * 6*baryCoord[2] + + nodes[btri[9]] * 6*baryCoord[1]; + + D2t12 = + nodes[btri[5]] * 6*baryCoord[1] + + nodes[btri[8]] * 6*baryCoord[2] + + nodes[btri[9]] * 6*baryCoord[0]; + + D2t22 = + nodes[btri[2]] * 6*baryCoord[2] + + nodes[btri[7]] * 6*baryCoord[0] + + nodes[btri[8]] * 6*baryCoord[1]; + + D2t0 += D2t22 - D2t02*2.0; + D2t01+= D2t22 - D2t02 - D2t12; + D2t1 += D2t22 - D2t12*2.0; +} +#endif + +template +void BezierShellInterpolation::draw(const core::visual::VisualParams* vparams) +{ + + if ((!vparams->displayFlags().getShowBehaviorModels())) + return; + + const VecVec3d& bn = mStateNodes->read(sofa::core::vec_id::read_access::position)->getValue(); + vparams->drawTool()->drawPoints(bn, 2.0, type::RGBAColor(0.5, 1.0, 0.5, 1.0)); + + typedef sofa::type::Vec<2,int> Vec2i; + std::vector< Vec2i > lines; + + VecVec3d points; + + for (int tri=0; trigetNbTriangles(); tri++ ) + { + // 1 // + // / \ // + // 6---5 // + // / \ / \ // + // 3---9---8 // + // / \ / \ / \ // + // 0---4---7---2 // + + // Control mesh + const BTri& btri = getBezierTriangle(tri); + lines.push_back(Vec2i(btri[0], btri[3])); + lines.push_back(Vec2i(btri[0], btri[4])); + lines.push_back(Vec2i(btri[1], btri[5])); + lines.push_back(Vec2i(btri[1], btri[6])); + lines.push_back(Vec2i(btri[2], btri[7])); + lines.push_back(Vec2i(btri[2], btri[8])); + + lines.push_back(Vec2i(btri[3], btri[6])); + lines.push_back(Vec2i(btri[4], btri[7])); + lines.push_back(Vec2i(btri[8], btri[5])); + + lines.push_back(Vec2i(btri[6], btri[9])); + lines.push_back(Vec2i(btri[5], btri[9])); + lines.push_back(Vec2i(btri[4], btri[9])); + lines.push_back(Vec2i(btri[7], btri[9])); + lines.push_back(Vec2i(btri[3], btri[9])); + lines.push_back(Vec2i(btri[8], btri[9])); + + // Surface + points.clear(); + for (double alpha=0.0; alpha<1.00001; alpha+=0.1) + { + for (double beta=0.0; beta<(1.0001-alpha); beta+=0.1) + { + Vec3 baryCoord(1.0-alpha-beta, alpha, beta); + Vec3 posPoint; + this->interpolateOnBTriangle(tri, bn, baryCoord, posPoint); + points.push_back(posPoint); + } + } + + vparams->drawTool()->drawPoints(points,1, type::RGBAColor(0,0,1,1)); + + // TODO: draw edges, normals? + } + + vparams->drawTool()->drawLines(bn, lines, 1, type::RGBAColor(0.5,1.0,0.5,1)); +} + + +} // namespace fem + +} // namespace component + +} // namespace sofa + +#endif // SOFA_COMPONENT_FEM_BEZIERSHELLINTERPOLATION_INL diff --git a/src/Shell/shells2/fem/BezierShellInterpolationM.cpp b/src/Shell/shells2/fem/BezierShellInterpolationM.cpp new file mode 100644 index 0000000..fb0f5de --- /dev/null +++ b/src/Shell/shells2/fem/BezierShellInterpolationM.cpp @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include + + +namespace sofa +{ + +namespace component +{ + +namespace fem +{ + +using namespace sofa::defaulttype; + +// Register in the Factory +int BezierShellInterpolationMClass = core::RegisterObject("Bezier Shell Interpolation supporting Mechanical Mapping") +.add< BezierShellInterpolationM >() +; + +template class SOFA_SHELL_API BezierShellInterpolationM; + +} // namespace fem + +} // namespace component + +} // namespace sofa + diff --git a/src/Shell/shells2/fem/BezierShellInterpolationM.h b/src/Shell/shells2/fem/BezierShellInterpolationM.h new file mode 100644 index 0000000..36738b8 --- /dev/null +++ b/src/Shell/shells2/fem/BezierShellInterpolationM.h @@ -0,0 +1,103 @@ +// +// Interpolation of Bézier triangles (with mechanical mapping) +// +// Author: Tomáš Golembiovský +// +// Copyright: +// +#ifndef SOFA_COMPONENT_FEM_BEZIERSHELLINTERPOLATIONM_H +#define SOFA_COMPONENT_FEM_BEZIERSHELLINTERPOLATIONM_H + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace sofa +{ + +namespace component +{ + +namespace fem +{ + +template +class BezierShellInterpolationM : public BezierShellInterpolation +{ + public: + + SOFA_CLASS(SOFA_TEMPLATE2(BezierShellInterpolationM,TIn,TOut), SOFA_TEMPLATE(BezierShellInterpolation,TIn)); + + typedef TIn In; + typedef TOut Out; + + typedef typename In::VecCoord InVecCoord; + typedef typename In::VecDeriv InVecDeriv; + typedef typename In::Coord InCoord; + typedef typename In::Deriv InDeriv; + typedef typename In::MatrixDeriv InMatrixDeriv; + typedef typename In::Real InReal; + + typedef typename Out::VecCoord OutVecCoord; + typedef typename Out::VecDeriv OutVecDeriv; + typedef typename Out::Coord OutCoord; + typedef typename Out::Deriv OutDeriv; + typedef typename Out::MatrixDeriv OutMatrixDeriv; + typedef typename Out::Real OutReal; + + typedef InReal Real; + + typedef typename Inherit1::Index Index; + typedef typename Inherit1::VecIndex VecIndex; + + typedef typename Inherit1::Vec2 Vec2; + typedef typename Inherit1::Vec3 Vec3; + typedef typename Inherit1::Mat33 Mat33; + typedef typename Inherit1::VecVec3 VecVec3; + + typedef typename Inherit1::VecVec3d VecVec3d; + + typedef typename Inherit1::ShapeFunctions ShapeFunctions; + typedef typename Inherit1::BTri BTri; + typedef typename Inherit1::VecShapeFunctions VecShapeFunctions; + typedef typename Inherit1::VecBTri VecBTri; + + BezierShellInterpolationM() {} + virtual ~BezierShellInterpolationM() {} + + void applyOnBTriangle(VecShapeFunctions projShapeFunctions, VecIndex projElements, helper::WriteAccessor< Data > &out); + void applyJOnBTriangle(VecShapeFunctions projShapeFunctions, VecIndex projElements, const InVecDeriv& in, helper::WriteAccessor< Data > &out); + void applyJTOnBTriangle(VecShapeFunctions projShapeFunctions, VecIndex projElements, const OutVecDeriv& in, helper::WriteAccessor< Data > &out); + void applyJTOnBTriangle(VecShapeFunctions projN, VecIndex projElements, + const OutMatrixDeriv& in, InMatrixDeriv &out); + + protected: + + void applyJTCore(const InVecCoord &xSim, const VecVec3d &x, + const Index &triId, const ShapeFunctions &N, const OutCoord &force, + Vec3 &f1, Vec3 &f2, Vec3 &f3, Vec3 &f1r, Vec3 &f2r, Vec3 &f3r); + + +}; + +} // namespace fem + +} // namespace component + +} // namespace sofa + +#endif // SOFA_COMPONENT_FEM_BEZIERSHELLINTERPOLATIONM_H diff --git a/src/Shell/shells2/fem/BezierShellInterpolationM.inl b/src/Shell/shells2/fem/BezierShellInterpolationM.inl new file mode 100644 index 0000000..71b7baf --- /dev/null +++ b/src/Shell/shells2/fem/BezierShellInterpolationM.inl @@ -0,0 +1,336 @@ +#ifndef SOFA_COMPONENT_FEM_BEZIERSHELLINTERPOLATION_INL +#define SOFA_COMPONENT_FEM_BEZIERSHELLINTERPOLATION_INL + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + + +// +// TODO: don't use MO but use PointSetTopologyContainer directly. The content +// of MO grows uncontrolably when topology changes happen. +// +// TODO: Bézier points and Bézier nodes are used interchangeably, choose just one! +// + + +using namespace sofa::core::behavior; + + +// Returns the skew-symetric matrix for computing a cross-product with the +// vector @x +template +inline void crossMatrix(const sofa::type::Vec<3, Real>& x, + sofa::type::Mat<3,3, Real>& m) +{ + m[0][0] = 0; + m[0][1] = -x[2]; + m[0][2] = x[1]; + + m[1][0] = x[2]; + m[1][1] = 0; + m[1][2] = -x[0]; + + m[2][0] = -x[1]; + m[2][1] = x[0]; + m[2][2] = 0; +} + +namespace sofa +{ + +namespace component +{ + +namespace fem +{ + +// @projBaryCoords Barycentric coordinates of projected points +// @projElements element index for each barycentric coordinate +template +void BezierShellInterpolationM::applyOnBTriangle( + VecShapeFunctions projN, VecIndex projElements, + helper::WriteAccessor< Data > &out) +{ + if (projN.size() != projElements.size()) + { + msg_warning() << "projN.size() != projElements.size()" ; + return; + } + + const VecVec3d& nodes = this->mStateNodes->read(sofa::core::vec_id::read_access::position)->getValue(); + + out.resize(projElements.size()); + for (Index i=0; iinterpolateOnBTriangle(projElements[i], nodes, projN[i], out[i]); + } +} + +template +void BezierShellInterpolationM::applyJOnBTriangle( + VecShapeFunctions projN, VecIndex projElements, + const InVecDeriv& in, helper::WriteAccessor< Data > &out) +{ + if (projN.size() != projElements.size()) + { + msg_warning() << "projN.size() != projElements.size()" ; + return; + } + + //const VecCoord& xSim = mState->read(sofa::core::vec_id::read_access::position)->getValue(); + const VecVec3d& x = this->mStateNodes->read(sofa::core::vec_id::read_access::position)->getValue(); + VecVec3d v; // NOTE: we use VecVec3d instead of VecVec3 because we supply velocities in place of interpolation points. + v.resize(dynamic_cast(this->bezierM2P->getTo())->getNumberOfElements()); + + // Compute nodes of the Bézier triangle for each input triangle + for (Index i=0; i<(Index)this->inputTopology->getNbTriangles(); i++) + { + sofa::core::topology::Triangle tri= this->inputTopology->getTriangle(i); + const BTri& bTri = this->getBezierTriangle(i); + + // Velocities in corner nodes + v[ bTri[0] ] = in[ tri[0] ].getVCenter(); + v[ bTri[1] ] = in[ tri[1] ].getVCenter(); + v[ bTri[2] ] = in[ tri[2] ].getVCenter(); + + /* + // Angular velocities in cross-product matrix + Mat33 Omega0, Omega1, Omega2; + crossMatrix(in[ tri[0] ].getVOrientation(), Omega0); + crossMatrix(in[ tri[1] ].getVOrientation(), Omega1); + crossMatrix(in[ tri[2] ].getVOrientation(), Omega2); + + // Apply optional local transform + Transform global_H_DOF0(xSim[ tri[0] ].getCenter(), xSim[ tri[0] ].getOrientation()); + Transform global_H_DOF1(xSim[ tri[1] ].getCenter(), xSim[ tri[1] ].getOrientation()); + Transform global_H_DOF2(xSim[ tri[2] ].getCenter(), xSim[ tri[2] ].getOrientation()); + + Transform DOF0_H_local0, DOF1_H_local1, DOF2_H_local2; + getDOFtoLocalTransform(tri, DOF0_H_local0, DOF1_H_local1, DOF2_H_local2); + + Transform global_H_local0 = global_H_DOF0 * DOF0_H_local0; + Transform global_H_local1 = global_H_DOF1 * DOF1_H_local1; + Transform global_H_local2 = global_H_DOF2 * DOF2_H_local2; + + Mat33 dR0, dR1, dR2; + + // Rotation matrices at corner nodes + global_H_local0.getOrientation().toMatrix(dR0); + global_H_local1.getOrientation().toMatrix(dR1); + global_H_local2.getOrientation().toMatrix(dR2); + + // Derivatives of the rotation matrix + dR0 = Omega0*dR0; + dR1 = Omega1*dR1; + dR2 = Omega2*dR2; + + // Velocities at other nodes + v[ bTri[3] ] = v[ bTri[0] ] + dR0*getSegment(bTri[3]); + v[ bTri[4] ] = v[ bTri[0] ] + dR0*getSegment(bTri[4]); + v[ bTri[5] ] = v[ bTri[1] ] + dR1*getSegment(bTri[5]); + v[ bTri[6] ] = v[ bTri[1] ] + dR1*getSegment(bTri[6]); + v[ bTri[7] ] = v[ bTri[2] ] + dR2*getSegment(bTri[7]); + v[ bTri[8] ] = v[ bTri[2] ] + dR2*getSegment(bTri[8]); + */ + + // This is faster + v[ bTri[3] ] = v[ bTri[0] ] + cross(in[ tri[0] ].getVOrientation(), x[ bTri[3] ] - x[ bTri[0] ]); + v[ bTri[4] ] = v[ bTri[0] ] + cross(in[ tri[0] ].getVOrientation(), x[ bTri[4] ] - x[ bTri[0] ]); + v[ bTri[5] ] = v[ bTri[1] ] + cross(in[ tri[1] ].getVOrientation(), x[ bTri[5] ] - x[ bTri[1] ]); + v[ bTri[6] ] = v[ bTri[1] ] + cross(in[ tri[1] ].getVOrientation(), x[ bTri[6] ] - x[ bTri[1] ]); + v[ bTri[7] ] = v[ bTri[2] ] + cross(in[ tri[2] ].getVOrientation(), x[ bTri[7] ] - x[ bTri[2] ]); + v[ bTri[8] ] = v[ bTri[2] ] + cross(in[ tri[2] ].getVOrientation(), x[ bTri[8] ] - x[ bTri[2] ]); + + + v[ bTri[9] ] = ( + v[ bTri[3] ] + v[ bTri[4] ] - v[ bTri[0] ] + + v[ bTri[5] ] + v[ bTri[6] ] - v[ bTri[1] ] + + v[ bTri[7] ] + v[ bTri[8] ] - v[ bTri[2] ])/3; + } + + out.resize(projElements.size()); + for (Index i=0; iinterpolateOnBTriangle(projElements[i], v, projN[i], out[i]); + } +} + +template +void BezierShellInterpolationM::applyJTOnBTriangle( + VecShapeFunctions projN, VecIndex projElements, + const OutVecDeriv& in, helper::WriteAccessor< Data > &out) +{ + if (projN.size() != projElements.size()) + { + msg_warning() << "projN.size() != projElements.size()" ; + return; + } + + const InVecCoord& xSim = this->mState->read(sofa::core::vec_id::read_access::position)->getValue(); + const VecVec3d& x = this->mStateNodes->read(sofa::core::vec_id::read_access::position)->getValue(); + + // Compute nodes of the Bézier triangle for each input triangle + out.resize(projElements.size()); + for (Index i=0; iinputTopology->getTriangle(projElements[i]); + + Vec3 f1, f2, f3; // resulting linear forces on corner nodes + Vec3 f1r, f2r, f3r; // resulting torques + + applyJTCore(xSim, x, projElements[i], projN[i], in[i], + f1, f2, f3, f1r, f2r, f3r); + + getVCenter(out[ tri[0] ]) += f1; + getVCenter(out[ tri[1] ]) += f2; + getVCenter(out[ tri[2] ]) += f3; + + getVOrientation(out[ tri[0] ]) += f1r; + getVOrientation(out[ tri[1] ]) += f2r; + getVOrientation(out[ tri[2] ]) += f3r; + } +} + +template +void BezierShellInterpolationM::applyJTOnBTriangle( + VecShapeFunctions projN, VecIndex projElements, + const OutMatrixDeriv& in, InMatrixDeriv &out) +{ + const InVecCoord& xSim = this->mState->read(sofa::core::vec_id::read_access::position)->getValue(); + const VecVec3d& x = this->mStateNodes->read(sofa::core::vec_id::read_access::position)->getValue(); + typename Out::MatrixDeriv::RowConstIterator rowItEnd = in.end(); + + for (typename OutMatrixDeriv::RowConstIterator rowIt = in.begin(); + rowIt != rowItEnd; ++rowIt) + { + typename OutMatrixDeriv::ColConstIterator colItEnd = rowIt.end(); + typename OutMatrixDeriv::ColConstIterator colIt = rowIt.begin(); + + if (colIt != colItEnd) + { + typename InMatrixDeriv::RowIterator o = out.writeLine(rowIt.index()); + for ( ; colIt != colItEnd; ++colIt) + { + Vec3 f1, f2, f3; // resulting linear velocities on corner nodes + Vec3 f1r, f2r, f3r; // resulting angular velocities + + Index ptId = colIt.index(); + applyJTCore(xSim, x, projElements[ptId], projN[ptId], colIt.val(), + f1, f2, f3, f1r, f2r, f3r); + + sofa::core::topology::Triangle tri = this->inputTopology->getTriangle(projElements[ptId]); + o.addCol(tri[0], InDeriv(f1, f1r)); + o.addCol(tri[1], InDeriv(f2, f2r)); + o.addCol(tri[2], InDeriv(f3, f3r)); + } + } + } +} + +template +void BezierShellInterpolationM::applyJTCore( + const InVecCoord &xSim, const VecVec3d &x, + const Index &triId, const ShapeFunctions &N, const OutCoord &force, + Vec3 &f1, Vec3 &f2, Vec3 &f3, Vec3 &f1r, Vec3 &f2r, Vec3 &f3r) +{ + if (force == Vec3(0,0,0)) { + f1 = f2 = f3 = f1r = f2r = f3r = Vec3(0,0,0); + return; + } + + const sofa::core::topology::Triangle &tri = this->inputTopology->getTriangle(triId); + const BTri& bTri = this->getBezierTriangle(triId); + + Vec3 fn; + + // Compute the influence on the corner nodes + f1 = force * N[0]; + f2 = force * N[1]; + f3 = force * N[2]; + + // Now the influence through other nodes + + fn = force * N[3]; + if (fn != Vec3(0,0,0)) + { + f1 += fn; + f1r += cross((x[ bTri[3] ] - x[ bTri[0] ]), fn); + } + + fn = force * N[4]; + if (fn != Vec3(0,0,0)) + { + f1 += fn; + f1r += cross((x[ bTri[4] ] - x[ bTri[0] ]), fn); + } + + fn = force * N[5]; + if (fn != Vec3(0,0,0)) + { + f2 += fn; + f2r += cross((x[ bTri[5] ] - x[ bTri[1] ]), fn); + } + + fn = force * N[6]; + if (fn != Vec3(0,0,0)) + { + f2 += fn; + f2r += cross((x[ bTri[6] ] - x[ bTri[1] ]), fn); + } + + fn = force * N[7]; + if (fn != Vec3(0,0,0)) + { + f3 += fn; + f3r += cross((x[ bTri[7] ] - x[ bTri[2] ]), fn); + } + + fn = force * N[8]; + if (fn != Vec3(0,0,0)) + { + f3 += fn; + f3r += cross((x[ bTri[8] ] - x[ bTri[2] ]), fn); + } + + fn = force * N[9]/3; + if (fn != Vec3(0,0,0)) + { + // Rotation matrices at corner nodes + Mat33 R[3]; + xSim[ tri[0] ].getOrientation().toMatrix(R[0]); + xSim[ tri[1] ].getOrientation().toMatrix(R[1]); + xSim[ tri[2] ].getOrientation().toMatrix(R[2]); + + f1 += fn; + f2 += fn; + f3 += fn; + f1r += cross(R[0]*(this->getSegment(bTri[3]) + this->getSegment(bTri[4])), fn); + f2r += cross(R[1]*(this->getSegment(bTri[5]) + this->getSegment(bTri[6])), fn); + f3r += cross(R[2]*(this->getSegment(bTri[7]) + this->getSegment(bTri[8])), fn); + } +} + + +} // namespace fem + +} // namespace component + +} // namespace sofa + +#endif // SOFA_COMPONENT_FEM_BEZIERSHELLINTERPOLATION_INL diff --git a/src/Shell/shells2/forcefield/BezierShellForceField.cpp b/src/Shell/shells2/forcefield/BezierShellForceField.cpp new file mode 100644 index 0000000..89767c9 --- /dev/null +++ b/src/Shell/shells2/forcefield/BezierShellForceField.cpp @@ -0,0 +1,55 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#include +#include +#include +#include +#include +#include + + +namespace sofa +{ + +namespace component +{ + +namespace forcefield +{ + +using namespace sofa::defaulttype; + +// Register in the Factory +int BezierShellForceFieldClass = core::RegisterObject("Triangular finite elements with bending based on Bézier triangle formulation") +.add< BezierShellForceField >() +; + +template class SOFA_SHELL_API BezierShellForceField; + +} // namespace forcefield + +} // namespace component + +} // namespace sofa diff --git a/src/Shell/shells2/forcefield/BezierShellForceField.h b/src/Shell/shells2/forcefield/BezierShellForceField.h new file mode 100644 index 0000000..bfa2e5d --- /dev/null +++ b/src/Shell/shells2/forcefield/BezierShellForceField.h @@ -0,0 +1,362 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#ifndef SOFA_COMPONENT_FORCEFIELD_BEZIERSHELLFORCEFIELD_H +#define SOFA_COMPONENT_FORCEFIELD_BEZIERSHELLFORCEFIELD_H + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + + +// Uncomment the following to use quaternions instead of matrices for +// rotations. Quaternions are slightly faster but numericaly much, much *less* +// stable. I don't recommend that! +//#define CRQUAT + +namespace sofa +{ + +namespace component +{ + +namespace forcefield +{ + +using namespace sofa::type; +using sofa::type::vector; +using namespace sofa::core::topology; +using namespace sofa::core::behavior; + +/// This class can be overridden if needed for additionnal storage within template specializations. +template +class BezierShellForceFieldInternalData +{ +public: +}; + + +template +class BezierShellForceField : public core::behavior::ForceField +{ + public: + SOFA_CLASS(SOFA_TEMPLATE(BezierShellForceField,DataTypes), SOFA_TEMPLATE(core::behavior::ForceField,DataTypes)); + + typedef core::behavior::ForceField Inherited; + typedef typename DataTypes::VecCoord VecCoord; + typedef typename DataTypes::VecDeriv VecDeriv; + + typedef typename DataTypes::Coord Coord; + typedef typename DataTypes::Deriv Deriv; + typedef typename Coord::value_type Real; + + typedef Vec<3,Real> Vec3; + typedef Vec<2,Real> Vec2; + typedef type::vector VecVec3; + + typedef Mat<2,2,Real> Mat22; + typedef Mat<3,3,Real> Mat33; + + typedef sofa::type::Quat Quat; + + typedef Data DataVecCoord; + typedef Data DataVecDeriv; + + typedef sofa::defaulttype::Vec3Types::VecCoord VecCoordHigh; + + typedef sofa::Index Index; + typedef sofa::core::topology::BaseMeshTopology::Triangle Triangle; + typedef sofa::core::topology::BaseMeshTopology::SeqTriangles SeqTriangles; + typedef type::vector VecIndex; + + typedef typename sofa::defaulttype::SolidTypes::Transform Transform; + typedef typename sofa::defaulttype::SolidTypes::SpatialVector SpatialVector; + + protected: + + // Displacement vectorshfor in-plane forces: + // [ U1x, U1y, dT1z, U2x, U2y, dT2z, U3x, U3y, dT3z ] + typedef Vec<9, Real> Displacement; + // Displacement vector for bending forces: + // [ U1z, dT1x, dT1y, U2z, dT2x, dT2y, U3z, dT3x, dT3y ] + typedef Vec<9, Real> DisplacementBending; + typedef Mat<3, 3, Real> MaterialStiffness; ///< matrix of material stiffness + typedef Mat<3, 9, Real> StrainDisplacement; ///< strain-displacement matrix for in-plane forces + typedef Mat<3, 9, Real> StrainDisplacementBending; + typedef Mat33 Transformation; ///< matrix for rigid transformations like rotations + typedef Mat<9, 9, Real> StiffnessMatrix; + typedef Mat<9, 9, Real> StiffnessMatrixBending; + typedef Mat<18, 18, Real> StiffnessMatrixGlobalSpace; + typedef Mat<4, 6, Real> GradDisplacement; + + + sofa::core::topology::BaseMeshTopology* _topology; + sofa::core::topology::BaseMeshTopology* _topologyTarget; + + // Nodes of the Bezier triangles + +public: + + // Data for Gaussian quadrature + static const int Gn = 6; // Number of Gauss points + + class TriangleInformation + { + public: + + type::fixed_array restLocalPositions; +#ifdef CRQUAT + type::fixed_array restLocalOrientationsInv; +#else + type::fixed_array restLocalOrientationsInv; +#endif + + // Index of this element + Index elementID; + // Indices of each vertex + Index a, b, c; + + // Corotational frame + Vec3 frameCenter; + Transformation frameOrientation; // frame orientation * + Transformation frameOrientationInv; // it's inverse (transposition) +#ifdef CRQUAT + Quat frameOrientationQ; // representation as quaternion +#endif + + // Matrix for computing displacement gradient + GradDisplacement gradU; + + // Matrix of interpolation functions + // NOTE: we might need to always use double here, with + // floats the matrix makes the strain-displacement and + // stiffness matrices unusable due to lack of precision. + Mat33 interpol; + + // Nodes of the Bezier triangle + type::fixed_array pts; // ... in local frame + + // the strain-displacement matrices at each Gauss point + StrainDisplacement strainDisplacementMatrix1; + StrainDisplacement strainDisplacementMatrix2; + StrainDisplacement strainDisplacementMatrix3; + StrainDisplacement strainDisplacementMatrix4; + + StrainDisplacement strainDisplacementMatrix[Gn]; + StrainDisplacement strainDisplacementMatrixB[Gn]; + + + // Measure stress or strain + struct MeasurePoint { + Vec3 point; // Barycentric coordinates + StrainDisplacement B; // Strain-displacement Matrix + StrainDisplacement Bb; // Strain-displacement Matrix bending + Index id; // Index into the result array + }; + type::vector measure; + + // the strain-displacement matrices at each Gauss point + StrainDisplacementBending strainDisplacementMatrixB1; + StrainDisplacementBending strainDisplacementMatrixB2; + StrainDisplacementBending strainDisplacementMatrixB3; + StrainDisplacementBending strainDisplacementMatrixB4; + + // Stiffness matrix K = J * M * Jt + StiffnessMatrix stiffnessMatrix; + + // Stiffness matrix for bending K = Jt * M * J + StiffnessMatrixBending stiffnessMatrixBending; + + // Surface Area * 2 + Real area2; + + TriangleInformation() { } + + /// Output stream + inline friend std::ostream& operator<< ( std::ostream& os, const TriangleInformation& /*ti*/ ) + { + return os; + } + + /// Input stream + inline friend std::istream& operator>> ( std::istream& in, TriangleInformation& /*ti*/ ) + { + return in; + } + }; + + class TriangleHandler : public core::topology::TopologyDataHandler > + { + typedef TopologyDataHandler > Inherited; + public: + TriangleHandler(BezierShellForceField* _ff, TriangleData >* _data) : Inherited(_data), ff(_ff) {} + + void applyCreateFunction(unsigned int triangleIndex, TriangleInformation& , + const Triangle & t, + const sofa::type::vector< unsigned int > &, + const sofa::type::vector< double > &); + + void swap(unsigned int i1, unsigned int i2); + + protected: + BezierShellForceField* ff; + }; + + BezierShellForceField(); + + virtual ~BezierShellForceField(); + void init() override; + void reinit() override; + void addForce(const sofa::core::MechanicalParams* /*mparams*/, DataVecDeriv& dataF, const DataVecCoord& dataX, const DataVecDeriv& /*dataV*/ ) override; + void addDForce(const sofa::core::MechanicalParams* /*mparams*/, DataVecDeriv& datadF, const DataVecDeriv& datadX ) override; + void addKToMatrix(const core::MechanicalParams* mparams, const sofa::core::behavior::MultiMatrixAccessor* matrix) override; + void handleTopologyChange() override; + + SReal getPotentialEnergy(const sofa::core::MechanicalParams* /*mparams*/, const DataVecCoord& /*x*/) const override { return 0; } + + void draw(const core::visual::VisualParams* vparams) override; + + sofa::core::topology::BaseMeshTopology* getTopology() {return _topology;} + + Data f_poisson; + Data f_young; + Data f_thickness; + Data f_polarMaxIters; + Data f_polarMinTheta; + Data f_drawFrame; + Data f_drawNodes; + Data f_measure; + Data f_drawPointSize; + Data > f_measuredValues; + + // Allow transition between rest shapes + SingleLink, + shell::controller::MeshInterpolator, + BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> restShape; + + // Indirect rest shape indexing (e.g. for "joining" two meshes) + bool mapTopology; + SingleLink, + shell::engine::JoinMeshPoints, + BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> topologyMapper; + + // Bezier shell interpolation + SingleLink, + sofa::component::fem::BezierShellInterpolation, + BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> bsInterpolation; + + + + static void computeEdgeBezierPoints(const Index& a, const Index& b, const Index& c, + const VecCoord& x, const type::vector& norms, + type::fixed_array &bezierPoints); + + void handleEvent(sofa::core::objectmodel::Event *event) override; + + const TriangleInformation& getTriangleInfo(Index t) + { + return triangleInfo.getValue()[t]; + } + + /** + * Set points at which compute the stress or strain. + * + * @param points Barycentric coordinates of points. + * @param elements Triangle ID for each point specifying the + * triangle to which the point is related. + */ + void stressAtPoints(const VecVec3 &points, const VecIndex &elements); + +protected : + + TriangleData< sofa::type::vector > triangleInfo; + TriangleHandler* triangleHandler; + + /// Material stiffness matrices for plane stress and bending + MaterialStiffness materialMatrix; + //MaterialStiffness materialMatrixBending; + + Real polarMinSinTheta; + + bool bMeasureStrain; + bool bMeasureStress; + + //unsigned int pditers; + + void initTriangleOnce(const int i, const Index&a, const Index&b, const Index&c); + void initTriangle(const int i); + + void computeLocalTriangle(const Index elementIndex, bool bFast); + + void computeDisplacements( Displacement &Disp, DisplacementBending &BDisp, const VecCoord &x, TriangleInformation *tinfo); + void computeStrainDisplacementMatrixMembrane(TriangleInformation &tinfo); + void computeStrainDisplacementMatrixBending(TriangleInformation &tinfo); + void computeStiffnessMatrixMembrane(StiffnessMatrix &K, const TriangleInformation &tinfo); + void computeStiffnessMatrixBending(StiffnessMatrixBending &K, const TriangleInformation &tinfo); + void computeForceMembrane(Displacement &F, const Displacement& D, const Index elementIndex); + void computeForceBending(DisplacementBending &F, const DisplacementBending& D, const Index elementIndex); + + // Strain-displacement matrices + void matrixSDM(StrainDisplacement &J, const Vec3 &GP, const TriangleInformation& tinfo); + void matrixSDB(StrainDisplacementBending &J, const Vec3 &GP, const TriangleInformation& tinfo); + + // inPlane Gradient + void computeInPlaneDisplacementGradient(GradDisplacement& gradU, const Vec3& GP, const TriangleInformation &tinfo); + + // Use polar decomposition to fix inPlane rotation + void fixFramePolar(const Displacement &Disp, Mat22 &R, TriangleInformation &tinfo); + + /// f += Kx where K is the stiffness matrix and x a displacement + virtual void applyStiffness(VecDeriv& f, const VecDeriv& dx, const Index elementIndex, const double kFactor); + virtual void computeMaterialMatrix(); + + //void bezierFunctions(const Vec2& baryCoord, sofa::type::fixed_array &f_bezier); + //void bezierDerivateFunctions(const Vec2& baryCoord, sofa::type::fixed_array &df_dx_bezier, sofa::type::fixed_array &df_dy_bezier); + void interpolateRefFrame(TriangleInformation *tinfo, const Vec2& baryCoord); + + + void accumulateForce(VecDeriv& f, const VecCoord & p, const Index elementIndex); + + void convertStiffnessMatrixToGlobalSpace(StiffnessMatrixGlobalSpace &K_gs, TriangleInformation *tinfo); + + void HSL2RGB(Vec3 &rgb, Real h, Real sl, Real l); +}; + + +} // namespace forcefield + +} // namespace component + +} // namespace sofa + +#endif // #ifndef SOFA_COMPONENT_FORCEFIELD_BEZIERSHELLFORCEFIELD_H diff --git a/src/Shell/shells2/forcefield/BezierShellForceField.inl b/src/Shell/shells2/forcefield/BezierShellForceField.inl new file mode 100644 index 0000000..59beb62 --- /dev/null +++ b/src/Shell/shells2/forcefield/BezierShellForceField.inl @@ -0,0 +1,2155 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#ifndef SOFA_COMPONENT_FORCEFIELD_BEZIERSHELLFORCEFIELD_INL +#define SOFA_COMPONENT_FORCEFIELD_BEZIERSHELLFORCEFIELD_INL + +#include +#include +#include +#include +#include +#include +#include +#include //for debugging +#include +#include + +#include +#include +#include + +#include + +#include + +#include + +#ifdef _WIN32 +#include +#endif + +// Use 4-point Gaussian quadrature to integrate over the triangle +#define GAUSS4 +// n=4 (source: can't remember) +//const double GX[] = { 0.211324865, 0.211324865, 0.788675134, 0.788675134 }; +//const double GY[] = { 0.166666667, 0.622008467, 0.044658198, 0.166666667 }; +//const double GW[] = { 0.197168783, 0.197168783, 0.052831216, 0.052831216 }; +// n=6, d = 4 (source: http://www.electromagnetics.biz/integration.htm) +const double GX[] = { 0.8168476, 0.09157621, 0.09157621, 0.1081030, 0.4459485, 0.4459485 }; +const double GY[] = { 0.09157621, 0.8168476, 0.09157621, 0.4459485, 0.1081030, 0.4459485 }; +const double GW[] = { 0.05497587, 0.05497587, 0.05497587, 0.1116908, 0.1116908, 0.1116908 }; +// n = 12, d = 6 (source: http://www.electromagnetics.biz/integration.htm) +//const double GX[] = { 0.5014265, 0.2492867, 0.2492867, 0.8738220, 0.06308901, 0.06308901, 0.6365025, 0.6365025, 0.05314505, 0.05314505, 0.3103525, 0.3103525 }; +//const double GY[] = { 0.2492867, 0.5014265, 0.2492867, 0.06308901, 0.8738220, 0.06308901, 0.05314505, 0.3103525, 0.6365025, 0.3103525, 0.6365025, 0.05314505 }; +//const double GW[] = { 0.05839314, 0.05839314, 0.05839314, 0.02542245, 0.02542245, 0.02542245, 0.04142554, 0.04142554, 0.04142554, 0.04142554, 0.04142554, 0.04142554 }; +// n = 27, d = 11 (source: http://www.electromagnetics.biz/integration.htm) +//const double GX[] = { 0.9352701, 0.03236495, 0.03236495, 0.7612982, 0.1193509, 0.1193509, 0.06922210, 0.5346110, 0.5346110, 0.5933802, 0.2033099, 0.2033099, 0.2020614, 0.3989693, 0.3989693, 0.05017814, 0.05017814, 0.5932012, 0.5932012, 0.3566206, 0.3566206, 0.02102202, 0.02102202, 0.8074890, 0.8074890, 0.1714890, 0.1714890 }; +//const double GY[] = { 0.03236495, 0.9352701, 0.03236495, 0.1193509, 0.7612982, 0.1193509, 0.5346110, 0.06922210, 0.5346110, 0.2033099, 0.5933802, 0.2033099, 0.3989693, 0.2020614, 0.3989693, 0.5932012, 0.3566206, 0.05017814, 0.3566206, 0.05017814, 0.5932012, 0.8074890, 0.1714890, 0.02102202, 0.1714890, 0.02102202, 0.8074890 }; +//const double GW[] = { 0.006829866, 0.006829866, 0.006829866, 0.01809227, 0.01809227, 0.01809227, 0.0004635032, 0.0004635032, 0.0004635032, 0.02966149, 0.02966149, 0.02966149, 0.03857477, 0.03857477, 0.03857477, 0.02616856, 0.02616856, 0.02616856, 0.02616856, 0.02616856, 0.02616856, 0.01035383, 0.01035383, 0.01035383, 0.01035383, 0.01035383, 0.01035383 }; + +namespace sofa +{ + namespace component + { + namespace forcefield + { + using namespace sofa::type; + using namespace sofa::component::topology; + + + +// -------------------------------------------------------------------------------------- +// --- Topology Creation/Destruction functions +// -------------------------------------------------------------------------------------- +template +void BezierShellForceField::TriangleHandler::applyCreateFunction(unsigned int triangleIndex, TriangleInformation &, const Triangle &t, const sofa::type::vector &, const sofa::type::vector &) +{ + if (ff) { + ff->initTriangleOnce(triangleIndex, t[0], t[1], t[2]); + ff->initTriangle(triangleIndex); + } +} + +/*template +void BezierShellForceField::TriangleHandler::swap(unsigned int i1, unsigned int i2) +{ + if (ff) { + type::vector& triangleInf = *(ff->triangleInfo.beginEdit()); + + triangleInf[i1].elementID = i2; + triangleInf[i2].elementID = i1; + + ff->triangleInfo.endEdit(); + } + + Inherited::swap(i1, i2); +}*/ + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +BezierShellForceField::BezierShellForceField() +: f_poisson(initData(&f_poisson,(Real)0.45,"poissonRatio","Poisson's ratio in Hooke's law")) +, f_young(initData(&f_young,(Real)3000.,"youngModulus","Young's modulus in Hooke's law")) +, f_thickness(initData(&f_thickness,(Real)0.1,"thickness","Thickness of the plates")) +, f_polarMaxIters(initData(&f_polarMaxIters, 5U, "polarMaxIters", "Use this number of polar decomposition iterations to fix the corotational frames. We suggest at least 1 iteration.")) +, f_polarMinTheta(initData(&f_polarMinTheta, (Real)1e-6, "polarMinTheta", "Stop if the angle change by polar decomposition is smaller than the value")) +, f_drawFrame(initData(&f_drawFrame, true, "drawFrame", "Draw the corotational frame")) +, f_drawNodes(initData(&f_drawNodes, true, "drawNodes", "Draw the control points of Bézier triangle")) +, f_measure(initData(&f_measure, "measure", "Draw the strain or stress")) +, f_drawPointSize(initData(&f_drawPointSize, (unsigned int)8, "drawPointSize", "Point size to use")) +, f_measuredValues(initData(&f_measuredValues, "measuredValues", "Measured values for stress or strain")) +, restShape(initLink("restShape","MeshInterpolator component for variable rest shape")) +, mapTopology(false) +, topologyMapper(initLink("topologyMapper","Component supplying different topology for the rest shape")) +, bsInterpolation(initLink("bsInterpolation","Attached BezierShellInterpolation object")) +, triangleInfo(initData(&triangleInfo, "triangleInfo", "Internal triangle data")) +{ + f_measure.beginEdit()->setNames( { + "None", // Draw nothing + "Strain (norm)", // L_2 norm of strain in x and y directions + "Von Mises stress" // Von Mises stress criterion + }); + f_measure.beginEdit()->setSelectedItem("None"); + f_measure.endEdit(); + + triangleHandler = new TriangleHandler(this, &triangleInfo); +} + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template void BezierShellForceField::handleTopologyChange() +{ + msg_warning() << "handleTopologyChange() not implemented" ; + +} + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +BezierShellForceField::~BezierShellForceField() +{ + if(triangleHandler) delete triangleHandler; +} + +// -------------------------------------------------------------------------------------- +// --- Initialization stage +// -------------------------------------------------------------------------------------- +template +void BezierShellForceField::init() +{ + this->Inherited::init(); + + if (this->mstate == NULL) { + msg_warning() << "Mechanical state is required!" ; + return; + } + + _topology = this->getContext()->getMeshTopology(); + + // Create specific handler for TriangleData + triangleInfo.createTopologyHandler(_topology); + + reinit(); +} + +// -------------------------------------------------------------------------------------- +// --- Re-initialization (called when we change a parameter through the GUI) +// -------------------------------------------------------------------------------------- +template void BezierShellForceField::reinit() +{ + // Decode the selected draw method + if (f_measure.getValue().getSelectedItem() == "None") { + bMeasureStrain = false; bMeasureStress = false; + } else if (f_measure.getValue().getSelectedItem() == "Strain (norm)") { + bMeasureStrain = true; bMeasureStress = false; + } else if (f_measure.getValue().getSelectedItem() == "Von Mises stress") { + bMeasureStrain = false; bMeasureStress = true; + } else { + msg_warning() << "Invalid value for measure'" << f_measure.getValue().getSelectedItem() << "'" ; + return; + } + + _topology = this->getContext()->getMeshTopology(); + + polarMinSinTheta = sin(f_polarMinTheta.getValue()); + + if (_topology->getNbTriangles()==0) + { + msg_warning() << "BezierShellForceField: object must have a Triangular Set Topology."; + return; + } + + if (topologyMapper.get() != NULL) { + + shell::engine::JoinMeshPoints* jmp = topologyMapper.get(); + if (jmp->f_output_triangles.getValue().size() == 0) + { + msg_warning() << "Mapped topology must be triangular! No triangles found." ; + } else { + mapTopology = true; + } + } + + if (restShape.get() != NULL) { + // Listen for MeshChangedEvent + *this->f_listening.beginEdit() = true; + this->f_listening.endEdit(); + + // Check if there is same number of nodes + const VecCoord &rx = restShape.get()->f_position.getValue(); + if (!mapTopology) { + if (rx.size() != this->mstate->read(sofa::core::vec_id::read_access::position)->getValue().size()) { + msg_warning() << "Different number of nodes in rest shape and mechanical state!" ; + } + } else if (rx.size() != topologyMapper.get()->f_input_position.getValue().size()) { + msg_warning() << "Different number of nodes in rest shape and (original) mapped topology!" ; + } + + } else if (mapTopology) { + // Mapped topology, no changing rest shape + + } + + if (!mapTopology && restShape.get() == NULL) { + // No topology mapper, no changing rest shape -> normal behaviour + } + + // Compute the material matrices + computeMaterialMatrix(); + + type::vector& triangleInf = *(triangleInfo.beginEdit()); + + if (bMeasureStrain || bMeasureStress) + { + f_measuredValues.beginEdit()->resize(_topology->getNbPoints()); + f_measuredValues.endEdit(); + } + + /// Prepare to store info in the triangle array + triangleInf.resize(_topology->getNbTriangles()); + + for (sofa::Index i=0; i<_topology->getNbTriangles(); ++i) + { + triangleHandler->applyCreateFunction(i, triangleInf[i], _topology->getTriangle(i), (const sofa::type::vector< unsigned int > )0, (const sofa::type::vector< double >)0); + } + + triangleInfo.endEdit(); +} + +// -------------------------------------------------------------------------------------- +// --- Initialisation of the triangle that has to be done only once +// -------------------------------------------------------------------------------------- +template +void BezierShellForceField::initTriangleOnce(const int i, const Index&a, const Index&b, const Index&c) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &triangleInf[i]; + + // Store own index + tinfo->elementID = i; + + // Store indices of each vertex + tinfo->a = a; + tinfo->b = b; + tinfo->c = c; + + tinfo->measure.resize(3); + tinfo->measure[0].id = a; tinfo->measure[0].point = Vec3(0,0,0); + tinfo->measure[1].id = b; tinfo->measure[1].point = Vec3(1,0,0); + tinfo->measure[2].id = c; tinfo->measure[2].point = Vec3(0,1,0); + + + triangleInfo.endEdit(); +} + +// -------------------------------------------------------------------------------------- +// --- Initialisation of the triangle that should be done when rest shape changes +// -------------------------------------------------------------------------------------- +template +void BezierShellForceField::initTriangle(const int i) +{ + if (this->mstate == NULL) { + msg_warning() << "Missing mechanical state" ; + return; + } + + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &triangleInf[i]; + + Index a0 = tinfo->a; + Index b0 = tinfo->b; + Index c0 = tinfo->c; + + // Gets vertices of rest positions + const VecCoord& x0 = (restShape.get() != NULL) + // if having changing rest shape take it + ? restShape.get()->f_position.getValue() + : (mapTopology + // if rest shape is fixed but we have mapped topology use it + ? topologyMapper.get()->f_input_position.getValue() + // otherwise just take rest shape in mechanical state + : this->mstate->read(sofa::core::vec_id::read_access::position)->getValue() + ); + + // Compute the initial position and rotation of the reference frame + this->interpolateRefFrame(tinfo, Vec2(1.0/3.0,1.0/3.0)); + + // Compute positions in local frame + computeLocalTriangle(i, false); + + Vec3 GP(1.0/3.0, 1.0/3.0, 1.0/3.0); + computeInPlaneDisplacementGradient(tinfo->gradU, GP, *tinfo); + + // Initial positions + tinfo->restLocalPositions[0] = tinfo->frameOrientation * (x0[a0].getCenter() - tinfo->frameCenter); + tinfo->restLocalPositions[1] = tinfo->frameOrientation * (x0[b0].getCenter() - tinfo->frameCenter); + tinfo->restLocalPositions[2] = tinfo->frameOrientation * (x0[c0].getCenter() - tinfo->frameCenter); + + // Initial rotations +#ifdef CRQUAT + tinfo->restLocalOrientationsInv[0] = (tinfo->frameOrientationQ * x0[a0].getOrientation()).inverse(); + tinfo->restLocalOrientationsInv[1] = (tinfo->frameOrientationQ * x0[b0].getOrientation()).inverse(); + tinfo->restLocalOrientationsInv[2] = (tinfo->frameOrientationQ * x0[c0].getOrientation()).inverse(); +#else + x0[a0].getOrientation().toMatrix(tinfo->restLocalOrientationsInv[0]); + x0[b0].getOrientation().toMatrix(tinfo->restLocalOrientationsInv[1]); + x0[c0].getOrientation().toMatrix(tinfo->restLocalOrientationsInv[2]); + + tinfo->restLocalOrientationsInv[0].transpose( tinfo->frameOrientation * tinfo->restLocalOrientationsInv[0] ); + tinfo->restLocalOrientationsInv[1].transpose( tinfo->frameOrientation * tinfo->restLocalOrientationsInv[1] ); + tinfo->restLocalOrientationsInv[2].transpose( tinfo->frameOrientation * tinfo->restLocalOrientationsInv[2] ); +#endif + + // Compute strain-displacement matrices at Gauss points + computeStrainDisplacementMatrixMembrane(*tinfo); + computeStrainDisplacementMatrixBending(*tinfo); + + // Compute stiffness matrices K = ∫ J^T*M*J dV + computeStiffnessMatrixMembrane(tinfo->stiffnessMatrix, *tinfo); + computeStiffnessMatrixBending(tinfo->stiffnessMatrixBending, *tinfo); + + triangleInfo.endEdit(); +} + +// ------------------------ +// --- Compute the position of the Bézier points situated at the edges based on +// --- the normals at triangle nodes. +// --- +// --- NOTE: While the output vector contains 10 nodes only nodes at index 3-8 +// --- are filled in. +// --- +// --- We do that by computing the actual bezier nodes and then computing +// --- rotation relative to the node orientation: +// --- +// --- To maintain C^0 continuity the nodes have to satisfy a few conditions. +// --- We use the conditions outlined in [Ubach and Oñate 2010]. The node has +// --- to lie on the: +// --- +// --- (1) plane perpendicular to the normal at the normal at triangle node +// --- (2) plane that contains the curve of triangle's contour +// --- - we us the plane defined by the edge (director between nodes of the +// --- flat triangle) and the average between normals at the triangle nodes +// --- connected by the edge +// --- (3) plane perpendicular to the edge of the flat triangle placed at 1/3 +// --- of it's length +// --- +// --- TODO: check the books if this also means C^1 continuity, IMO it should. +// ------------------------ +template +void BezierShellForceField::computeEdgeBezierPoints( + const Index& a, const Index& b, const Index& c, + const VecCoord& x, + const type::vector& norms, + type::fixed_array &bezierPoints) +{ + if (norms.size() == 0) { + // No normals, assume flat triangles + + // Edge A-B + bezierPoints[3] = (x[b].getCenter() - x[a].getCenter())/3.0; + bezierPoints[6] = (x[a].getCenter() - x[b].getCenter())/3.0; + + // Edge B-C + bezierPoints[5] = (x[c].getCenter() - x[b].getCenter())/3.0; + bezierPoints[7] = (x[b].getCenter() - x[c].getCenter())/3.0; + + // Edge C-A + bezierPoints[8] = (x[a].getCenter() - x[c].getCenter())/3.0; + bezierPoints[4] = (x[c].getCenter() - x[a].getCenter())/3.0; + + return; + } + + // Edge A-B + Vec3 n = (norms[a] + norms[b]) / 2.0; + n.normalize(); + + Vec3 e = x[b].getCenter() - x[a].getCenter(); + Real elen = e.norm()/3.0; + e.normalize(); + + Mat33 M, MI; + + // (1) (2) (3) + M = Mat33(norms[a], cross(e,n), e); + + // Solve M*x = (0, 0, |e|/3) + MI.invert(M); + bezierPoints[3] = MI * Vec3(0, 0, elen); + + M = Mat33(norms[b], cross(-e,n), -e); + MI.invert(M); + bezierPoints[6] = MI * Vec3(0, 0, elen); + + + // Edge B-C + n = (norms[b] + norms[c]) / 2.0; + n.normalize(); + + e = x[c].getCenter() - x[b].getCenter(); + elen = e.norm()/3.0; + e.normalize(); + + M = Mat33(norms[b], cross(e,n), e); + MI.invert(M); + bezierPoints[5] = MI * Vec3(0, 0, elen); + + M = Mat33(norms[c], cross(-e,n), -e); + MI.invert(M); + bezierPoints[7] = MI * Vec3(0, 0, elen); + + + // Edge C-A + n = (norms[c] + norms[a]) / 2.0; + n.normalize(); + + e = x[a].getCenter() - x[c].getCenter(); + elen = e.norm()/3.0; + e.normalize(); + + M = Mat33(norms[c], cross(e,n), e); + MI.invert(M); + bezierPoints[8] = MI * Vec3(0, 0, elen); + + M = Mat33(norms[a], cross(-e,n), -e); + MI.invert(M); + bezierPoints[4] = MI * Vec3(0, 0, elen); +} + +#if 0 +template +void BezierShellForceField::bezierFunctions(const Vec2& baryCoord, sofa::type::fixed_array &f_bezier) +{ + Real a=1-baryCoord[0]-baryCoord[1]; + Real b=baryCoord[0]; + Real c=baryCoord[1]; + + f_bezier[0]= a*a*a; + f_bezier[1]= b*b*b; + f_bezier[2]= c*c*c; + f_bezier[3]=3*a*a*b; f_bezier[4]=3*a*a*c; + f_bezier[5]=3*b*b*c; f_bezier[6]=3*b*b*a; + f_bezier[7]=3*c*c*a; f_bezier[8]=3*c*c*b; + f_bezier[9]=6*a*b*c; +} + +template +void BezierShellForceField::bezierDerivateFunctions(const Vec2& baryCoord, sofa::type::fixed_array &df_dx_bezier, sofa::type::fixed_array &df_dy_bezier) +{ + Real a=1-baryCoord[0]-baryCoord[1]; + Real b=baryCoord[0]; + Real c=baryCoord[1]; + + df_dx_bezier[0]= -3.0*a*a; + df_dx_bezier[1]= 3.0*b*b; + df_dx_bezier[2]= 0; + df_dx_bezier[3]= -6.0*a*b+3.0*a*a ; df_dx_bezier[4]= -6.0*a*c; + df_dx_bezier[5]= 6.0*b*c; df_dx_bezier[6]=6.0*b*a - 3.0*b*b; + df_dx_bezier[7]= -3.0*c*c; df_dx_bezier[8]=3.0*c*c; + df_dx_bezier[9]= -6.0*b*c + 6.0*a*c; + + df_dy_bezier[0]= -3.0*a*a; + df_dy_bezier[1]= 0.0; + df_dy_bezier[2]= 3.0*c*c; + df_dy_bezier[3]=-6.0*a*b; df_dy_bezier[4]=-6.0*a*c+3.0*a*a; + df_dy_bezier[5]=3.0*b*b; df_dy_bezier[6]=-3.0*b*b; + df_dy_bezier[7]=-3.0*c*c+6.0*c*a; df_dy_bezier[8]=6.0*c*b; + df_dy_bezier[9]=-6.0*b*c + 6.0*a*b; +} +#endif + +template +void BezierShellForceField::interpolateRefFrame(TriangleInformation *tinfo, + const Vec2& /*baryCoord*/) +{ +#if 0 // Reference frame by rotation at the center of the bezier triangle + + Vec3 bc(1 - baryCoord[0] - baryCoord[1], baryCoord[0], baryCoord[1]); + Vec3 pt, normal, X1, Y1; + + bsInterpolation->interpolateOnBTriangle(tinfo->elementID, bc, pt, normal, X1, Y1); + tinfo->frameCenter = pt; + +#else // Reference frame by the corner nodes + + // Get the position of Bézier points + sofa::type::fixed_array X_bezierPoints; + bsInterpolation->getBezierNodes(tinfo->elementID, X_bezierPoints); + + tinfo->frameCenter = (X_bezierPoints[0] + X_bezierPoints[1] + X_bezierPoints[2])/3; + + Vec3 X1 = X_bezierPoints[1] - X_bezierPoints[0], + Y1 = X_bezierPoints[2] - X_bezierPoints[0]; + +#endif + + // compute the orthogonal frame directions + Vec3 Y,Z; + Real X1n = X1.norm(), Y1n = Y1.norm(); + if (X1n > 1e-20 && Y1n > 1e-20 && fabs(1-dot(X1,Y1)/(X1n*Y1n)) >1e-20 ) + { + X1.normalize(); + //Y1.normalize(); + Z=cross(X1,Y1); + Z.normalize(); + Y=cross(Z,X1); + //Y.normalize(); + } + else + { + msg_warning()<<" WARNING : can not compute the Ref FRame of the element: " + << X_bezierPoints[0] << ", " << X_bezierPoints[1] << ", " + << X_bezierPoints[2] << + " tangent: " << X1 << ", " << Y1 ; + X1=Vec3(1.0,0.0,0.0); + Y =Vec3(0.0,1.0,0.0); + Z =Vec3(0.0,0.0,1.0); + } + + // compute the corresponding rotation + Mat33 R(X1,Y,Z); + tinfo->frameOrientation = R; + tinfo->frameOrientationInv.transpose(R); + +#ifdef CRQUAT + tinfo->frameOrientationQ.fromMatrix(tinfo->frameOrientation); + tinfo->frameOrientationQ.normalize(); +#endif +} + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +void BezierShellForceField::applyStiffness(VecDeriv& v, const VecDeriv& dx, const Index elementIndex, const double kFactor) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &triangleInf[elementIndex]; + + // Get the indices of the 3 vertices for the current triangle + const Index& a = tinfo->a; + const Index& b = tinfo->b; + const Index& c = tinfo->c; + + // Computes in-plane displacements and bending displacements + Displacement Disp; + DisplacementBending Disp_bending; + Vec3 x, o; + + x = tinfo->frameOrientation * getVCenter(dx[a]); + o = tinfo->frameOrientation * getVOrientation(dx[a]); + Disp[0] = x[0]; + Disp[1] = x[1]; + Disp[2] = o[2]; + Disp_bending[0] = x[2]; + Disp_bending[1] = o[0]; + Disp_bending[2] = o[1]; + + x = tinfo->frameOrientation * getVCenter(dx[b]); + o = tinfo->frameOrientation * getVOrientation(dx[b]); + Disp[3] = x[0]; + Disp[4] = x[1]; + Disp[5] = o[2]; + Disp_bending[3] = x[2]; + Disp_bending[4] = o[0]; + Disp_bending[5] = o[1]; + + x = tinfo->frameOrientation * getVCenter(dx[c]); + o = tinfo->frameOrientation * getVOrientation(dx[c]); + Disp[6] = x[0]; + Disp[7] = x[1]; + Disp[8] = o[2]; + Disp_bending[6] = x[2]; + Disp_bending[7] = o[0]; + Disp_bending[8] = o[1]; + + // Compute dF + Displacement dF; + dF = tinfo->stiffnessMatrix * Disp; + + // Compute dF_bending + DisplacementBending dF_bending; + dF_bending = tinfo->stiffnessMatrixBending * Disp_bending; + + // Go back into global frame + Vec3 fa1, fa2, fb1, fb2, fc1, fc2; + fa1 = tinfo->frameOrientationInv * Vec3(dF[0], dF[1], dF_bending[0]); + fa2 = tinfo->frameOrientationInv * Vec3(dF_bending[1], dF_bending[2], dF[2]); + + fb1 = tinfo->frameOrientationInv * Vec3(dF[3], dF[4], dF_bending[3]); + fb2 = tinfo->frameOrientationInv * Vec3(dF_bending[4], dF_bending[5], dF[5]); + + fc1 = tinfo->frameOrientationInv * Vec3(dF[6], dF[7], dF_bending[6]); + fc2 = tinfo->frameOrientationInv * Vec3(dF_bending[7], dF_bending[8], dF[8]); + + v[a] += Deriv(-fa1, -fa2) * kFactor; + v[b] += Deriv(-fb1, -fb2) * kFactor; + v[c] += Deriv(-fc1, -fc2) * kFactor; + + triangleInfo.endEdit(); +} + +// ----------------------------------------------------------------------------- +// --- Compute all nodes of the Bézier triangle in local frame +// --- +// --- If @bFast is false only local position of the corer nodes is computed. +// ----------------------------------------------------------------------------- +template +void BezierShellForceField::computeLocalTriangle( + const Index elementIndex, bool bFast) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &triangleInf[elementIndex]; + + type::fixed_array bn; + bsInterpolation->getBezierNodes(tinfo->elementID, bn); + + type::fixed_array &pts = tinfo->pts; + + // The element is being rotated along the frame situated at the center of + // the element + + //// Rotate the already computed nodes + //msg_info() << "QFrame: " << tinfo->frame.getOrientation() ; + for (int i = 0; i < (bFast ? 3 : 10); i++) { +#ifdef CRQUAT + pts[i] = tinfo->frameOrientationQ.rotate(bn[i] - tinfo->frameCenter); +#else + pts[i] = tinfo->frameOrientation * (bn[i] - tinfo->frameCenter); +#endif + } + + if (bFast) return; + + // TODO: this is no longer correct, or is it? (the nodes of the shell don't + // lie on the plane of reference frame and have non-zero z-coordinate) + Mat<3, 3, Real> m; + m(0,0) = 1; m(0,1) = 1; m(0,2) = 1; + m(1,0) = pts[0][0]; m(1,1) = pts[1][0]; m(1,2) = pts[2][0]; + m(2,0) = pts[0][1]; m(2,1) = pts[1][1]; m(2,2) = pts[2][1]; + + tinfo->interpol.invert(m); + tinfo->area2 = cross(pts[1] - pts[0], pts[2] - pts[0]).norm(); + + triangleInfo.endEdit(); +} + +// ----------------------------------------------------------------------------- +// --- Compute displacement vectors for in-plane and bending deformations in +// --- co-rotational frame of reference. +// ----------------------------------------------------------------------------- +template +void BezierShellForceField::computeDisplacements( Displacement &Disp, DisplacementBending &BDisp, const VecCoord &x, TriangleInformation *tinfo) +{ + Index a = tinfo->a; + Index b = tinfo->b; + Index c = tinfo->c; + + // Rotations + +#ifdef CRQUAT + Quat Q0 = (tinfo->frameOrientationQ * x[a].getOrientation()) * tinfo->restLocalOrientationsInv[0]; + Quat Q1 = (tinfo->frameOrientationQ * x[b].getOrientation()) * tinfo->restLocalOrientationsInv[1]; + Quat Q2 = (tinfo->frameOrientationQ * x[c].getOrientation()) * tinfo->restLocalOrientationsInv[2]; +#else + Transformation tmp0, tmp1, tmp2; + x[a].getOrientation().toMatrix(tmp0); + x[b].getOrientation().toMatrix(tmp1); + x[c].getOrientation().toMatrix(tmp2); + + Quat Q0; Q0.fromMatrix( tinfo->frameOrientation * tmp0 * tinfo->restLocalOrientationsInv[0] ); + Quat Q1; Q1.fromMatrix( tinfo->frameOrientation * tmp1 * tinfo->restLocalOrientationsInv[1] ); + Quat Q2; Q2.fromMatrix( tinfo->frameOrientation * tmp2 * tinfo->restLocalOrientationsInv[2] ); + // TODO: can we do this without the quaternions? +#endif + + Vec3 dQ0 = Q0.toEulerVector(); + Vec3 dQ1 = Q1.toEulerVector(); + Vec3 dQ2 = Q2.toEulerVector(); + + //std::cout << "Θ: " << dQ0 << " || " << dQ1 << " || " << dQ2 ; + + //bending => rotation along X and Y + BDisp[1] = dQ0[0]; + BDisp[2] = dQ0[1]; + BDisp[4] = dQ1[0]; + BDisp[5] = dQ1[1]; + BDisp[7] = dQ2[0]; + BDisp[8] = dQ2[1]; + + // inPlane => rotation along Z + Disp[2] = dQ0[2]; + Disp[5] = dQ1[2]; + Disp[8] = dQ2[2]; + + // Translations + + // translation compute the current position of the node on the element frame + //Vec3 Center_T_node0 = _global_R_element.inverseRotate( x[a].getCenter() - tinfo->frame.getCenter() ); + //Vec3 Center_T_node1 = _global_R_element.inverseRotate( x[b].getCenter() - tinfo->frame.getCenter() ); + //Vec3 Center_T_node2 = _global_R_element.inverseRotate( x[c].getCenter() - tinfo->frame.getCenter() ); + + // Compare this current position with the rest position + Vec3 dX0 = tinfo->pts[0] - tinfo->restLocalPositions[0]; + Vec3 dX1 = tinfo->pts[1] - tinfo->restLocalPositions[1]; + Vec3 dX2 = tinfo->pts[2] - tinfo->restLocalPositions[2]; + + // inPlane => translation along X and Y + Disp[0] = dX0[0]; + Disp[1] = dX0[1]; + Disp[3] = dX1[0]; + Disp[4] = dX1[1]; + Disp[6] = dX2[0]; + Disp[7] = dX2[1]; + + // bending => translation along Z + BDisp[0] = dX0[2]; + BDisp[3] = dX1[2]; + BDisp[6] = dX2[2]; + + //std::cout << "u:"; + //for (int i=0; i<9; i++) { std::cout << " " << round(Disp[i]*1e8)/1e8; } + //std::cout << "\n "; + //for (int i=0; i<9; i++) { std::cout << " " << round(BDisp[i]*1e8)/1e8; } + //std::cout ; + + + //////////////////// comparison /////////////////////////////// +/* + Transform world_H_DofA(x[a].getCenter(), x[a].getOrientation()); + Transform world_H_DofB(x[b].getCenter(), x[b].getOrientation()); + Transform world_H_DofC(x[c].getCenter(), x[c].getOrientation()); + + + Quat world_R_local; world_R_local.fromMatrix(tinfo->frameOrientationInv); + Transform world_H_EltFrame(tinfo->frameCenter, world_R_local); + + + Transform EltFrame_H_DofA = world_H_EltFrame.inversed()*world_H_DofA; + Transform EltFrame_H_DofB = world_H_EltFrame.inversed()*world_H_DofB; + Transform EltFrame_H_DofC = world_H_EltFrame.inversed()*world_H_DofC; + + + + // get the rest position... + const VecCoord &x0 = (*this->mstate->getX0()); + Transform world_H_DofArest(x0[a].getCenter(), x0[a].getOrientation()); + Transform world_H_DofBrest(x0[b].getCenter(), x0[b].getOrientation()); + Transform world_H_DofCrest(x0[c].getCenter(), x0[c].getOrientation()); + + Transform DofA_REST_H_DofA_debug = world_H_DofArest.inversed()*world_H_DofA; + Transform DofB_REST_H_DofB_debug = world_H_DofBrest.inversed()*world_H_DofB; + Transform DofC_REST_H_DofC_debug = world_H_DofCrest.inversed()*world_H_DofC; + + + + Quat DofA_R_EltFrame_REST; + DofA_R_EltFrame_REST.fromMatrix(tinfo->restLocalOrientationsInv[0]); + Transform EltFrame_H_DofA_REST(tinfo->restLocalPositions[0], DofA_R_EltFrame_REST.inverse()); + Transform DofA_REST_H_DofA = EltFrame_H_DofA_REST.inversed()*EltFrame_H_DofA; + // SpatialVector DispA_in_DofA_REST = DofA_REST_H_DofA.CreateSpatialVector(); + SpatialVector DispA_in_DofA_REST = DofA_REST_H_DofA_debug.CreateSpatialVector(); + SpatialVector DispA_in_EltFrame; + DispA_in_EltFrame.setAngularVelocity(EltFrame_H_DofA.projectVector( DispA_in_DofA_REST.getAngularVelocity() ) ) ; + DispA_in_EltFrame.setLinearVelocity(EltFrame_H_DofA.projectVector( DispA_in_DofA_REST.getLinearVelocity() ) ) ; + + + Quat DofB_R_EltFrame_REST; + DofB_R_EltFrame_REST.fromMatrix(tinfo->restLocalOrientationsInv[1]); + Transform EltFrame_H_DofB_REST(tinfo->restLocalPositions[1], DofB_R_EltFrame_REST.inverse()); + Transform DofB_REST_H_DofB = EltFrame_H_DofB_REST.inversed()*EltFrame_H_DofB; + // SpatialVector DispB_in_DofB_REST = DofB_REST_H_DofB.CreateSpatialVector(); + SpatialVector DispB_in_DofB_REST = DofB_REST_H_DofB_debug.CreateSpatialVector(); + SpatialVector DispB_in_EltFrame; + DispB_in_EltFrame.setAngularVelocity(EltFrame_H_DofB.projectVector( DispB_in_DofB_REST.getAngularVelocity() ) ) ; + DispB_in_EltFrame.setLinearVelocity(EltFrame_H_DofB.projectVector( DispB_in_DofB_REST.getLinearVelocity() ) ) ; + + + Quat DofC_R_EltFrame_REST; + DofC_R_EltFrame_REST.fromMatrix(tinfo->restLocalOrientationsInv[2]); + Transform EltFrame_H_DofC_REST(tinfo->restLocalPositions[2], DofC_R_EltFrame_REST.inverse()); + Transform DofC_REST_H_DofC = EltFrame_H_DofC_REST.inversed()*EltFrame_H_DofC; + //SpatialVector DispC_in_DofC_REST = DofC_REST_H_DofC.CreateSpatialVector(); + SpatialVector DispC_in_DofC_REST = DofC_REST_H_DofC_debug.CreateSpatialVector(); + SpatialVector DispC_in_EltFrame; + DispC_in_EltFrame.setAngularVelocity(EltFrame_H_DofC.projectVector( DispC_in_DofC_REST.getAngularVelocity() ) ) ; + DispC_in_EltFrame.setLinearVelocity(EltFrame_H_DofC.projectVector( DispC_in_DofC_REST.getLinearVelocity() ) ) ; + + + + + // InPLANE + + Displacement DispBuf=Disp; + + // inPlane => translation along X and Y + Disp[0] = DispA_in_EltFrame.getLinearVelocity()[0]; + Disp[1] = DispA_in_EltFrame.getLinearVelocity()[1]; + Disp[3] = DispB_in_EltFrame.getLinearVelocity()[0]; + Disp[4] = DispB_in_EltFrame.getLinearVelocity()[1]; + Disp[6] = DispC_in_EltFrame.getLinearVelocity()[0]; + Disp[7] = DispC_in_EltFrame.getLinearVelocity()[1]; + + // bending => translation along Z + Disp[2] = DispA_in_EltFrame.getAngularVelocity()[2]; + Disp[5] = DispB_in_EltFrame.getAngularVelocity()[2]; + Disp[8] = DispC_in_EltFrame.getAngularVelocity()[2]; + + + Vec3 DispA_world = world_H_EltFrame.projectVector(Vec3(Disp[0],Disp[1],0)); + Vec3 DispB_world = world_H_EltFrame.projectVector(Vec3(Disp[3],Disp[4],0)); + Vec3 DispC_world = world_H_EltFrame.projectVector(Vec3(Disp[6],Disp[7],0)); +*/ + + + /* std::cout<<"*********\n \n EltFrame_H_DofA ="< +void BezierShellForceField::computeInPlaneDisplacementGradient(GradDisplacement& gradU, const Vec3& GP, const TriangleInformation &tinfo ) +{ + + // Directional vectors from corner nodes + Vec3 P4P1 = tinfo.pts[3] - tinfo.pts[0]; + Vec3 P5P1 = tinfo.pts[4] - tinfo.pts[0]; + Vec3 P6P2 = tinfo.pts[5] - tinfo.pts[1]; + Vec3 P7P2 = tinfo.pts[6] - tinfo.pts[1]; + Vec3 P8P3 = tinfo.pts[7] - tinfo.pts[2]; + Vec3 P9P3 = tinfo.pts[8] - tinfo.pts[2]; + Vec3 P10P1 = tinfo.pts[9] - tinfo.pts[0]; + Vec3 P10P2 = tinfo.pts[9] - tinfo.pts[1]; + Vec3 P10P3 = tinfo.pts[9] - tinfo.pts[2]; + +#ifndef GAUSS4 + Vec3 P(1, GP[0], GP[1]); + + Vec3 p; // Barycentric coordinates of the point GP + p[0] = tinfo.interpol.line(0)*P; + p[1] = tinfo.interpol.line(1)*P; + p[2] = tinfo.interpol.line(2)*P; + +#else + Vec3 p(GP[0], GP[1], 1-GP[0]-GP[1]); +#endif + + Real b1 = tinfo.interpol(0,1); + Real c1 = tinfo.interpol(0,2); + Real b2 = tinfo.interpol(1,1); + Real c2 = tinfo.interpol(1,2); + Real b3 = tinfo.interpol(2,1); + Real c3 = tinfo.interpol(2,2); + + Vec3 p2(p[0]*p[0], p[1]*p[1], p[2]*p[2]); // Squares of p + + // Derivatives of powers of p (by x and y) + Real DPhi1n3_x= 3.0 * b1 * p2[0]; + Real DPhi1n2_x= 2.0 * b1 * p[0]; + Real DPhi1n3_y= 3.0 * c1 * p2[0]; + Real DPhi1n2_y= 2.0 * c1 * p[0]; + Real DPhi2n3_x= 3.0 * b2 * p2[1]; + Real DPhi2n2_x= 2.0 * b2 * p[1]; + Real DPhi2n3_y= 3.0 * c2 * p2[1]; + Real DPhi2n2_y= 2.0 * c2 * p[1]; + Real DPhi3n3_x= 3.0 * b3 * p2[2]; + Real DPhi3n2_x= 2.0 * b3 * p[2]; + Real DPhi3n3_y= 3.0 * c3 * p2[2]; + Real DPhi3n2_y= 2.0 * c3 * p[2]; + + Real DPhi123_x = b1*p[1]*p[2] + p[0]*b2*p[2] + p[0]*p[1]*b3; + Real DPhi123_y = c1*p[1]*p[2] + p[0]*c2*p[2] + p[0]*p[1]*c3; + + // Derivatives of the U1, U2, U3 parts (with respect to x and y) + Real du_dx_U1= DPhi1n3_x + 3.0*p2[0]*b2 + 3.0*DPhi1n2_x*p[1] + 3.0*p2[0]*b3 + 3.0*DPhi1n2_x*p[2] + 2.0*DPhi123_x; + Real du_dx_U2= DPhi2n3_x + 3.0*p2[1]*b3 + 3.0*DPhi2n2_x*p[2] + 3.0*p2[1]*b1 + 3.0*DPhi2n2_x*p[0] + 2.0*DPhi123_x; + Real du_dx_U3= DPhi3n3_x + 3.0*p2[2]*b1 + 3.0*DPhi3n2_x*p[0] + 3.0*p2[2]*b2 + 3.0*DPhi3n2_x*p[1] + 2.0*DPhi123_x; + + // du_dx = du_dx_DU1 * dU1 + ... + + Real du_dy_U1= DPhi1n3_y + 3.0*p2[0]*c2 + 3.0*DPhi1n2_y*p[1] + 3.0*p2[0]*c3 + 3.0*DPhi1n2_y*p[2] + 2.0*DPhi123_y; + Real du_dy_U2= DPhi2n3_y + 3.0*p2[1]*c3 + 3.0*DPhi2n2_y*p[2] + 3.0*p2[1]*c1 + 3.0*DPhi2n2_y*p[0] + 2.0*DPhi123_y; + Real du_dy_U3= DPhi3n3_y + 3.0*p2[2]*c1 + 3.0*DPhi3n2_y*p[0] + 3.0*p2[2]*c2 + 3.0*DPhi3n2_y*p[1] + 2.0*DPhi123_y; + + + // Derivatives of Theta1..3 parts (with respect to x and y) + Real dux_dx_T1= 3.0*DPhi1n2_x*p[1]*P4P1[1] + 3.0*p2[0]*b2*P4P1[1] + 3.0*DPhi1n2_x*p[2]*P5P1[1] + 3.0*p2[0]*b3*P5P1[1] + 2.0*DPhi123_x*P10P1[1]; + Real duy_dx_T1=-3.0*DPhi1n2_x*p[1]*P4P1[0] - 3.0*p2[0]*b2*P4P1[0] - 3.0*DPhi1n2_x*p[2]*P5P1[0] - 3.0*p2[0]*b3*P5P1[0] - 2.0*DPhi123_x*P10P1[0]; + + Real dux_dy_T1= 3.0*DPhi1n2_y*p[1]*P4P1[1] + 3.0*p2[0]*c2*P4P1[1] + 3.0*DPhi1n2_y*p[2]*P5P1[1] + 3.0*p2[0]*c3*P5P1[1] + 2.0*DPhi123_y*P10P1[1]; + Real duy_dy_T1=-3.0*DPhi1n2_y*p[1]*P4P1[0] - 3.0*p2[0]*c2*P4P1[0] - 3.0*DPhi1n2_y*p[2]*P5P1[0] - 3.0*p2[0]*c3*P5P1[0] - 2.0*DPhi123_y*P10P1[0]; + + + Real dux_dx_T2= 3.0*DPhi2n2_x*p[2]*P6P2[1] + 3.0*p2[1]*b3*P6P2[1] + 3.0*DPhi2n2_x*p[0]*P7P2[1] + 3.0*p2[1]*b1*P7P2[1] + 2.0*DPhi123_x*P10P2[1]; + Real duy_dx_T2=-3.0*DPhi2n2_x*p[2]*P6P2[0] - 3.0*p2[1]*b3*P6P2[0] - 3.0*DPhi2n2_x*p[0]*P7P2[0] - 3.0*p2[1]*b1*P7P2[0] - 2.0*DPhi123_x*P10P2[0]; + + Real dux_dy_T2= 3.0*DPhi2n2_y*p[2]*P6P2[1] + 3.0*p2[1]*c3*P6P2[1] + 3.0*DPhi2n2_y*p[0]*P7P2[1] + 3.0*p2[1]*c1*P7P2[1] + 2.0*DPhi123_y*P10P2[1]; + Real duy_dy_T2=-3.0*DPhi2n2_y*p[2]*P6P2[0] - 3.0*p2[1]*c3*P6P2[0] - 3.0*DPhi2n2_y*p[0]*P7P2[0] - 3.0*p2[1]*c1*P7P2[0] - 2.0*DPhi123_y*P10P2[0]; + + + Real dux_dx_T3= 3.0*DPhi3n2_x*p[0]*P8P3[1] + 3.0*p2[2]*b1*P8P3[1] + 3.0*DPhi3n2_x*p[1]*P9P3[1] + 3.0*p2[2]*b2*P9P3[1] + 2.0*DPhi123_x*P10P3[1]; + Real duy_dx_T3=-3.0*DPhi3n2_x*p[0]*P8P3[0] - 3.0*p2[2]*b1*P8P3[0] - 3.0*DPhi3n2_x*p[1]*P9P3[0] - 3.0*p2[2]*b2*P9P3[0] - 2.0*DPhi123_x*P10P3[0]; + + Real dux_dy_T3= 3.0*DPhi3n2_y*p[0]*P8P3[1] + 3.0*p2[2]*c1*P8P3[1] + 3.0*DPhi3n2_y*p[1]*P9P3[1] + 3.0*p2[2]*c2*P9P3[1] + 2.0*DPhi123_y*P10P3[1]; + Real duy_dy_T3=-3.0*DPhi3n2_y*p[0]*P8P3[0] - 3.0*p2[2]*c1*P8P3[0] - 3.0*DPhi3n2_y*p[1]*P9P3[0] - 3.0*p2[2]*c2*P9P3[0] - 2.0*DPhi123_y*P10P3[0]; + + // dux/dx + gradU[0][0] = du_dx_U1; + gradU[0][1] = du_dx_U2; + gradU[0][2] = du_dx_U3; + gradU[0][3] = dux_dx_T1; + gradU[0][4] = dux_dx_T2; + gradU[0][5] = dux_dx_T3; + // dux/dy + gradU[1][0] = du_dy_U1; + gradU[1][1] = du_dy_U2; + gradU[1][2] = du_dy_U3; + gradU[1][3] = dux_dy_T1; + gradU[1][4] = dux_dy_T2; + gradU[1][5] = dux_dy_T3; + // duy/dx + gradU[2][0] = du_dx_U1; + gradU[2][1] = du_dx_U2; + gradU[2][2] = du_dx_U3; + gradU[2][3] = duy_dx_T1; + gradU[2][4] = duy_dx_T2; + gradU[2][5] = duy_dx_T3; + // duy/dy + gradU[3][0] = du_dy_U1; + gradU[3][1] = du_dy_U2; + gradU[3][2] = du_dy_U3; + gradU[3][3] = duy_dy_T1; + gradU[3][4] = duy_dy_T2; + gradU[3][5] = duy_dy_T3; +} + +template +void BezierShellForceField::fixFramePolar(const Displacement &Disp, Mat22 &R, TriangleInformation &tinfo) +{ + + //Vec3 GP(1.0/3.0, 1.0/3.0, 1.0/3.0); + //computeInPlaneDisplacementGradient(tinfo->gradU, GP, *tinfo); + + Mat22 gradient; + // dux/dx + gradient[0][0] = tinfo.gradU[0] * Vec<6,Real>(Disp[0], Disp[3], Disp[6], Disp[2], Disp[5], Disp[8]); + // dux/dy + gradient[0][1] = tinfo.gradU[1] * Vec<6,Real>(Disp[0], Disp[3], Disp[6], Disp[2], Disp[5], Disp[8]); + // duy/dx + gradient[1][0] = tinfo.gradU[2] * Vec<6,Real>(Disp[1], Disp[4], Disp[7], Disp[2], Disp[5], Disp[8]); + // duy/dy + gradient[1][1] = tinfo.gradU[3] * Vec<6,Real>(Disp[1], Disp[4], Disp[7], Disp[2], Disp[5], Disp[8]); + + + // gradient Pos = gradient(U) + I + gradient[0][0]+=1; + gradient[1][1]+=1; + + // get the rotation + R.clear(); + helper::Decompose::polarDecomposition(gradient, R); + + //std::cout<<"gradient = " << gradient << " Rotation = " << R << "\n"; + + // + // Scale the angle of rotation down because the polar decomposition tends + // to "overshoot" the best rotation. + // + Real theta; + //Real theta2; + if (R[0][0] >= 0.0) + { + if (R[1][0] >= 0.0) + { + theta = acos(R[0][0]); + //theta2 = asin(R[1][0]); + } + else + { + theta = - acos(R[0][0]); + //theta2 = asin(R[1][0]); + } + } + else + { + if (R[1][0] >= 0.0) + { + theta = acos(R[0][0]); + //theta2 = M_PI - asin(R[1][0]); + } + else + { + theta = - acos(R[0][0]); + //theta2 = M_PI - asin(R[1][0]); + } + } + + //if (abs(theta - theta2) > 1e-10) + //std::cout << "θ: "<< theta << " " << theta2 << "\n"; + + // The Magical Constant + theta *= 0.61; + + Mat22 R2; + R2[0][0] = R2[1][1] = cos(theta); + R2[1][0] = sin(theta); + R2[0][1] = - R2[1][0]; + + + // Create 3D rotation matrix from 2D, transposing the matrix for inverse rotation. + Transformation R3d; + R3d[0][0]= R2[0][0]; R3d[0][1]= R2[1][0]; + R3d[1][0]= R2[0][1]; R3d[1][1]= R2[1][1]; + R3d[2][2]=1.0; + /*Quat newFrame_R_oldFrame; + newFrame_R_oldFrame.fromMatrix(R3d);*/ + + + // Modification of the element frame + + // tinfo.frameOrientationInv = world_R_old + // R3d = new_R_old + // so modification of tinfo.frameOrientationInv + tinfo.frameOrientationInv = tinfo.frameOrientationInv* R3d.transposed(); + + // tinfo.frameOrientation = old_R_world + // R3d = new_R_old + // so modification of tinfo.frameOrientation + tinfo.frameOrientation = R3d * tinfo.frameOrientation; + + // Update node position in local frame + computeLocalTriangle(tinfo.elementID, true); +} + + +// ---------------------------------------------------------------------------- +// --- Compute the strain-displacement matrix for in-plane deformation +// ----------------------------------------------------------------------------- +template +void BezierShellForceField:: computeStrainDisplacementMatrixMembrane(TriangleInformation &tinfo) +{ + // Calculation of the 3 Gauss points +#ifndef GAUSS4 + Vec3 gaussPoint1 = tinfo.pts[0]*(2.0/3.0) + tinfo.pts[1]/6.0 + tinfo.pts[2]/6.0; + Vec3 gaussPoint2 = tinfo.pts[0]/6.0 + tinfo.pts[1]*(2.0/3.0) + tinfo.pts[2]/6.0; + Vec3 gaussPoint3 = tinfo.pts[0]/6.0 + tinfo.pts[1]/6.0 + tinfo.pts[2]*(2.0/3.0); + + matrixSDM(tinfo.strainDisplacementMatrix1, gaussPoint1, tinfo); + matrixSDM(tinfo.strainDisplacementMatrix2, gaussPoint2, tinfo); + matrixSDM(tinfo.strainDisplacementMatrix3, gaussPoint3, tinfo); +#else + for (int i=0; if_printLog.getValue()) { + msg_info() << "pts: " << tinfo.pts ; + msg_info() << "x2b: " << tinfo.interpol ; + +#ifdef GAUSS4 + msg_info() << "Bm:"; + for (int i=0; i(1, -5, 0, 1, -5, 0, 1, -5, 0); + + msg_info() << "-- Disp test Bm (u=" << u << ")\n"; +#ifdef GAUSS4 + for (int i=0; i +void BezierShellForceField::matrixSDM( + StrainDisplacement &J, const Vec3 &GP, const TriangleInformation& tinfo) +{ + // Directional vectors from corner nodes + Vec3 P4P1 = tinfo.pts[3] - tinfo.pts[0]; + Vec3 P5P1 = tinfo.pts[4] - tinfo.pts[0]; + Vec3 P6P2 = tinfo.pts[5] - tinfo.pts[1]; + Vec3 P7P2 = tinfo.pts[6] - tinfo.pts[1]; + Vec3 P8P3 = tinfo.pts[7] - tinfo.pts[2]; + Vec3 P9P3 = tinfo.pts[8] - tinfo.pts[2]; + Vec3 P10P1 = tinfo.pts[9] - tinfo.pts[0]; + Vec3 P10P2 = tinfo.pts[9] - tinfo.pts[1]; + Vec3 P10P3 = tinfo.pts[9] - tinfo.pts[2]; + +#ifndef GAUSS4 + Vec3 P(1, GP[0], GP[1]); + + Vec3 p; // Barycentric coordinates of the point GP + p[0] = tinfo.interpol.line(0)*P; + p[1] = tinfo.interpol.line(1)*P; + p[2] = tinfo.interpol.line(2)*P; + +#else + Vec3 p(GP[0], GP[1], 1-GP[0]-GP[1]); +#endif + + Real b1 = tinfo.interpol(0,1); + Real c1 = tinfo.interpol(0,2); + Real b2 = tinfo.interpol(1,1); + Real c2 = tinfo.interpol(1,2); + Real b3 = tinfo.interpol(2,1); + Real c3 = tinfo.interpol(2,2); + + Vec3 p2(p[0]*p[0], p[1]*p[1], p[2]*p[2]); // Squares of p + + // Derivatives of powers of p (by x and y) + Real DPhi1n3_x= 3.0 * b1 * p2[0]; + Real DPhi1n2_x= 2.0 * b1 * p[0]; + Real DPhi1n3_y= 3.0 * c1 * p2[0]; + Real DPhi1n2_y= 2.0 * c1 * p[0]; + Real DPhi2n3_x= 3.0 * b2 * p2[1]; + Real DPhi2n2_x= 2.0 * b2 * p[1]; + Real DPhi2n3_y= 3.0 * c2 * p2[1]; + Real DPhi2n2_y= 2.0 * c2 * p[1]; + Real DPhi3n3_x= 3.0 * b3 * p2[2]; + Real DPhi3n2_x= 2.0 * b3 * p[2]; + Real DPhi3n3_y= 3.0 * c3 * p2[2]; + Real DPhi3n2_y= 2.0 * c3 * p[2]; + + Real DPhi123_x = b1*p[1]*p[2] + p[0]*b2*p[2] + p[0]*p[1]*b3; + Real DPhi123_y = c1*p[1]*p[2] + p[0]*c2*p[2] + p[0]*p[1]*c3; + + // Derivatives of the U1, U2, U3 parts (with respect to x and y) + Real du_dx_U1= DPhi1n3_x + 3.0*p2[0]*b2 + 3.0*DPhi1n2_x*p[1] + 3.0*p2[0]*b3 + 3.0*DPhi1n2_x*p[2] + 2.0*DPhi123_x; + Real du_dx_U2= DPhi2n3_x + 3.0*p2[1]*b3 + 3.0*DPhi2n2_x*p[2] + 3.0*p2[1]*b1 + 3.0*DPhi2n2_x*p[0] + 2.0*DPhi123_x; + Real du_dx_U3= DPhi3n3_x + 3.0*p2[2]*b1 + 3.0*DPhi3n2_x*p[0] + 3.0*p2[2]*b2 + 3.0*DPhi3n2_x*p[1] + 2.0*DPhi123_x; + + // du_dx = du_dx_DU1 * dU1 + ... + + Real du_dy_U1= DPhi1n3_y + 3.0*p2[0]*c2 + 3.0*DPhi1n2_y*p[1] + 3.0*p2[0]*c3 + 3.0*DPhi1n2_y*p[2] + 2.0*DPhi123_y; + Real du_dy_U2= DPhi2n3_y + 3.0*p2[1]*c3 + 3.0*DPhi2n2_y*p[2] + 3.0*p2[1]*c1 + 3.0*DPhi2n2_y*p[0] + 2.0*DPhi123_y; + Real du_dy_U3= DPhi3n3_y + 3.0*p2[2]*c1 + 3.0*DPhi3n2_y*p[0] + 3.0*p2[2]*c2 + 3.0*DPhi3n2_y*p[1] + 2.0*DPhi123_y; + + + // Derivatives of Theta1..3 parts (with respect to x and y) + Real dux_dx_T1= 3.0*DPhi1n2_x*p[1]*P4P1[1] + 3.0*p2[0]*b2*P4P1[1] + 3.0*DPhi1n2_x*p[2]*P5P1[1] + 3.0*p2[0]*b3*P5P1[1] + 2.0*DPhi123_x*P10P1[1]; + Real duy_dx_T1=-3.0*DPhi1n2_x*p[1]*P4P1[0] - 3.0*p2[0]*b2*P4P1[0] - 3.0*DPhi1n2_x*p[2]*P5P1[0] - 3.0*p2[0]*b3*P5P1[0] - 2.0*DPhi123_x*P10P1[0]; + + Real dux_dy_T1= 3.0*DPhi1n2_y*p[1]*P4P1[1] + 3.0*p2[0]*c2*P4P1[1] + 3.0*DPhi1n2_y*p[2]*P5P1[1] + 3.0*p2[0]*c3*P5P1[1] + 2.0*DPhi123_y*P10P1[1]; + Real duy_dy_T1=-3.0*DPhi1n2_y*p[1]*P4P1[0] - 3.0*p2[0]*c2*P4P1[0] - 3.0*DPhi1n2_y*p[2]*P5P1[0] - 3.0*p2[0]*c3*P5P1[0] - 2.0*DPhi123_y*P10P1[0]; + + + Real dux_dx_T2= 3.0*DPhi2n2_x*p[2]*P6P2[1] + 3.0*p2[1]*b3*P6P2[1] + 3.0*DPhi2n2_x*p[0]*P7P2[1] + 3.0*p2[1]*b1*P7P2[1] + 2.0*DPhi123_x*P10P2[1]; + Real duy_dx_T2=-3.0*DPhi2n2_x*p[2]*P6P2[0] - 3.0*p2[1]*b3*P6P2[0] - 3.0*DPhi2n2_x*p[0]*P7P2[0] - 3.0*p2[1]*b1*P7P2[0] - 2.0*DPhi123_x*P10P2[0]; + + Real dux_dy_T2= 3.0*DPhi2n2_y*p[2]*P6P2[1] + 3.0*p2[1]*c3*P6P2[1] + 3.0*DPhi2n2_y*p[0]*P7P2[1] + 3.0*p2[1]*c1*P7P2[1] + 2.0*DPhi123_y*P10P2[1]; + Real duy_dy_T2=-3.0*DPhi2n2_y*p[2]*P6P2[0] - 3.0*p2[1]*c3*P6P2[0] - 3.0*DPhi2n2_y*p[0]*P7P2[0] - 3.0*p2[1]*c1*P7P2[0] - 2.0*DPhi123_y*P10P2[0]; + + + Real dux_dx_T3= 3.0*DPhi3n2_x*p[0]*P8P3[1] + 3.0*p2[2]*b1*P8P3[1] + 3.0*DPhi3n2_x*p[1]*P9P3[1] + 3.0*p2[2]*b2*P9P3[1] + 2.0*DPhi123_x*P10P3[1]; + Real duy_dx_T3=-3.0*DPhi3n2_x*p[0]*P8P3[0] - 3.0*p2[2]*b1*P8P3[0] - 3.0*DPhi3n2_x*p[1]*P9P3[0] - 3.0*p2[2]*b2*P9P3[0] - 2.0*DPhi123_x*P10P3[0]; + + Real dux_dy_T3= 3.0*DPhi3n2_y*p[0]*P8P3[1] + 3.0*p2[2]*c1*P8P3[1] + 3.0*DPhi3n2_y*p[1]*P9P3[1] + 3.0*p2[2]*c2*P9P3[1] + 2.0*DPhi123_y*P10P3[1]; + Real duy_dy_T3=-3.0*DPhi3n2_y*p[0]*P8P3[0] - 3.0*p2[2]*c1*P8P3[0] - 3.0*DPhi3n2_y*p[1]*P9P3[0] - 3.0*p2[2]*c2*P9P3[0] - 2.0*DPhi123_y*P10P3[0]; + + + J[0][0] = du_dx_U1; + J[0][1] = 0; + J[0][2] = -dux_dx_T1; + J[0][3] = du_dx_U2; + J[0][4] = 0; + J[0][5] = -dux_dx_T2; + J[0][6] = du_dx_U3; + J[0][7] = 0; + J[0][8] = -dux_dx_T3; + + J[1][0] = 0; + J[1][1] = du_dy_U1; + J[1][2] = -duy_dy_T1; + J[1][3] = 0; + J[1][4] = du_dy_U2; + J[1][5] = -duy_dy_T2; + J[1][6] = 0; + J[1][7] = du_dy_U3; + J[1][8] = -duy_dy_T3; + + J[2][0] = du_dy_U1; + J[2][1] = du_dx_U1; + J[2][2] = -dux_dy_T1 - duy_dx_T1; + J[2][3] = du_dy_U2; + J[2][4] = du_dx_U2; + J[2][5] = -duy_dx_T2 - dux_dy_T2; + J[2][6] = du_dy_U3; + J[2][7] = du_dx_U3; + J[2][8] = -duy_dx_T3 - dux_dy_T3; +} + + + + + + +// ------------------------------------------------------------------------------------------------------------ +// --- Compute the bending strain-displacement matrix where (a, b, c) are the coordinates of the 3 nodes of a triangle +// ------------------------------------------------------------------------------------------------------------ +template +void BezierShellForceField::computeStrainDisplacementMatrixBending(TriangleInformation &tinfo) +{ +#ifndef GAUSS4 + // Calculation of the 3 Gauss points + Vec3 gaussPoint1 = tinfo.pts[0]*(2.0/3.0) + tinfo.pts[1]/6.0 + tinfo.pts[2]/6.0; + Vec3 gaussPoint2 = tinfo.pts[0]/6.0 + tinfo.pts[1]*(2.0/3.0) + tinfo.pts[2]/6.0; + Vec3 gaussPoint3 = tinfo.pts[0]/6.0 + tinfo.pts[1]/6.0 + tinfo.pts[2]*(2.0/3.0); + + matrixSDB(tinfo.strainDisplacementMatrixB1, gaussPoint1, tinfo); + matrixSDB(tinfo.strainDisplacementMatrixB2, gaussPoint2, tinfo); + matrixSDB(tinfo.strainDisplacementMatrixB3, gaussPoint3, tinfo); +#else + for (int i=0; if_printLog.getValue()) { +#ifndef GAUSS4 + msg_info() << "Bb: " << tinfo.strainDisplacementMatrixB1 << + "\n " << tinfo.strainDisplacementMatrixB2 << + "\n " << tinfo.strainDisplacementMatrixB3 << + "\n"; +#else + msg_info() << "Bb:"; + for (int i=0; i +void BezierShellForceField::stressAtPoints(const VecVec3 &points, const VecIndex &elements) +{ + if (points.size() != elements.size()) + { + msg_warning() << "Invalid arguments for stressAtPoints(), sizes do not match." ; + return; + } + + type::vector& triangleInf = *(triangleInfo.beginEdit()); + + for (unsigned int t=0; tresize(points.size()); + f_measuredValues.endEdit(); + + if (!bMeasureStrain && !bMeasureStress) { + bMeasureStress = true; + f_measure.beginEdit()->setSelectedItem("Von Mises stress"); + f_measure.endEdit(); + } +} + +// ---------------------------------------------------------------------------- +// --- Compute the strain-displacement matrix for bending deformation +// ----------------------------------------------------------------------------- +template +void BezierShellForceField::matrixSDB( + StrainDisplacementBending &J, const Vec3 &GP, const TriangleInformation& tinfo) +{ + // Directional vectors from corner nodes + Vec3 P4P1 = tinfo.pts[0] - tinfo.pts[3] ; + Vec3 P5P1 = tinfo.pts[0] - tinfo.pts[4] ; + Vec3 P6P2 = tinfo.pts[1] - tinfo.pts[5] ; + Vec3 P7P2 = tinfo.pts[1] - tinfo.pts[6] ; + Vec3 P8P3 = tinfo.pts[2] - tinfo.pts[7] ; + Vec3 P9P3 = tinfo.pts[2] - tinfo.pts[8] ; + Vec3 P10P1 = tinfo.pts[0] - tinfo.pts[9] ; + Vec3 P10P2 = tinfo.pts[1] - tinfo.pts[9] ; + Vec3 P10P3 = tinfo.pts[2] - tinfo.pts[9] ; + + +#ifndef GAUSS4 + Vec3 P(1, GP[0], GP[1]); + + Vec3 p; // Barycentric coordinates of the point GP + p[0] = tinfo.interpol.line(0)*P; + p[1] = tinfo.interpol.line(1)*P; + p[2] = tinfo.interpol.line(2)*P; + +#else + Vec3 p(GP[0], GP[1], 1-GP[0]-GP[1]); +#endif + + Real b1 = tinfo.interpol(0,1); + Real c1 = tinfo.interpol(0,2); + Real b2 = tinfo.interpol(1,1); + Real c2 = tinfo.interpol(1,2); + Real b3 = tinfo.interpol(2,1); + Real c3 = tinfo.interpol(2,2); + + //Vec3 p2(p[0]*p[0], p[1]*p[1], p[2]*p[2]); // Squares of p + + // Doubles derivatives by x and y + Real D2Phi1n3_xx = 6*b1*b1*p[0]; + Real D2Phi1n3_xy = 6*b1*c1*p[0]; + Real D2Phi1n3_yy = 6*c1*c1*p[0]; + + Real D2Phi2n3_xx = 6*b2*b2*p[1]; + Real D2Phi2n3_xy = 6*b2*c2*p[1]; + Real D2Phi2n3_yy = 6*c2*c2*p[1]; + + Real D2Phi3n3_xx = 6*b3*b3*p[2]; + Real D2Phi3n3_xy = 6*b3*c3*p[2]; + Real D2Phi3n3_yy = 6*c3*c3*p[2]; + + Real D2Phi123_xx = 2.0*b1*b2*p[2] + 2.0*b1*p[1]*b3 + 2.0*p[0]*b2*b3; + Real D2Phi123_xy = c1*b2*p[2] + c1*p[1]*b3 + b1*c2*p[2]+ p[0]*c2*b3 + b1*p[1]*c3 + p[0]*b2*c3; + Real D2Phi123_yy = 2.0*c1*c2*p[2] + 2.0*c1*p[1]*c3 + 2.0*p[0]*c2*c3; + + Real D2Phi1n2Phi2_xx = 2.0*b1*b1*p[1]+ 4.0*p[0]*b1*b2; + Real D2Phi1n2Phi3_xx = 2.0*b1*b1*p[2]+ 4.0*p[0]*b1*b3; + Real D2Phi1n2Phi2_yy = 2.0*c1*c1*p[1]+ 4.0*p[0]*c1*c2; + Real D2Phi1n2Phi3_yy = 2.0*c1*c1*p[2]+ 4.0*p[0]*c1*c3; + Real D2Phi1n2Phi2_xy = 2.0*b1*c1*p[1]+ 2.0*b1*p[0]*c2 + 2.0*p[0]*c1*b2; + Real D2Phi1n2Phi3_xy = 2.0*b1*c1*p[2]+ 2.0*b1*p[0]*c3 + 2.0*p[0]*c1*b3; + + Real D2Phi2n2Phi1_xx = 2.0*b2*b2*p[0]+ 4.0*p[1]*b2*b1; + Real D2Phi2n2Phi3_xx = 2.0*b2*b2*p[2]+ 4.0*p[1]*b2*b3; + Real D2Phi2n2Phi1_yy = 2.0*c2*c2*p[0]+ 4.0*p[1]*c2*c1; + Real D2Phi2n2Phi3_yy = 2.0*c2*c2*p[2]+ 4.0*p[1]*c2*c3; + Real D2Phi2n2Phi1_xy = 2.0*b2*c2*p[0]+ 2.0*b2*p[1]*c1 + 2.0*p[1]*c2*b1; + Real D2Phi2n2Phi3_xy = 2.0*b2*c2*p[2]+ 2.0*b2*p[1]*c3 + 2.0*p[1]*c2*b3; + + Real D2Phi3n2Phi1_xx = 2.0*b3*b3*p[0]+ 4.0*p[2]*b3*b1; + Real D2Phi3n2Phi2_xx = 2.0*b3*b3*p[1]+ 4.0*p[2]*b3*b2; + Real D2Phi3n2Phi1_yy = 2.0*c3*c3*p[0]+ 4.0*p[2]*c3*c1; + Real D2Phi3n2Phi2_yy = 2.0*c3*c3*p[1]+ 4.0*p[2]*c3*c2; + Real D2Phi3n2Phi1_xy = 2.0*b3*c3*p[0]+ 2.0*b3*p[2]*c1 + 2.0*p[2]*c3*b1; + Real D2Phi3n2Phi2_xy = 2.0*b3*c3*p[1]+ 2.0*b3*p[2]*c2 + 2.0*p[2]*c3*b2; + + // Second derivatives of uz (with respect to x and y) -- translation parts + Real d2uz_dxx_dU1 = D2Phi1n3_xx + 3.0*D2Phi1n2Phi2_xx + 3.0*D2Phi1n2Phi3_xx + 2.0*D2Phi123_xx; + Real d2uz_dxy_dU1 = D2Phi1n3_xy + 3.0*D2Phi1n2Phi2_xy + 3.0*D2Phi1n2Phi3_xy + 2.0*D2Phi123_xy; + Real d2uz_dyy_dU1 = D2Phi1n3_yy + 3.0*D2Phi1n2Phi2_yy + 3.0*D2Phi1n2Phi3_yy + 2.0*D2Phi123_yy; + + Real d2uz_dxx_dU2 = D2Phi2n3_xx + 3.0*D2Phi2n2Phi1_xx + 3.0*D2Phi2n2Phi3_xx + 2.0*D2Phi123_xx; + Real d2uz_dxy_dU2 = D2Phi2n3_xy + 3.0*D2Phi2n2Phi1_xy + 3.0*D2Phi2n2Phi3_xy + 2.0*D2Phi123_xy; + Real d2uz_dyy_dU2 = D2Phi2n3_yy + 3.0*D2Phi2n2Phi1_yy + 3.0*D2Phi2n2Phi3_yy + 2.0*D2Phi123_yy; + + Real d2uz_dxx_dU3 = D2Phi3n3_xx + 3.0*D2Phi3n2Phi1_xx + 3.0*D2Phi3n2Phi2_xx + 2.0*D2Phi123_xx; + Real d2uz_dxy_dU3 = D2Phi3n3_xy + 3.0*D2Phi3n2Phi1_xy + 3.0*D2Phi3n2Phi2_xy + 2.0*D2Phi123_xy; + Real d2uz_dyy_dU3 = D2Phi3n3_yy + 3.0*D2Phi3n2Phi1_yy + 3.0*D2Phi3n2Phi2_yy + 2.0*D2Phi123_yy; + + // The macro gives a vector with first two values (the third one is 0) on + // the 3rd line of the 3x3 vector cross product matrix +#define CROSS_VEC(p) Vec2 cv##p(-(p)[1], (p)[0]) + CROSS_VEC(P4P1); CROSS_VEC(P6P2); CROSS_VEC(P8P3); + CROSS_VEC(P5P1); CROSS_VEC(P7P2); CROSS_VEC(P9P3); + CROSS_VEC(P10P1); CROSS_VEC(P10P2); CROSS_VEC(P10P3); +#undef CROSS_VEC + + // Second derivatives with of uz (with respect to x and y) -- rotation parts + Vec2 d2uz_dxx_dT1 = cvP4P1*3.0*D2Phi1n2Phi2_xx + cvP5P1*3.0*D2Phi1n2Phi3_xx + cvP10P1*2.0*D2Phi123_xx; + Vec2 d2uz_dxy_dT1 = cvP4P1*3.0*D2Phi1n2Phi2_xy + cvP5P1*3.0*D2Phi1n2Phi3_xy + cvP10P1*2.0*D2Phi123_xy; + Vec2 d2uz_dyy_dT1 = cvP4P1*3.0*D2Phi1n2Phi2_yy + cvP5P1*3.0*D2Phi1n2Phi3_yy + cvP10P1*2.0*D2Phi123_yy; + + Vec2 d2uz_dxx_dT2 = cvP6P2*3.0*D2Phi2n2Phi3_xx + cvP7P2*3.0*D2Phi2n2Phi1_xx + cvP10P2*2.0*D2Phi123_xx; + Vec2 d2uz_dxy_dT2 = cvP6P2*3.0*D2Phi2n2Phi3_xy + cvP7P2*3.0*D2Phi2n2Phi1_xy + cvP10P2*2.0*D2Phi123_xy; + Vec2 d2uz_dyy_dT2 = cvP6P2*3.0*D2Phi2n2Phi3_yy + cvP7P2*3.0*D2Phi2n2Phi1_yy + cvP10P2*2.0*D2Phi123_yy; + + Vec2 d2uz_dxx_dT3 = cvP8P3*3.0*D2Phi3n2Phi1_xx + cvP9P3*3.0*D2Phi3n2Phi2_xx + cvP10P3*2.0*D2Phi123_xx; + Vec2 d2uz_dxy_dT3 = cvP8P3*3.0*D2Phi3n2Phi1_xy + cvP9P3*3.0*D2Phi3n2Phi2_xy + cvP10P3*2.0*D2Phi123_xy; + Vec2 d2uz_dyy_dT3 = cvP8P3*3.0*D2Phi3n2Phi1_yy + cvP9P3*3.0*D2Phi3n2Phi2_yy + cvP10P3*2.0*D2Phi123_yy; + + + J[0][0] = -d2uz_dxx_dU1; + J[0][1] = -d2uz_dxx_dT1[0]; + J[0][2] = -d2uz_dxx_dT1[1]; + J[0][3] = -d2uz_dxx_dU2; + J[0][4] = -d2uz_dxx_dT2[0]; + J[0][5] = -d2uz_dxx_dT2[1]; + J[0][6] = -d2uz_dxx_dU3; + J[0][7] = -d2uz_dxx_dT3[0]; + J[0][8] = -d2uz_dxx_dT3[1]; + + J[1][0] = -d2uz_dyy_dU1; + J[1][1] = -d2uz_dyy_dT1[0]; + J[1][2] = -d2uz_dyy_dT1[1]; + J[1][3] = -d2uz_dyy_dU2; + J[1][4] = -d2uz_dyy_dT2[0]; + J[1][5] = -d2uz_dyy_dT2[1]; + J[1][6] = -d2uz_dyy_dU3; + J[1][7] = -d2uz_dyy_dT3[0]; + J[1][8] = -d2uz_dyy_dT3[1]; + + J[2][0] = -2.0*d2uz_dxy_dU1; + J[2][1] = -2.0*d2uz_dxy_dT1[0]; + J[2][2] = -2.0*d2uz_dxy_dT1[1]; + J[2][3] = -2.0*d2uz_dxy_dU2; + J[2][4] = -2.0*d2uz_dxy_dT2[0]; + J[2][5] = -2.0*d2uz_dxy_dT2[1]; + J[2][6] = -2.0*d2uz_dxy_dU3; + J[2][7] = -2.0*d2uz_dxy_dT3[0]; + J[2][8] = -2.0*d2uz_dxy_dT3[1]; +} + + + + +// ----------------------------------------------------------------------------- +// --- Compute the stiffness matrix K = J * M * Jt where J is the +// --- strain-displacement matrix and M the material matrix. Uses Gaussian +// --- quadrature for integration over the shell area. +// ----------------------------------------------------------------------------- +template +void BezierShellForceField::computeStiffnessMatrixMembrane( + StiffnessMatrix &K, const TriangleInformation &tinfo) +{ + Mat<9, 3, Real> Jt1, Jt2, Jt3; + Jt1.transpose(tinfo.strainDisplacementMatrix1); + Jt2.transpose(tinfo.strainDisplacementMatrix2); + Jt3.transpose(tinfo.strainDisplacementMatrix3); + +#ifndef GAUSS4 + K = Jt1 * materialMatrix * tinfo.strainDisplacementMatrix1 + + Jt2 * materialMatrix * tinfo.strainDisplacementMatrix2 + + Jt3 * materialMatrix * tinfo.strainDisplacementMatrix3; + K *= f_thickness.getValue(); + K /= 3.0; +#else + Mat<9, 3, Real> Jt; + K.clear(); + for (int i=0; if_printLog.getValue()) + { + msg_info() << "2*Area = " << tinfo.area2 ; + msg_info() << "Km = " << K ; + Displacement u = Vec<9,Real>(1, -5, 0, 1, -5, 0, 1, -5, 0); + msg_info() << "-- Disp test Km (u=" << u << ")" << + " : " << K * u << " ... should be zero" ; + } +} + + +// ----------------------------------------------------------------------------- +// --- Compute the stiffness matrix for bending K = J * M * Jt where J is the +// --- strain-displacement matrix and M the material matrix. Uses Gaussian +// --- quadrature for integration over the shell area. +// ----------------------------------------------------------------------------- +template +void BezierShellForceField::computeStiffnessMatrixBending(StiffnessMatrixBending &K, const TriangleInformation &tinfo) +{ + Real t = f_thickness.getValue(); + +#ifndef GAUSS4 + Mat<9, 3, Real> J1t, J2t, J3t; + J1t.transpose(tinfo.strainDisplacementMatrixB1); + J2t.transpose(tinfo.strainDisplacementMatrixB2); + J3t.transpose(tinfo.strainDisplacementMatrixB3); + + K = J1t * materialMatrix * tinfo.strainDisplacementMatrixB1 + + J2t * materialMatrix * tinfo.strainDisplacementMatrixB2 + + J3t * materialMatrix * tinfo.strainDisplacementMatrixB3; + K *= t*t*t / (Real)12.0; + K /= 3.0; +#else + Mat<9, 3, Real> Jt; + K.clear(); + for (int i=0; if_printLog.getValue()) + { + msg_info() << "Kb = " << K ; + } +} + +// ----------------------------------------------------------------------------- +// --- Compute material matrix for plan stress and bending (Hooke's law) +// --- NOTE: These are alraedy integrated over the thickness of the shell. +// ----------------------------------------------------------------------------- +template +void BezierShellForceField::computeMaterialMatrix() +{ + // Material matrix for plain stress + materialMatrix[0][0] = 1.0; + materialMatrix[0][1] = f_poisson.getValue(); + materialMatrix[0][2] = 0; + materialMatrix[1][0] = f_poisson.getValue(); + materialMatrix[1][1] = 1.0; + materialMatrix[1][2] = 0; + materialMatrix[2][0] = 0; + materialMatrix[2][1] = 0; + materialMatrix[2][2] = ((Real)1.0 - f_poisson.getValue())/(Real)2.0; + + materialMatrix *= f_young.getValue() / ( + (Real)1.0 - f_poisson.getValue() * f_poisson.getValue()); +} + + +// ----------------------------------------------------------------------------- +// --- Compute force F = J * material * Jt * u +// ----------------------------------------------------------------------------- +template +void BezierShellForceField::computeForceMembrane( + Displacement &F, const Displacement& D, const Index elementIndex) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation &tinfo = triangleInf[elementIndex]; + + // Compute forces + F = tinfo.stiffnessMatrix * D; + + triangleInfo.endEdit(); +} + + +// -------------------------------------------------------------------------------------- +// --- Compute force F = Jt * material * J * u +// -------------------------------------------------------------------------------------- +template +void BezierShellForceField::computeForceBending(DisplacementBending &F_bending, const DisplacementBending& D_bending, const Index elementIndex) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation &tinfo = triangleInf[elementIndex]; + + // Compute forces + F_bending = tinfo.stiffnessMatrixBending * D_bending; + + triangleInfo.endEdit(); +} + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +void BezierShellForceField::accumulateForce(VecDeriv &f, const VecCoord &x, const Index elementIndex) +{ + type::vector& triangleInf = *(triangleInfo.beginEdit()); + TriangleInformation *tinfo = &triangleInf[elementIndex]; + + // Get the indices of the 3 vertices for the current triangle + const Index& a = tinfo->a; + const Index& b = tinfo->b; + const Index& c = tinfo->c; + + // Compute the quaternion that embodies the rotation between the triangle + // and world frames (co-rotational method) + interpolateRefFrame(tinfo, Vec2(1.0/3.0, 1.0/3.0)); + + computeLocalTriangle(elementIndex, true); + + // Compute in-plane and bending displacements in the triangle's frame + Displacement D; + DisplacementBending D_bending; + computeDisplacements(D, D_bending, x, tinfo); + + // Use polar decomposition to fix the frame (inPlane) + Mat22 R; + //bool bStop = false; + for (unsigned int i=0; i 0) + // std::cerr << "."; + // std::cout << elementIndex << "\n"; + + // TODO: is this necessary? + computeLocalTriangle(elementIndex, false); + + // Compute in-plane forces on this element (in the co-rotational space) + Displacement F; + computeForceMembrane(F, D, elementIndex); + + // Compute bending forces on this element (in the co-rotational space) + DisplacementBending F_bending; + computeForceBending(F_bending, D_bending, elementIndex); + + // Compute the measure to draw + if (bMeasureStrain) { + type::vector &values = *f_measuredValues.beginEdit(); + for (unsigned int i=0; i< tinfo->measure.size(); i++) { + Vec3 strain = tinfo->measure[i].B * D + tinfo->measure[i].Bb * D_bending; + // Norm from strain in x and y + // NOTE: Shear strain is not included + values[ tinfo->measure[i].id ] = helper::rsqrt( + strain[0] * strain[0] + strain[1] * strain[1]); + } + f_measuredValues.endEdit(); + } else if (bMeasureStress) { + type::vector &values = *f_measuredValues.beginEdit(); + for (unsigned int i=0; i< tinfo->measure.size(); i++) { + Vec3 stress = materialMatrix * tinfo->measure[i].B * D + + materialMatrix * tinfo->measure[i].Bb * D_bending; + // Von Mises stress criterion (plane stress) + values[ tinfo->measure[i].id ] = helper::rsqrt( + stress[0] * stress[0] - stress[0] * stress[1] + + stress[1] * stress[1] + 3 * stress[2] * stress[2]); + } + f_measuredValues.endEdit(); + } + + + // Transform forces back into global reference frame + Vec3 fa1 = tinfo->frameOrientationInv * Vec3(F[0], F[1], F_bending[0]); + Vec3 fa2 = tinfo->frameOrientationInv * Vec3(F_bending[1], F_bending[2], F[2]); + + Vec3 fb1 = tinfo->frameOrientationInv * Vec3(F[3], F[4], F_bending[3]); + Vec3 fb2 = tinfo->frameOrientationInv * Vec3(F_bending[4], F_bending[5], F[5]); + + Vec3 fc1 = tinfo->frameOrientationInv * Vec3(F[6], F[7], F_bending[6]); + Vec3 fc2 = tinfo->frameOrientationInv * Vec3(F_bending[7], F_bending[8], F[8]); + + if (this->f_printLog.getValue()) { + std::cout << "E: " << elementIndex << "\tu: " << D << "\n\tf: " << F << "\n"; + std::cout << "E: " << elementIndex << "\tuB: " << D_bending + << "\n\tfB: " << F_bending << "\n"; + std::cout << " xg [ " << a << "/" << b << "/" << c << " - " + << x[a] << " || " << x[b] << " || " << x[c] << "\n"; + std::cout << " xl [ " << tinfo->pts[0] << ", " << tinfo->pts[1] << ", " << tinfo->pts[2] << "\n"; + std::cout << " fg: " << Deriv(-fa1, -fa2) << " | " << Deriv(-fb1, -fb2) << " | " << Deriv(-fc1, -fc2) ; + } + + + f[a] += Deriv(-fa1, -fa2); + f[b] += Deriv(-fb1, -fb2); + f[c] += Deriv(-fc1, -fc2); + + triangleInfo.endEdit(); +} + + +// -------------------------------------------------------------------------------------- +// --- +// -------------------------------------------------------------------------------------- +template +void BezierShellForceField::addForce(const sofa::core::MechanicalParams* /*mparams*/, DataVecDeriv& dataF, const DataVecCoord& dataX, const DataVecDeriv& /*dataV*/ ) +{ +// std::cout << "addForce\n"; + + VecDeriv& f = *(dataF.beginEdit()); + const VecCoord& p = dataX.getValue() ; + +// sofa::helper::system::thread::ctime_t start, stop; +// sofa::helper::system::thread::CTime timer; +// +// start = timer.getTime(); + + int nbTriangles=_topology->getNbTriangles(); + f.resize(p.size()); + + for (int i=0; ikFactor(); + + int nbTriangles=_topology->getNbTriangles(); + df.resize(dp.size()); + + for (int i=0; i +void BezierShellForceField::convertStiffnessMatrixToGlobalSpace(StiffnessMatrixGlobalSpace &K_gs, TriangleInformation *tinfo) +{ + // Stiffness matrix of current triangle + const StiffnessMatrix &K = tinfo->stiffnessMatrix; + + // Firstly, add all degrees of freedom (we add the unused translation in z) + StiffnessMatrixGlobalSpace K_18x18; + K_18x18.clear(); + unsigned int ig, jg; + + // Copy the stiffness matrix into 18x18 matrix (the new index of each bloc into global matrix is a combination of 0, 6 and 12 in indices) + for (unsigned int bx=0; bx<3; bx++) + { + // Global row index + ig = 6*bx; + + for (unsigned int by=0; by<3; by++) + { + // Global column index + jg = 6*by; + + // linear X + K_18x18[ig+0][jg+0] = K[3*bx+0][3*by+0]; // linear X + K_18x18[ig+0][jg+1] = K[3*bx+0][3*by+1]; // linear Y + K_18x18[ig+0][jg+5] = K[3*bx+0][3*by+2]; // angular Z + + // linear Y + K_18x18[ig+1][jg+0] = K[3*bx+1][3*by+0]; // linear X + K_18x18[ig+1][jg+1] = K[3*bx+1][3*by+1]; // linear Y + K_18x18[ig+1][jg+5] = K[3*bx+1][3*by+2]; // angular Z + + // angular Z + K_18x18[ig+5][jg+0] = K[3*bx+2][3*by+0]; // linear X + K_18x18[ig+5][jg+1] = K[3*bx+2][3*by+1]; // linear Y + K_18x18[ig+5][jg+5] = K[3*bx+2][3*by+2]; // angular Z + } + } + + + // Stiffness matrix in bending of current triangle + const StiffnessMatrixBending &K_bending = tinfo->stiffnessMatrixBending; + + // Copy the stiffness matrix by block 3x3 into global matrix (the new index of each bloc into global matrix is a combination of 2, 8 and 15 in indices) + for (unsigned int bx=0; bx<3; bx++) + { + // Global row index + ig = 6*bx+2; + + for (unsigned int by=0; by<3; by++) + { + // Global column index + jg = 6*by+2; + + // Iterates over the indices of the 3x3 block + for (unsigned int i=0; i<3; i++) + { + for (unsigned int j=0; j<3; j++) + { + K_18x18[ig+i][jg+j] += K_bending[3*bx+i][3*by+j]; + } + } + + } + } + + // Extend rotation matrix and its transpose + StiffnessMatrixGlobalSpace R18x18, Rt18x18; + + for(unsigned int i=0;i<3;++i) + { + for(unsigned int j=0;j<3;++j) + { + R18x18[i][j] = R18x18[i+3][j+3] = R18x18[i+6][j+6] = R18x18[i+9][j+9] = R18x18[i+12][j+12] = R18x18[i+15][j+15] = + tinfo->frameOrientation[i][j]; + Rt18x18[i][j] = Rt18x18[i+3][j+3] = Rt18x18[i+6][j+6] = Rt18x18[i+9][j+9] = Rt18x18[i+12][j+12] = Rt18x18[i+15][j+15] = + tinfo->frameOrientationInv[i][j]; + } + } + + /* + std::cout<<"R18x18 ="<getNbTriangles() ; ++t) + { + TriangleInformation *tinfo = &triangleInf[t]; + const Triangle triangle = _topology->getTriangle(t); + + convertStiffnessMatrixToGlobalSpace(K_gs, tinfo); + + // find index of node 1 + for (n1=0; n1<3; n1++) + { + node1 = triangle[n1]; + + for(i=0; i<6; i++) + { + ROW = r.offset+6*node1+i; + row = 6*n1+i; + // find index of node 2 + for (n2=0; n2<3; n2++) + { + node2 = triangle[n2]; + + for (j=0; j<6; j++) + { + COLUMN = r.offset+6*node2+j; + column = 6*n2+j; + r.matrix->add(ROW, COLUMN, - K_gs[row][column] * kFactor); + + } + } + } + } + } + + + #ifdef PRINT + std::cout << "Global matrix (" << r.matrix->rowSize() << "x" << r.matrix->colSize() << ")" ; + for (unsigned int i=0; irowSize(); i++) + { + for (unsigned int j=0; jcolSize(); j++) + { + std::cout << r.matrix->element(i,j) << ","; + } + std::cout ; + } + #endif + + triangleInfo.endEdit(); +} + + +#else + +template +void BezierShellForceField::addKToMatrix(sofa::linearalgebra::BaseMatrix *mat, SReal /*k*/, unsigned int &offset) +{ + VecCoord X = *this->mstate->getX(); + VecDeriv df, dx; + + dx.resize(X.size()); + df.resize(X.size()); + + for (unsigned int i=0; iadd(6*k+l, 6*i+j, -df[k][l]); + } + } + + } + } + + #ifdef PRINT + std::cout << "Global matrix (" << mat->rowSize() << "x" << mat->colSize() << ")" ; + for (unsigned int i=0; irowSize(); i++) + { + for (unsigned int j=0; jcolSize(); j++) + { + std::cout << mat->element(i,j) << "," ; + } + std::cout ; + } + #endif +} + +#endif + + +template +void BezierShellForceField::handleEvent(sofa::core::objectmodel::Event *event) +{ + if ( /*sofa::core::objectmodel::MeshChangedEvent* ev =*/ dynamic_cast(event)) + { + // Update of the rest shape + // NOTE: the number of triangles should be the same in all topologies + unsigned int nbTriangles = _topology->getNbTriangles(); + for (unsigned int i=0; i +void BezierShellForceField::HSL2RGB(Vec3 &rgb, Real h, Real sl, Real l) +{ + Real v; + Real r,g,b; + + r = l; // default to gray + g = l; + b = l; + v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl); + if (v > 0) + { + Real m; + Real sv; + int sextant; + Real fract, vsf, mid1, mid2; + + m = l + l - v; + sv = (v - m ) / v; + h *= 6.0; + sextant = (int)h; + fract = h - sextant; + vsf = v * sv * fract; + mid1 = m + vsf; + mid2 = v - vsf; + switch (sextant) + { + case 0: + r = v; + g = mid1; + b = m; + break; + case 1: + r = mid2; + g = v; + b = m; + break; + case 2: + r = m; + g = v; + b = mid1; + break; + case 3: + r = m; + g = mid2; + b = v; + break; + case 4: + r = mid1; + g = m; + b = v; + break; + case 5: + r = v; + g = m; + b = mid2; + break; + } + } + + rgb[0] = r; + rgb[1] = g; + rgb[2] = b; +} + + +template +void BezierShellForceField::draw(const core::visual::VisualParams* vparams) +{ + // TODO: draw the mapping between topologies. I'm not sure whether to put + // it in showForceField or showBehaviorModels + if(vparams->displayFlags().getShowForceFields()) + { + type::vector& triangleInf = *(triangleInfo.beginEdit()); + + // Render Bezier points + if (f_drawNodes.getValue()) { + + glPointSize(f_drawPointSize.getValue()); + glDisable(GL_LIGHTING); + glBegin(GL_POINTS); + + sofa::type::fixed_array bn; + for (sofa::Index i=0; i<_topology->getNbTriangles(); ++i) + { + TriangleInformation *tinfo = &triangleInf[i]; + bsInterpolation->getBezierNodes(tinfo->elementID, bn); + + for (int j=0; j<9; j++) + { + glColor4f(0.0, 0.7, 0.0, 1.0); + glVertex3f(bn[j][0], bn[j][1], bn[j][2]); + } + + // Central node in lighter color + glColor4f(0.0, 1.0, 0.0, 1.0); + glVertex3f(bn[9][0], bn[9][1], bn[9][2]); + } + + glEnd(); + glPointSize(1); + } + + // Render the frame of each element + if (f_drawFrame.getValue()) { + for (sofa::Index i=0; i<_topology->getNbTriangles(); ++i) + { + TriangleInformation *tinfo = &triangleInf[i]; + +#ifdef CRQUAT + Quat qFrame = tinfo->frameOrientationQ.inverse(); +#else + Quat qFrame; + qFrame.fromMatrix(tinfo->frameOrientationInv); +#endif + vparams->drawTool()->drawFrame( + tinfo->frameCenter, + qFrame, + Vec3(tinfo->area2, tinfo->area2, tinfo->area2)/4); + + } + } + + triangleInfo.endEdit(); + } // if(getShowForceFields()) +} + +} // namespace forcefield + +} // namespace component + +} // namespace sofa + + +#endif // #ifndef SOFA_COMPONENT_FORCEFIELD_BEZIERSHELLFORCEFIELD_INL diff --git a/src/Shell/shells2/mapping/BezierShellMechanicalMapping.cpp b/src/Shell/shells2/mapping/BezierShellMechanicalMapping.cpp new file mode 100644 index 0000000..51aed5c --- /dev/null +++ b/src/Shell/shells2/mapping/BezierShellMechanicalMapping.cpp @@ -0,0 +1,55 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include +#include +#include +#include +#include + +namespace sofa +{ + +namespace component +{ + +namespace mapping +{ + +using namespace sofa::defaulttype; +using namespace core; +using namespace core::behavior; + +int BezierShellMechanicalMappingClass = core::RegisterObject("Mechanical mapping between triangular shell and a cloud of points") +.add< BezierShellMechanicalMapping< Rigid3Types, Vec3Types > >() +; + +template class SOFA_SHELL_API BezierShellMechanicalMapping< Rigid3Types, Vec3Types >; + +} // namespace mapping + +} // namespace component + +} // namespace sofa diff --git a/src/Shell/shells2/mapping/BezierShellMechanicalMapping.h b/src/Shell/shells2/mapping/BezierShellMechanicalMapping.h new file mode 100644 index 0000000..bb41c5e --- /dev/null +++ b/src/Shell/shells2/mapping/BezierShellMechanicalMapping.h @@ -0,0 +1,245 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#ifndef SOFA_COMPONENT_MAPPING_BEZIERSHELLMECHANICALMAPPING_H +#define SOFA_COMPONENT_MAPPING_BEZIERSHELLMECHANICALMAPPING_H + + +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#include + + +namespace sofa +{ + +namespace component +{ + +namespace mapping +{ + +using namespace sofa::type; +using namespace sofa::core::topology; +using namespace core::topology; +using namespace sofa::core::behavior; + +template +class BezierShellMechanicalMapping : public core::Mapping +{ +public: + SOFA_CLASS(SOFA_TEMPLATE2(BezierShellMechanicalMapping,TIn,TOut), SOFA_TEMPLATE2(core::Mapping,TIn,TOut)); + typedef core::Mapping Inherit; + typedef TIn In; + typedef TOut Out; + + typedef typename In::VecCoord InVecCoord; + typedef typename In::VecDeriv InVecDeriv; + typedef typename In::Coord InCoord; + typedef typename In::Deriv InDeriv; + typedef typename In::MatrixDeriv InMatrixDeriv; + typedef typename In::Real InReal; + + typedef typename Out::VecCoord OutVecCoord; + typedef typename Out::VecDeriv OutVecDeriv; + typedef typename Out::Coord OutCoord; + typedef typename Out::Deriv OutDeriv; + typedef typename Out::MatrixDeriv OutMatrixDeriv; + typedef typename Out::Real OutReal; + + typedef InReal Real; + + typedef Vec<3, Real> Vec3; + typedef Mat<3, 3, Real> Mat33; + + typedef sofa::type::Quat Quat; + + typedef typename sofa::component::fem::BezierShellInterpolationM::ShapeFunctions ShapeFunctions; + typedef typename sofa::component::fem::BezierShellInterpolationM::VecShapeFunctions VecShapeFunctions; + + typedef sofa::Index Index; + typedef BaseMeshTopology::SeqEdges SeqEdges; + typedef BaseMeshTopology::Triangle Triangle; + typedef BaseMeshTopology::SeqTriangles SeqTriangles; + + enum { NIn = sofa::defaulttype::DataTypeInfo::Size }; + enum { NOut = sofa::defaulttype::DataTypeInfo::Size }; + typedef type::Mat MBloc; + typedef sofa::linearalgebra::CompressedRowSparseMatrix MatrixType; + + BezierShellMechanicalMapping(core::State* from, core::State* to) + : Inherit(from, to) + , inputTopo(NULL) + , outputTopo(NULL) + , bsInterpolation(initLink("bsInterpolation","Attached BezierShellInterpolationM object")) + , measureError(initData(&measureError, false, "measureError","Error with high resolution mesh")) + , measureStress(initData(&measureStress, false, "measureStress","Tell forcefield to measure stress values at mapped points")) + , targetTopology(initLink("targetTopology","Targeted high resolution topology")) + , matrixJ() + , updateJ(false) + { + } + + virtual ~BezierShellMechanicalMapping() + { + } + + void init() override; + void reinit() override; + //virtual void draw(const core::visual::VisualParams* vparams); + + + void apply(const core::MechanicalParams *mparams, Data& out, const Data& in) override; + void applyJ(const core::MechanicalParams *mparams, Data& out, const Data& in) override; + void applyJT(const core::MechanicalParams *mparams, Data& out, const Data& in) override; + void applyJT(const core::ConstraintParams *cparams, Data& out, const Data& in) override; + + +#if 0 + /// For checkJacobian and to hide some deprecation warnings + const sofa::linearalgebra::BaseMatrix* getJ() { return getJ(NULL); } + void applyJ(Data& out, const Data& in) + { applyJ(NULL, out, in); } + void applyJ( OutVecDeriv& out, const InVecDeriv& in) + { + Data dout; + Data din; + *din.beginEdit() = in; + din.endEdit(); + dout.beginEdit()->resize(out.size()); + dout.endEdit(); + applyJ(NULL, dout, din); + out = dout.getValue(); + } + void applyJT(InVecDeriv& out, const OutVecDeriv& in) + { + Data din; + Data dout; + *din.beginEdit() = in; + din.endEdit(); + dout.beginEdit()->resize(out.size()); + dout.endEdit(); + applyJT(NULL, dout, din); + out = dout.getValue(); + } + /// +#endif + + + void handleEvent(sofa::core::objectmodel::Event *event) override { + if (dynamic_cast(event)) + { + //std::cout << "begin\n"; + // We have to update the matrix at every step + updateJ = true; + } + } + +protected: + + BezierShellMechanicalMapping() + : Inherit() + , inputTopo(NULL) + , outputTopo(NULL) + , bsInterpolation(initLink("bsInterpolation","Attached BezierShellInterpolationM object")) + , measureError(initData(&measureError, false, "measureError","Error with high resolution mesh")) + , measureStress(initData(&measureStress, false, "measureStress","Tell forcefield to measure stress values at mapped points")) + , targetTopology(initLink("targetTopology","Targeted high resolution topology")) + , matrixJ() + , updateJ(false) + { + } + + typedef struct { + + // Nodes of the Bézier triangle + sofa::type::fixed_array bezierNodesV; + + // Contains the list of poinst connected to this triangle + sofa::type::vector attachedPoints; + + } TriangleInformation; + + gl::GLSLShader shader; + + BaseMeshTopology* inputTopo; + BaseMeshTopology* outputTopo; + + SingleLink, + sofa::component::fem::BezierShellInterpolationM, + BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> bsInterpolation; + + Data measureError; + Data measureStress; + SingleLink, + sofa::core::topology::BaseMeshTopology, + BaseLink::FLAG_STOREPATH|BaseLink::FLAG_STRONGLINK> targetTopology; + + topology::container::dynamic::TriangleSetTopologyContainer* topologyTarget; + + type::vector colourMapping; + type::vector coloursPerVertex; + type::vector vectorErrorCoarse; + type::vector vectorErrorTarget; + + type::vector triangleInfo; + type::vector projBaryCoords; // Barycentric coordinates + VecShapeFunctions projN; // Precomputed shape functions + type::vector projElements; + + std::unique_ptr matrixJ; + bool updateJ; + + // Pointer on the topological mapping to retrieve the list of edges + // XXX: The edges are no longer there!!! + //TriangleSubdivisionTopologicalMapping* triangleSubdivisionTopologicalMapping; + + void HSL2RGB(Vec3 &rgb, Real h, Real sl, Real l); + void MeasureError(); + Real DistanceHausdorff(BaseMeshTopology *topo1, BaseMeshTopology *topo2, type::vector &vectorError); + void ComputeNormals(type::vector &normals); + void FindTriangleInNormalDirection(const InVecCoord& highResVertices, const SeqTriangles highRestriangles, const type::vector &normals); + + // Contains the barycentric coordinates of the point within a triangle + sofa::type::vector barycentricCoordinates; +}; + +} // namespace mapping + +} // namespace component + +} // namespace sofa + +#endif // #ifndef SOFA_COMPONENT_MAPPING_BEZIERSHELLMECHANICALMAPPING_H diff --git a/src/Shell/shells2/mapping/BezierShellMechanicalMapping.inl b/src/Shell/shells2/mapping/BezierShellMechanicalMapping.inl new file mode 100644 index 0000000..b5d8af9 --- /dev/null +++ b/src/Shell/shells2/mapping/BezierShellMechanicalMapping.inl @@ -0,0 +1,895 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4 * +* (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS * +* * +* This library is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +******************************************************************************* +* SOFA :: Modules * +* * +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#ifndef SOFA_COMPONENT_MAPPING_BEZIERSHELLMECHANICALMAPPING_INL +#define SOFA_COMPONENT_MAPPING_BEZIERSHELLMECHANICALMAPPING_INL + +#include +#include +#include + +#include +#include + +// We have own code to check the getJ() because checkJacobian sucks (at this +// point in time). +//#define CHECK_J + +namespace sofa +{ + +namespace component +{ + +namespace mapping +{ + +using namespace sofa::helper; + + +template +void BezierShellMechanicalMapping::init() +{ +// std::cout << "BezierShellMechanicalMapping::init()" << std::endl; + + *this->f_listening.beginEdit() = true; + this->f_listening.endEdit(); + + if (this->fromModel == NULL) + { + msg_warning() << "Missing input Mechanical state!" ; + return; + } + + if (this->toModel == NULL) + { + msg_warning() << "Missing output Mechanical state!" ; + return; + } + + // Retrieves topology + inputTopo = this->fromModel->getContext()->getMeshTopology(); + outputTopo = this->toModel->getContext()->getMeshTopology(); + + if (!inputTopo || (inputTopo->getNbTriangles() <= 0)) + { + msg_warning() << "BezierShellMechanicalMapping requires an input triangular topology" ; + return; + } + + if (!outputTopo || (outputTopo->getNbTriangles() <= 0)) + { + msg_warning() << "BezierShellMechanicalMapping requires an output triangular topology" ; + return; + } + + const OutVecCoord &outVertices = this->toModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + barycentricCoordinates.clear(); + barycentricCoordinates.resize(outVertices.size()); + + // Retrieves 'in' vertices and triangles + const InVecCoord &inVerticesRigid = this->fromModel->read(sofa::core::vec_id::read_access::position)->getValue(); + + // Conversion to Vec3Types to be able to call same methods used by Hausdorff distance + OutVecCoord inVertices; + for (unsigned int i=0; igetEdges(); + const SeqTriangles &inTriangles = inputTopo->getTriangles(); + + // Iterates over 'in' triangles + triangleInfo.resize(inTriangles.size()); + projBaryCoords.clear(); + projN.clear(); + projElements.clear(); + for (unsigned int t=0; t proj(*dynamic_cast(inputTopo)); + + // Iterates over 'out' vertices + for (unsigned int i=0; icomputeShapeFunctions(projBaryCoords.back(), N); + projN.push_back(N); + projElements.push_back(triangleID); + } + + if (measureStress.getValue()) + { + forcefield::BezierShellForceField *ff; + this->getContext()->get(ff); + + if (ff) + { + ff->stressAtPoints(projBaryCoords, projElements); + } + else + { + msg_warning() << "Unable to find force field component! Ignoring 'measureStress' option." ; + } + } + +#if 0 + // Retrieves topological mapping to get list of edges (for contour rendering) + triangleSubdivisionTopologicalMapping = NULL; + // this->getContext()->get(triangleSubdivisionTopologicalMapping, nameHighTopology.getValue(), sofa::core::objectmodel::BaseContext::SearchRoot); + this->getContext()->get(triangleSubdivisionTopologicalMapping); + if (!triangleSubdivisionTopologicalMapping) + { + // This is not fatal + msg_warning() << "triangleSubdivisionTopologicalMapping was not found" ; + } +#endif + + // Call of apply() and applyJ() + this->Inherit::init(); + + // Set each colour of each vertex to default + for (unsigned int i=0; imaximum) + { + maximum = fabs(vectorErrorCoarse[i]); + } + } + Real correctedError; + for (unsigned int i=0; i maximum) + correctedError = maximum; + coloursPerVertex[i] = colourMapping[ (int)((correctedError/maximum)*239) ]; + } + } +} + + +template +void BezierShellMechanicalMapping::reinit() +{ + msg_info() << "reinit()" ; + init(); +} + + +// Given H,S,L in range of 0-1 +// Returns a RGB colour in range of 0-255 +// http://www.geekymonkey.com/Programming/CSharp/RGB2HSL_HSL2RGB.htm +template +void BezierShellMechanicalMapping::HSL2RGB(Vec3 &rgb, Real h, Real sl, Real l) +{ + Real v; + Real r,g,b; + + r = l; // default to gray + g = l; + b = l; + v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl); + if (v > 0) + { + Real m; + Real sv; + int sextant; + Real fract, vsf, mid1, mid2; + + m = l + l - v; + sv = (v - m ) / v; + h *= 6.0; + sextant = (int)h; + fract = h - sextant; + vsf = v * sv * fract; + mid1 = m + vsf; + mid2 = v - vsf; + switch (sextant) + { + case 0: + r = v; + g = mid1; + b = m; + break; + case 1: + r = mid2; + g = v; + b = m; + break; + case 2: + r = m; + g = v; + b = mid1; + break; + case 3: + r = m; + g = mid2; + b = v; + break; + case 4: + r = mid1; + g = m; + b = v; + break; + case 5: + r = v; + g = m; + b = mid2; + break; + } + } + + rgb[0] = r; + rgb[1] = g; + rgb[2] = b; +} + + +template +void BezierShellMechanicalMapping::MeasureError() +{ + Real distance1; + msg_info() << "Computing Hausdorff distance high res->coarse" ; + distance1 = DistanceHausdorff(targetTopology.get(), outputTopo, vectorErrorTarget); + msg_info() << "Hausdorff distance between high res mesh and coarse mesh = " << distance1 ; + + Real average = 0; + for (unsigned int i=0; ihigh res" ; + distance2 = DistanceHausdorff(outputTopo, targetTopology.get(), vectorErrorCoarse); + msg_info() << "Hausdorff distance between coarse mesh and high res mesh = " << distance2 ; + + average = 0; + for (unsigned int i=0; iapplyOnBTriangle(projN, projElements, out); + + //stop = timer.getTime(); + //std::cout << "time apply = " << stop-start << std::endl; +} + + +// Updates velocities of the visual mesh from mechanical vertices +template +void BezierShellMechanicalMapping::applyJ(const core::MechanicalParams* /*mparams*/, Data& dOut, const Data& dIn) +{ + helper::WriteAccessor< Data > out = dOut; + SOFA_UNUSED(dIn); + + //std::cout << "---------------- ApplyJ ----------------------------" << std::endl; + + //sofa::helper::system::thread::ctime_t start, stop; + //sofa::helper::system::thread::CTime timer; + + //start = timer.getTime(); + + bsInterpolation->applyJOnBTriangle(projN, projElements, dIn.getValue(), out); + + // The following code compares the result with results obtained using + // getJ() because checkJacobian sucks (at this point in time). +#ifdef CHECK_J + const sofa::linearalgebra::BaseMatrix* J = getJ(NULL); + if (J != NULL) { + Real* in_alloc = NULL; + Real* out_alloc = NULL; + + // Prepare in vector + in_alloc = new Real[in.size()*NIn]; + for (unsigned int i=0;iopMulV(out_alloc, in_alloc); + + // Compare results + Real amax = 0; Index maxi=0; + //std::cout << "Delta with getJ():"; + Real dif; + for (unsigned int i=0;i amax) { amax = rabs(dif); maxi = i; } + } + //std::cout << "\n"; + if (amax > 1e-9) + std::cout << "check J: amax=" << amax << " i=" << maxi << " phi=" << + barycentricCoordinates[maxi][0] << "/" << + barycentricCoordinates[maxi][1] << "/" << + barycentricCoordinates[maxi][2] << "\n"; + + // Cleanup + delete[] in_alloc; + delete[] out_alloc; + } +#endif + + // Dump input and output vectors {{{ + //{ + // std::cout << "In[" << in.size() << "] : "; + // for (unsigned int i=0; i > rout = dOut; + // std::cout << "Out[" << rout.size() << "]: "; + // for (unsigned int i=0; i + // dOutVertices(*this->toModel->getX()), + // dOutVertices2(*this->toModel->getX()); + + //Data + // dInVertices(*this->fromModel->getX()), + // dInVertices2(*this->fromModel->getX()); + + //helper::WriteAccessor< Data > iv = dInVertices; + //helper::WriteAccessor< Data > iv2 = dInVertices2; + + //for (unsigned int i=0; i > ov = dOutVertices; + //helper::ReadAccessor< Data > ov2 = dOutVertices2; + + //Real amax = 0; Index maxi=0; + ////std::cout << "Dif[" << ov.size() << "]: "; + //for (unsigned int i=0; i amax) { amax = rabs(dif[k]); maxi = i; } } + // //if (i != 0) std::cout << ", "; + // //std::cout << dif; + // //std::cout << out[i]; + //} + ////std::cout << std::endl; + //if (amax > 1e-9) + //std::cout << "amax=" << amax << " i=" << maxi << " phi=" << + // barycentricCoordinates[maxi][0] << "/" << + // barycentricCoordinates[maxi][1] << "/" << + // barycentricCoordinates[maxi][2] << "\n"; + // }}} + + //stop = timer.getTime(); + //std::cout << "time applyJ = " << stop-start << std::endl; +} + +#if 0 // TODO {{{ +template +const BaseMatrix* BezierShellMechanicalMapping::getJ(const core::MechanicalParams * /*mparams*/) +{ + //std::cout << "---------------- getJ ----------------------------" << std::endl; + + if (matrixJ.get() == NULL || updateJ) + { + if (!inputTopo || !outputTopo) + { + msg_warning() << "getJ() was called before init()" ; + return NULL; + } + if (inputTopo->getNbTriangles() <= 0) + { + msg_warning() << "getJ() requires an input triangular topology" ; + return NULL; + } + + const OutVecCoord& out = *this->toModel->getX(); + const InVecCoord& in = *this->fromModel->getX(); + + // Initialize the matrix + if (matrixJ.get() == 0 || + matrixJ->rowBSize() != out.size() || + matrixJ->colBSize() != in.size()) + { + matrixJ.reset(new MatrixType( + out.size() * NOut, + in.size() * NIn)); + } + else + { + matrixJ->clear(); + } + + Mat33 I(Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)); + + const SeqTriangles& inTriangles = inputTopo->getTriangles(); + const InVecCoord& inVertices = *this->fromModel->getX(); + + // Go through all input triangles + for (unsigned int t=0; twbloc(pt, triangle[k], true); + Mat33 trans, ang; + block.getsub(0, 0, trans); // Translational DOFS + block.getsub(0, 3, ang); // Angular DOFS + + // Corner node + trans += I * bc[k]*bc[k]*bc[k]; + + //// 3 / 5 / 7 + int l = (k + 1) % 3; + trans += I * 3*bc[k]*bc[k]*bc[l]; + ang += Ap1[k] * 3*bc[k]*bc[k]*bc[l]; + + // 4 / 6 / 8 + l = (k + 2) % 3; + trans += I * 3*bc[k]*bc[k]*bc[l]; + ang += Ap2[k] * 3*bc[k]*bc[k]*bc[l]; + + // Central node + trans += I * 2*bc[0]*bc[1]*bc[2]; // <-- 6/3 = 2 + ang += (Ap1[k] + Ap2[k]) * 2*bc[0]*bc[1]*bc[2]; // <-- 6/3 = 2 + + block.setsub(0, 0, trans); + block.setsub(0, 3, ang); + } + } + } + + } // if (matrixJ.get() == NULL || updateJ) + + return matrixJ.get(); +} +#endif // }}} + +// Updates positions of the mechanical vertices from visual f(n-1) = JT * fn +template +void BezierShellMechanicalMapping::applyJT(const core::MechanicalParams * /*mparams*/, Data& dOut, const Data& dIn) +{ + helper::WriteAccessor< Data > out = dOut; + helper::ReadAccessor< Data > in = dIn; + + //std::cout << "---------------- ApplyJT ----------------------------" << std::endl; + + //sofa::helper::system::thread::ctime_t start, stop; + //sofa::helper::system::thread::CTime timer; + + //start = timer.getTime(); + + if (!inputTopo || !outputTopo) + { + msg_warning() << "applyJT() was called before init()" ; + return; + } + if (inputTopo->getNbTriangles() <= 0) + { + msg_warning() << "applyJT() requires an input triangular topology" ; + return; + } + +#ifdef CHECK_J + const sofa::linearalgebra::BaseMatrix* J = getJ(NULL); + Real* in_alloc = NULL; + Real* out_alloc = NULL; + if (J != NULL) { + // Prepare in vector + in_alloc = new Real[in.size()*NOut]; + for (unsigned int i=0;iapplyJTOnBTriangle(projN, projElements, + in.ref(), out); + + // The following code compares the result with results obtained using + // getJ() because checkJacobian sucks (at this point in time). +#ifdef CHECK_J + if (J != NULL) { + J->opPMulTV(out_alloc, in_alloc); + + // Compare results + Real amax = 0; Index maxi=0; + //std::cout << "Delta with getJT():"; + Real dif; + for (unsigned int i=0;i amax) { amax = rabs(dif); maxi = i; } + } + //std::cout << "\n"; + if (amax > 1e-9) + std::cout << "check JT: amax=" << amax << " i=" << maxi << " phi=" << + barycentricCoordinates[maxi][0] << "/" << + barycentricCoordinates[maxi][1] << "/" << + barycentricCoordinates[maxi][2] << + //" val= " << out_alloc[maxi*NIn+0] << " " << + //out_alloc[maxi*NIn+1] << " " << + //out_alloc[maxi*NIn+2] << " " << + //out_alloc[maxi*NIn+3] << " " << + //out_alloc[maxi*NIn+4] << " " << + //out_alloc[maxi*NIn+5] << " / " << + //out[maxi] << + "\n"; + + // Cleanup + delete[] in_alloc; + delete[] out_alloc; + } +#endif + + //stop = timer.getTime(); + //std::cout << "time applyJT = " << stop-start << std::endl; +} + +template +void BezierShellMechanicalMapping::applyJT(const sofa::core::ConstraintParams* cparams, Data& dOut, const Data& dIn) +{ + //std::cout << "---------------- ApplyJT (constraints) --------------" << std::endl; + + helper::WriteAccessor< Data > out = dOut; + SOFA_UNUSED(dIn); + + if (!inputTopo || !outputTopo) + { + msg_warning() << "applyJT() was called before init()" ; + return; + } + if (inputTopo->getNbTriangles() <= 0) + { + msg_warning() << "applyJT() requires an input triangular topology" ; + return; + } + + bsInterpolation->applyJTOnBTriangle(projN, projElements, dIn.getValue(), *dOut.beginEdit()); + dOut.endEdit(); +} + + +#if 0 // TODO {{{ +template +void BezierShellMechanicalMapping::draw(const core::visual::VisualParams* vparams) +{ + if (!inputTopo || !outputTopo) + { + msg_warning() << "draw() was called before init()" ; + return; + } + if (inputTopo->getNbTriangles() <= 0) + { + msg_warning() << "draw() requires an input triangular topology" ; + return; + } + if (outputTopo->getNbTriangles() <= 0) + { + msg_warning() << "draw() requires an output triangular topology" ; + return; + } + + const OutVecCoord &outVertices = *this->toModel->getX(); + + if(vparams->displayFlags().getShowVisualModels()) + { + glDisable(GL_LIGHTING); + + const SeqTriangles &outTriangles = outputTopo->getTriangles(); + unsigned int index; + + glEnable(GL_DEPTH_TEST); + glPolygonOffset(1.0, 1.0); + + if(vparams->displayFlags().getShowWireFrame()) + { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glEnable(GL_POLYGON_OFFSET_LINE); + glColor4f(0.0, 0.0, 1.0, 1.0); + glBegin(GL_TRIANGLES); + for (unsigned int i=0; igetSubEdges(); + //const SeqEdges &outEdges = outputTopo->getEdges(); + glColor4f(1.0, 1.0, 1.0, 1.0); + glLineWidth(0.5); + glBegin(GL_LINES); + for (unsigned int i=0; idisplayFlags().getShowMechanicalMappings()) + { + // Render nodes of the Bézier triangles + glPointSize(8); + glDisable(GL_LIGHTING); + glBegin(GL_POINTS); + //for (unsigned int i=0; igetTriangles().size(); i++) + unsigned int i=3; + { + sofa::type::fixed_array &bn = triangleInfo[i].bezierNodes; + for (int j=0; j<10; j++) + { + //glColor4f(0.0, 0.5, 0.3, 1.0); + glColor4f(0.5, 1.0, 0.5, 1.0); + glVertex3f(bn[j][0], bn[j][1], bn[j][2]); + } + } + glEnd(); + glPointSize(1); + + } + // TODO: visualise the mesh +} +#endif // }}} + + +} // namespace mapping + +} // namespace component + +} // namespace sofa + +#endif // #ifndef SOFA_COMPONENT_MAPPING_BEZIERSHELLMECHANICALMAPPING_INL