From 95501a4e8bf53d26b1cacd12a7609c0f43173270 Mon Sep 17 00:00:00 2001 From: Hugues Delorme Date: Fri, 20 Sep 2024 16:31:04 +0200 Subject: [PATCH] Gui: adapt "fit all" operation to selection and objects visibility Relates to GitHub #292 --- src/app/app_module.cpp | 1 + src/app/widget_gui_document.cpp | 5 ++- src/graphics/graphics_view_ptr.h | 2 +- src/gui/gui_document.cpp | 54 ++++++++++++++++++++++++++++++-- src/gui/gui_document.h | 26 +++++++++++---- 5 files changed, 78 insertions(+), 10 deletions(-) diff --git a/src/app/app_module.cpp b/src/app/app_module.cpp index 5caa3948..0d448a72 100644 --- a/src/app/app_module.cpp +++ b/src/app/app_module.cpp @@ -13,6 +13,7 @@ #include "../base/io_writer.h" #include "../base/io_system.h" #include "../base/settings.h" +#include "../base/tkernel_utils.h" #include "../gui/gui_application.h" #include "../gui/gui_document.h" #include "../qtcommon/filepath_conv.h" diff --git a/src/app/widget_gui_document.cpp b/src/app/widget_gui_document.cpp index 21820e96..c1a59429 100644 --- a/src/app/widget_gui_document.cpp +++ b/src/app/widget_gui_document.cpp @@ -122,7 +122,10 @@ WidgetGuiDocument::WidgetGuiDocument(GuiDocument* guiDoc, QWidget* parent) m_qtOccView->redraw(); }); QObject::connect(m_btnFitAll, &ButtonFlat::clicked, this, [=]{ - m_guiDoc->runViewCameraAnimation(&GraphicsUtils::V3dView_fitAll); + m_guiDoc->runViewCameraAnimation([=](OccHandle view) { + auto bndBoxFlags = GuiDocument::OnlySelectedGraphics | GuiDocument::OnlyVisibleGraphics; + view->FitAll(this->guiDocument()->graphicsBoundingBox(bndBoxFlags)); + }); }); QObject::connect( m_btnGrid, &ButtonFlat::checked, diff --git a/src/graphics/graphics_view_ptr.h b/src/graphics/graphics_view_ptr.h index 72b8a9e0..65736301 100644 --- a/src/graphics/graphics_view_ptr.h +++ b/src/graphics/graphics_view_ptr.h @@ -12,7 +12,7 @@ namespace Mayo { -// Helper class providing a V3d view created associated with the owner GraphicsScene object +// Helper class providing a V3d view associated with the owner GraphicsScene object // The redraw() member function actually calls GraphicsScene::redraw() which under the hood sends // a "redraw requested" signal class GraphicsViewPtr { diff --git a/src/gui/gui_document.cpp b/src/gui/gui_document.cpp index 39fe3f48..ba72d2bf 100644 --- a/src/gui/gui_document.cpp +++ b/src/gui/gui_document.cpp @@ -105,11 +105,61 @@ GuiDocument::GuiDocument(const DocumentPtr& doc, GuiApplication* guiApp) for (int i = 0; i < doc->entityCount(); ++i) this->mapEntity(doc->entityTreeNodeId(i)); + doc->signalEntityAdded.connectSlot(&GuiDocument::onDocumentEntityAdded, this); doc->signalEntityAboutToBeDestroyed.connectSlot(&GuiDocument::onDocumentEntityAboutToBeDestroyed, this); m_gfxScene.signalSelectionChanged.connectSlot(&GuiDocument::onGraphicsSelectionChanged, this); } +Bnd_Box GuiDocument::graphicsBoundingBox(GraphicsBoundingBoxFlags flags) const +{ + if (flags == GraphicsBoundingBoxFlag::AllGraphics) + return m_gfxBoundingBox; + + Bnd_Box bndBox; + + auto fnIsVisibleTreeNode = [=](TreeNodeId nodeId) { + return (flags & OnlyVisibleGraphics) == 0 || this->nodeVisibleState(nodeId) == CheckState::On; + }; + + // Retrieve the application items being selected and visible + auto appSelectionModel = this->guiApplication()->selectionModel(); + std::vector appItems; + if (flags & OnlySelectedGraphics) { + for (const ApplicationItem& item : appSelectionModel->selectedItems()) { + if (item.document() == this->document() + && (!item.isDocumentTreeNode() || fnIsVisibleTreeNode(item.documentTreeNode().id()))) + { + appItems.push_back(item); + } + } + } + + // If no application items selected(and visible), then take the whole document + if (appItems.empty()) + appItems = { ApplicationItem{this->document()} }; + + // Helper function to extend main bounding box with each visible graphics object inside tree node + auto fnAddTreeNodeBndBox = [&](TreeNodeId nodeId) { + if (fnIsVisibleTreeNode(nodeId)) { + this->foreachGraphicsObject(nodeId, [&](GraphicsObjectPtr gfxObject) { + BndUtils::add(&bndBox, GraphicsUtils::AisObject_boundingBox(gfxObject)); + }); + } + }; + + // Iterate over application items to compute main bounding box + for (const ApplicationItem& item : appItems) { + const Tree& modelTree = item.document()->modelTree(); + if (item.isDocument()) + traverseTree(modelTree, fnAddTreeNodeBndBox); + else + traverseTree(item.documentTreeNode().id(), modelTree, fnAddTreeNodeBndBox); + } + + return bndBox; +} + void GuiDocument::setDevicePixelRatio(double ratio) { if (MathUtils::fuzzyEqual(m_devicePixelRatio, ratio)) @@ -374,7 +424,7 @@ void GuiDocument::setViewCameraOrientation(V3d_TypeOfOrientation projection) { this->runViewCameraAnimation([=](OccHandle view) { view->SetProj(projection); - GraphicsUtils::V3dView_fitAll(view); + view->FitAll(this->graphicsBoundingBox(OnlySelectedGraphics | OnlyVisibleGraphics)); }); } @@ -516,6 +566,7 @@ void GuiDocument::onDocumentEntityAdded(TreeNodeId entityTreeNodeId) { this->mapEntity(entityTreeNodeId); BndUtils::add(&m_gfxBoundingBox, m_vecGraphicsEntity.back().bndBox); + m_v3dView->FitAll(this->graphicsBoundingBox(OnlySelectedGraphics | OnlyVisibleGraphics)); this->signalGraphicsBoundingBoxChanged.send(m_gfxBoundingBox); } @@ -642,7 +693,6 @@ void GuiDocument::mapEntity(TreeNodeId entityTreeNodeId) m_mapTreeNodeCheckState.insert({ id, CheckState::On }); }); - GraphicsUtils::V3dView_fitAll(m_v3dView); m_vecGraphicsEntity.push_back(std::move(gfxEntity)); } diff --git a/src/gui/gui_document.h b/src/gui/gui_document.h index 9fa8e8c5..db9f9e92 100644 --- a/src/gui/gui_document.h +++ b/src/gui/gui_document.h @@ -9,7 +9,6 @@ #include "../base/document.h" #include "../base/global.h" #include "../base/signal.h" -#include "../base/tkernel_utils.h" #include "../graphics/graphics_object_driver.h" #include "../graphics/graphics_scene.h" #include "../graphics/graphics_view_ptr.h" @@ -19,7 +18,6 @@ #include #include #include -#include #include #include @@ -29,24 +27,40 @@ class ApplicationItem; class GuiApplication; class V3dViewCameraAnimation; -// Provides the link between Base::Document and graphical representations +// Provides the link between Base::Document and graphical representations(called "graphics objects") class GuiDocument { public: + // Applicable flags for function graphicsBoundingBox() + enum GraphicsBoundingBoxFlag { + AllGraphics = 0xFF, + OnlyVisibleGraphics = 0x01, + OnlySelectedGraphics = 0x02 + }; + using GraphicsBoundingBoxFlags = unsigned; + + // Constructor & destructor GuiDocument(const DocumentPtr& doc, GuiApplication* guiApp); ~GuiDocument(); - // Not copyable + // GuiDocument objects are not copyable GuiDocument(const GuiDocument&) = delete; GuiDocument& operator=(const GuiDocument&) = delete; + // Gets the base document linked to const DocumentPtr& document() const { return m_document; } + // Gets the owning GuiApplication object GuiApplication* guiApplication() const { return m_guiApp; } + // Gets the main 3D graphics view const OccHandle& v3dView() const { return m_v3dView; } - GraphicsScene* graphicsScene() { return &m_gfxScene; } GraphicsViewPtr graphicsView() { return GraphicsViewPtr{ &m_gfxScene, m_v3dView }; } - const Bnd_Box& graphicsBoundingBox() const { return m_gfxBoundingBox; } + + // Gets the graphics scene, container of all document's graphics objects + GraphicsScene* graphicsScene() { return &m_gfxScene; } + + // Returns the bounding box of all graphics objects satisfying `flags` + Bnd_Box graphicsBoundingBox(GraphicsBoundingBoxFlags flags = AllGraphics) const; // Gets/sets the ratio between physical pixels and device-independent pixels for the target window. // This value is dependent on the screen the window is on, and may have to be updated when the