diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bd9716..02160ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,7 +78,8 @@ set(SOURCE_FILES # Add component for carving using refinement mesh find_package(MeshRefinement QUIET) if (MeshRefinement_FOUND) - message("MeshRefinement plugin found - Adding it to ${PROJECT_NAME}") + message("## ${PROJECT_NAME}: MeshRefinement plugin found") + list(APPEND HEADER_FILES ${INFINYTOOLKIT_SRC_DIR}/CarvingTools/RefineCarvingPerformer.h ${INFINYTOOLKIT_SRC_DIR}/CarvingTools/CuttingPerformer.h @@ -92,6 +93,34 @@ if (MeshRefinement_FOUND) add_definitions(-DHAS_MESHREFINEMENT_PLUGIN) endif() + +find_package(Sofa.GUI.Qt QUIET) +if (Sofa.GUI.Qt_FOUND) + message("## ${PROJECT_NAME}: Sofa.GUI.Qt plugin found") + + list(APPEND HEADER_FILES + ${INFINYTOOLKIT_SRC_DIR}/MouseCarvingOperation/CarvingOperation.h + ) + list(APPEND SOURCE_FILES + ${INFINYTOOLKIT_SRC_DIR}/MouseCarvingOperation/CarvingOperation.cpp + ${INFINYTOOLKIT_SRC_DIR}/MouseCarvingOperation/QCarvingOperation.cpp + ) + + set(MOC_HEADER_FILES + ${INFINYTOOLKIT_SRC_DIR}/MouseCarvingOperation/QCarvingOperation.h + ) + + if (Qt5Core_FOUND) + qt5_wrap_cpp(MOC_FILES ${MOC_HEADER_FILES}) + qt5_wrap_ui(FORM_FILES ${UI_FILES}) + elseif (Qt6Core_FOUND) + qt6_wrap_cpp(MOC_FILES ${MOC_HEADER_FILES}) + qt6_wrap_ui(FORM_FILES ${UI_FILES}) + endif() + + add_definitions(-DUSE_GUI) +endif() + set(README_FILES README.md) # Create the plugin library. @@ -101,7 +130,7 @@ add_library(${PROJECT_NAME} SHARED ${HEADER_FILES} ${SOURCE_FILES} ${README_FILE target_compile_definitions(${PROJECT_NAME} PRIVATE "-DSOFA_BUILD_INFINYTOOLKIT") # Link the plugin library to its dependencies (other libraries). -target_link_libraries(${PROJECT_NAME} +target_link_libraries(${PROJECT_NAME} PUBLIC Sofa.Component.StateContainer Sofa.Component.MechanicalLoad Sofa.Component.Topology.Container.Dynamic @@ -118,6 +147,10 @@ if(MeshRefinement_FOUND) target_link_libraries(${PROJECT_NAME} MeshRefinement) endif() +if(Sofa.GUI.Qt_FOUND) + target_link_libraries(${PROJECT_NAME} PUBLIC Sofa.GUI.Qt) +endif() + target_include_directories(${PROJECT_NAME} PUBLIC "$") target_include_directories(${PROJECT_NAME} PUBLIC "$") diff --git a/examples/Cube_AdvCarvingWithRefinement.scn b/examples/Cube_AdvCarvingWithRefinement.scn index 4dc5f97..eead92d 100644 --- a/examples/Cube_AdvCarvingWithRefinement.scn +++ b/examples/Cube_AdvCarvingWithRefinement.scn @@ -33,12 +33,12 @@ - + - diff --git a/src/InfinyToolkit/CarvingTools/AdvancedCarvingManager.cpp b/src/InfinyToolkit/CarvingTools/AdvancedCarvingManager.cpp index b202c0a..fdf1a8d 100644 --- a/src/InfinyToolkit/CarvingTools/AdvancedCarvingManager.cpp +++ b/src/InfinyToolkit/CarvingTools/AdvancedCarvingManager.cpp @@ -70,7 +70,7 @@ AdvancedCarvingManager::AdvancedCarvingManager() , d_cuttingMode(initData(&d_cuttingMode, false, "cuttingMode", "Activate the option tetrahedral cutting.")) , d_carvingDistance( initData(&d_carvingDistance, 0.0, "carvingDistance", "Collision distance at which cavring will start. Equal to contactDistance by default.")) , d_refineDistance(initData(&d_refineDistance, 0.0, "refineDistance", "Collision distance at which cavring will start. Equal to contactDistance by default.")) - , d_refineCriteria( initData(&d_refineCriteria, 0.5, "refineCriteria", "Collision distance at which cavring will start. Equal to contactDistance by default.")) + , d_refineCriteria( initData(&d_refineCriteria, 0.5, "refineCriteria", "Factor to determine the minimum thresold to not devide a tetrahedron (minVolume = criteria*refVolume).")) , d_carvingSpeed(initData(&d_carvingSpeed, 0.001, "carvingSpeed", "Collision distance at which cavring will start. Equal to contactDistance by default.")) , d_refineThreshold(initData(&d_refineThreshold, 1.0, "refineThreshold", "Collision distance at which cavring will start. Equal to contactDistance by default.")) , m_testID(initData(&m_testID, sofa::type::vector(), "testID", "Scale of the terahedra (between 0 and 1; if <1.0, it produces gaps between the tetrahedra)")) diff --git a/src/InfinyToolkit/MouseCarvingOperation/CarvingOperation.cpp b/src/InfinyToolkit/MouseCarvingOperation/CarvingOperation.cpp new file mode 100644 index 0000000..1ca2d2f --- /dev/null +++ b/src/InfinyToolkit/MouseCarvingOperation/CarvingOperation.cpp @@ -0,0 +1,203 @@ +/***************************************************************************** + * Copyright (C) - InfinyTech3D - All Rights Reserved * + * * + * Unauthorized copying of this file, via any medium is strictly prohibited * + * Proprietary and confidential. * + * * + * Written by Erik Pernod , June 2021 * + ****************************************************************************/ +#include +#include +#include +#include +#include + +namespace sofa::gui::component::performer +{ + +CuttingPerformer::CuttingPerformer(BaseMouseInteractor* interactor) + : InteractionPerformer(interactor) + , m_tetraCuttingMgr(nullptr) + , m_topoCon(nullptr) + , m_topoGeo(nullptr) + , isInit(false) + , clickCount(0) + , m_depth(1.0) +{ + m_tetraCuttingMgr = new sofa::meshrefinement::TetrahedronCuttingManager(); +} + + +void CuttingPerformer::start() +{ + ////pickHandle->getInteraction()->mouseInteractor; + //std::cout << "CuttingPerformer::start()" << std::endl; + + BodyPicked picked = this->m_interactor->getBodyPicked(); + if (!picked.body) return; + + if (!isInit) + { + sofa::core::objectmodel::BaseContext* base = picked.body->getContext(); + + m_topoCon = base->get(); + if (m_topoCon == nullptr) // possible Tetra2Triangle topology + { + base->get(m_topoCon, core::objectmodel::BaseContext::SearchUp); + if (m_topoCon == nullptr) + { + msg_warning("CuttingPerformer") << "No TetrahedronSetTopologyContainer found in target object."; + isInit = false; + return; + } + else + { + m_topoGeo = m_topoCon->getContext()->get>(); + } + } + else { + m_topoGeo = m_topoCon->getContext()->get>(); + } + + m_tetraCuttingMgr->init(m_topoCon->getContext()); + isInit = true; + } + + + if (clickCount == 0) + { + m_pos0 = picked.point; + clickCount++; + } + else if (clickCount == 1) + { + if (!m_topoGeo) + return; + + m_pos1 = picked.point; + + fixed_array m_planPositions; + m_planPositions[0] = m_pos0; + m_planPositions[1] = m_pos1; + + // sofa::component::collision::RayCollisionModel* rayModel = this->interactor->getMouseRayModel(); + sofa::component::collision::geometry::Ray ray = this->m_interactor->getMouseRayModel()->getRay(0); + Vec3 normA = -ray.direction(); + normA.normalize(); +// = m_topoGeo->computeTriangleNormal(picked.indexCollisionElement); + //normA.normalize(); + + m_planPositions[2] = m_planPositions[1] - normA * m_depth * 0.1; + m_planPositions[3] = m_planPositions[0] - normA * m_depth * 0.1; + + Vec3 m_planNormal = (m_planPositions[1] - m_planPositions[0]).cross(normA); + m_tetraCuttingMgr->createCutPlanPath(m_planPositions, m_planNormal, 1.0); + + clickCount++; + } + else if (clickCount == 2) + { + m_tetraCuttingMgr->processCut(false); + } +} + + +void CuttingPerformer::execute() +{ + BodyPicked picked = this->m_interactor->getBodyPicked(); + if (!picked.body) return; + + if (clickCount == 1) + { + m_pos1 = picked.point; + } + +} + + +void CuttingPerformer::draw(const core::visual::VisualParams* vparams) +{ + if (!isInit) + return; + + if (clickCount > 0) { + vparams->drawTool()->drawSphere(m_pos0, float(1.0), RGBAColor::red()); + } + if (clickCount > 1) { + vparams->drawTool()->drawSphere(m_pos1, float(1.0), RGBAColor::green()); + m_tetraCuttingMgr->drawCutPlan(vparams); + m_tetraCuttingMgr->drawDebugCut(vparams); + } + + + + + +} + + +sofa::helper::Creator CuttingPerformerClass("CuttingPerformer", true); + +} // namespace sofa::gui::component::performer + + + +namespace sofa::gui::common +{ + +CuttingOperation::CuttingOperation() + : clickCount(0) +{ + +} + +CuttingOperation::~CuttingOperation() +{ + +} + +void CuttingOperation::start() +{ + if (performer == nullptr) + { + performer = component::performer::InteractionPerformer::InteractionPerformerFactory::getInstance()->createObject("CuttingPerformer", pickHandle->getInteraction()->mouseInteractor.get()); + pickHandle->getInteraction()->mouseInteractor->addInteractionPerformer(performer); + + component::performer::CuttingPerformer* performerCut = dynamic_cast(performer); + if (performerCut) + performerCut->setIncisionDepth(this->getDepth()); + } + + if (performer) { + performer->start(); + } + + //if (clickCount == 0) + //{ + // + //} + +} + +void CuttingOperation::execution() +{ + //std::cout << "CuttingOperation::execution()" << std::endl; +} + +void CuttingOperation::end() +{ + //std::cout << "CuttingOperation::end()" << std::endl; +} + +void CuttingOperation::endOperation() +{ + if (performer) + { + pickHandle->getInteraction()->mouseInteractor->removeInteractionPerformer(performer); + delete performer; performer = nullptr; + clickCount = 0; + } +} + + +} // namespace sofa::gui::common \ No newline at end of file diff --git a/src/InfinyToolkit/MouseCarvingOperation/CarvingOperation.h b/src/InfinyToolkit/MouseCarvingOperation/CarvingOperation.h new file mode 100644 index 0000000..019b721 --- /dev/null +++ b/src/InfinyToolkit/MouseCarvingOperation/CarvingOperation.h @@ -0,0 +1,74 @@ +/***************************************************************************** + * Copyright (C) - InfinyTech3D - All Rights Reserved * + * * + * Unauthorized copying of this file, via any medium is strictly prohibited * + * Proprietary and confidential. * + * * + * Written by Erik Pernod , June 2021 * + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include + +using sofa::meshrefinement::TetrahedronCuttingManager; + +namespace sofa::gui::component::performer +{ + +class SOFA_MESHREFINEMENT_API CuttingPerformer : public InteractionPerformer +{ +public: + CuttingPerformer(BaseMouseInteractor* interactor); + + void start(); + void execute(); + void draw(const core::visual::VisualParams* vparams); + + void setIncisionDepth(double value) { m_depth = value; } + +protected: + sofa::meshrefinement::TetrahedronCuttingManager* m_tetraCuttingMgr; + TetrahedronSetTopologyContainer* m_topoCon; + TetrahedronSetGeometryAlgorithms::SPtr m_topoGeo; + + Vec3 m_pos0; + Vec3 m_pos1; + + double m_depth; + + bool isInit; + int clickCount; +}; + +} // namespace sofa::gui::component::performer + + +namespace sofa::gui::common +{ + +class SOFA_MESHREFINEMENT_API CuttingOperation : public Operation +{ +public: + CuttingOperation(); + ~CuttingOperation(); + + void start() override; + void execution() override; + void end() override; + void endOperation() override; + + virtual double getDepth() const { return incisionDepth; } + + static std::string getDescription() { return "3D mesh incision using the Mouse"; } + +protected: + double incisionDepth; + int clickCount; +}; + + + +} // namespace sofa::gui::common diff --git a/src/InfinyToolkit/MouseCarvingOperation/QCarvingOperation.cpp b/src/InfinyToolkit/MouseCarvingOperation/QCarvingOperation.cpp new file mode 100644 index 0000000..b023ac2 --- /dev/null +++ b/src/InfinyToolkit/MouseCarvingOperation/QCarvingOperation.cpp @@ -0,0 +1,47 @@ +/***************************************************************************** + * Copyright (C) - InfinyTech3D - All Rights Reserved * + * * + * Unauthorized copying of this file, via any medium is strictly prohibited * + * Proprietary and confidential. * + * * + * Written by Erik Pernod , June 2021 * + ****************************************************************************/ +#include +#include +#include +#include + +namespace sofa::gui::qt +{ + +QCuttingOperation::QCuttingOperation() +{ + QVBoxLayout* layout = new QVBoxLayout(this); + + QHBoxLayout* HLayout1 = new QHBoxLayout(); + QLabel* label = new QLabel(QString("Depth (mm)"), this); + depthSlider = new QSlider(Qt::Horizontal, this); + depthValue = new QSpinBox(this); + depthValue->setMinimum(0); + depthValue->setMaximum(100); + depthValue->setSingleStep(1); + depthValue->setEnabled(true); + depthValue->setValue(0); + + HLayout1->addWidget(label); + HLayout1->addWidget(depthSlider); + HLayout1->addWidget(depthValue); + layout->addLayout(HLayout1); + + connect(depthSlider, SIGNAL(valueChanged(int)), depthValue, SLOT(setValue(int))); + connect(depthValue, SIGNAL(valueChanged(int)), depthSlider, SLOT(setValue(int))); +} + + +double QCuttingOperation::getDepth() const +{ + return depthValue->value(); +} + + +} // namespace sofa::gui::qt \ No newline at end of file diff --git a/src/InfinyToolkit/MouseCarvingOperation/QCarvingOperation.h b/src/InfinyToolkit/MouseCarvingOperation/QCarvingOperation.h new file mode 100644 index 0000000..0e0acc7 --- /dev/null +++ b/src/InfinyToolkit/MouseCarvingOperation/QCarvingOperation.h @@ -0,0 +1,34 @@ +/***************************************************************************** + * Copyright (C) - InfinyTech3D - All Rights Reserved * + * * + * Unauthorized copying of this file, via any medium is strictly prohibited * + * Proprietary and confidential. * + * * + * Written by Erik Pernod , June 2021 * + ****************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace sofa::gui::qt +{ + +class QCuttingOperation : public QWidget, public sofa::gui::common::CuttingOperation +{ + Q_OBJECT +public: + QCuttingOperation(); + + double getDepth() const override; + +protected: + QSlider* depthSlider; + QSpinBox* depthValue; +}; + +} // namespace sofa::gui::qt \ No newline at end of file