From 3db4b8e70bbebb8662bc549c179c3652f4b56797 Mon Sep 17 00:00:00 2001 From: Leonardo Zide Date: Sat, 24 Oct 2026 13:16:12 -0700 Subject: [PATCH] Preserve ancestor steps while editing nested submodels Drilling into a child deactivated its parent and evaluated the parent at its final step. After navigating to an earlier step, this moved surrounding geometry and separated the child's editing transform from its placement. Keep the complete editing path active at its chosen steps, and use that path when filtering ancestor geometry for rendering. Rebuild the transform from the selected instances when restoring a tab so changes in an ancestor's own tab also keep the editing target aligned. Returning through a breadcrumb deactivates only departed descendants; ending editing restores completed assembly poses while retaining stored step choices. Also route Submodel Properties to the model currently being edited rather than the tab model. Validated the macOS Qt 6 application build, all 8 Qt Test cases, and 106 focused compiled-application checks covering ancestor poses and visibility, properties edits, tab switching, breadcrumbs, focus status, and exports. --- common/lc_mainwindow.cpp | 3 ++- common/lc_model.cpp | 4 ++- common/lc_scene.cpp | 1 + common/lc_scene.h | 9 ++++++- common/lc_view.cpp | 53 ++++++++++++++++++++-------------------- common/lc_view.h | 3 ++- common/project.cpp | 11 +++++---- 7 files changed, 48 insertions(+), 36 deletions(-) diff --git a/common/lc_mainwindow.cpp b/common/lc_mainwindow.cpp index bb841934..c343aeba 100644 --- a/common/lc_mainwindow.cpp +++ b/common/lc_mainwindow.cpp @@ -3611,7 +3611,8 @@ void lcMainWindow::HandleCommand(lcCommandId CommandId) break; case LC_MODEL_PROPERTIES: - lcGetActiveModel()->ShowPropertiesDialog(); + if (ActiveModel) + ActiveModel->ShowPropertiesDialog(); break; case LC_MODEL_LIST: diff --git a/common/lc_model.cpp b/common/lc_model.cpp index 91a34e1a..70c9a42f 100644 --- a/common/lc_model.cpp +++ b/common/lc_model.cpp @@ -1660,9 +1660,11 @@ void lcModel::GetScene(lcScene* Scene, const lcCamera* ViewCamera, bool AllowHig void lcModel::AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMatrix, int DefaultColorIndex, lcRenderMeshState RenderMeshState, bool ParentActive) const { + const bool Editing = ParentActive || Scene->IsModelEditing(this); + for (const std::unique_ptr& Piece : mPieces) { - const bool Visible = ParentActive ? (Piece->IsSelected() || Piece->IsVisible(mCurrentStep)) : Piece->IsVisibleInSubModel(); + const bool Visible = Editing ? (Piece->IsSelected() || Piece->IsVisible(mCurrentStep)) : Piece->IsVisibleInSubModel(); if (Visible) { diff --git a/common/lc_scene.cpp b/common/lc_scene.cpp index 1f2ed7b3..8b551b8e 100644 --- a/common/lc_scene.cpp +++ b/common/lc_scene.cpp @@ -21,6 +21,7 @@ void lcScene::Begin(const lcMatrix44& ViewMatrix) mHighlightRenderMode = lcHighlightRenderMode::Tint; mViewMatrix = ViewMatrix; mActiveSubmodelInstance = nullptr; + mModelEditingPath.clear(); mRenderMeshes.clear(); mOpaqueMeshes.clear(); mTranslucentMeshes.clear(); diff --git a/common/lc_scene.h b/common/lc_scene.h index 6a757284..4fe13f73 100644 --- a/common/lc_scene.h +++ b/common/lc_scene.h @@ -35,10 +35,16 @@ class lcScene public: lcScene(); - void SetActiveSubmodelInstance(lcPiece* ActiveSubmodelInstance, const lcMatrix44& ActiveSubmodelTransform) + void SetActiveSubmodelInstance(lcPiece* ActiveSubmodelInstance, const lcMatrix44& ActiveSubmodelTransform, const std::vector& ModelEditingPath) { mActiveSubmodelInstance = ActiveSubmodelInstance; mActiveSubmodelTransform = ActiveSubmodelTransform; + mModelEditingPath = ModelEditingPath; + } + + bool IsModelEditing(const lcModel* Model) const + { + return mDrawInterface && mActiveSubmodelInstance && std::find(mModelEditingPath.begin(), mModelEditingPath.end(), Model) != mModelEditingPath.end(); } lcPiece* GetActiveSubmodelInstance() const @@ -165,6 +171,7 @@ protected: lcMatrix44 mViewMatrix; lcMatrix44 mActiveSubmodelTransform; lcPiece* mActiveSubmodelInstance = nullptr; + std::vector mModelEditingPath; lcShadingMode mShadingMode; bool mDrawInterface = false; lcHighlightRenderMode mHighlightRenderMode; diff --git a/common/lc_view.cpp b/common/lc_view.cpp index ecbe0037..3eea10b6 100644 --- a/common/lc_view.cpp +++ b/common/lc_view.cpp @@ -152,12 +152,24 @@ std::vector lcView::GetModelEditingPath() const { std::vector Models = { mModel }; - for (const std::pair& Entry : mSubmodelEditingPath) - Models.push_back(Entry.first->mPieceData->GetModel()); + for (const lcPiece* Piece : mSubmodelEditingPath) + Models.push_back(Piece->mPieceData->GetModel()); return Models; } +void lcView::RestoreModelEditingContext() +{ + mModel->SetActive(true); + mActiveSubmodelTransform = lcMatrix44Identity(); + + for (lcPiece* Piece : mSubmodelEditingPath) + { + mActiveSubmodelTransform = lcMul(Piece->mModelWorld, mActiveSubmodelTransform); + Piece->mPieceData->GetModel()->SetActive(true); + } +} + void lcView::SetSubmodelActive(size_t Depth) { if (Depth > mSubmodelEditingPath.size()) @@ -165,26 +177,22 @@ void lcView::SetSubmodelActive(size_t Depth) StopTracking(true); - lcModel* ActiveModel = GetActiveModel(); + const std::vector EditingModels = GetModelEditingPath(); - if (mActiveSubmodelInstance) - { - ActiveModel->SetActive(false); + for (size_t Index = EditingModels.size(); Index > Depth + 1; Index--) + EditingModels[Index - 1]->SetActive(false); - std::vector UpdatedModels; - ActiveModel->UpdatePieceData(UpdatedModels); - } + std::vector UpdatedModels; + for (size_t Index = EditingModels.size(); Index > Depth + 1; Index--) + EditingModels[Index - 1]->UpdatePieceData(UpdatedModels); mSubmodelEditingPath.resize(Depth); mActiveSubmodelInstance = nullptr; if (!mSubmodelEditingPath.empty()) - { - mActiveSubmodelInstance = mSubmodelEditingPath.back().first; - mActiveSubmodelTransform = mSubmodelEditingPath.back().second; - } + mActiveSubmodelInstance = mSubmodelEditingPath.back(); - GetActiveModel()->SetActive(true); + RestoreModelEditingContext(); GetActiveModel()->UpdateInterface(); } @@ -206,22 +214,13 @@ void lcView::SetSelectedSubmodelActive() if (!Piece->mPieceData->IsModel()) return; - // Preserve the selected instance path and capture its transform before SetActive(false) changes the step. - lcMatrix44 SubmodelTransform = Piece->mModelWorld; - - if (mActiveSubmodelInstance) - SubmodelTransform = lcMul(SubmodelTransform, mActiveSubmodelTransform); - - if (mActiveSubmodelInstance) - ActiveModel->SetActive(false); - - mSubmodelEditingPath.emplace_back(Piece, SubmodelTransform); - mActiveSubmodelTransform = SubmodelTransform; + // Keep ancestor models at their chosen steps so the selected instance stays in place. + mSubmodelEditingPath.push_back(Piece); mActiveSubmodelInstance = Piece; ActiveModel = mActiveSubmodelInstance->mPieceData->GetModel(); ActiveModel->SetTemporaryStep(ActiveModel->GetLastStep()); - ActiveModel->SetActive(true); + RestoreModelEditingContext(); RemoveCamera(); @@ -1048,7 +1047,7 @@ void lcView::OnDraw() mScene->Begin(mCamera->mWorldView); - mScene->SetActiveSubmodelInstance(mActiveSubmodelInstance, mActiveSubmodelTransform); + mScene->SetActiveSubmodelInstance(mActiveSubmodelInstance, mActiveSubmodelTransform, GetModelEditingPath()); mScene->SetDrawInterface(DrawInterface); bool DrawInsertPreview = DrawInterface && std::any_of(mViews.begin(), mViews.end(), [](lcView* View){ return View->mTrackTool == lcTrackTool::Insert; }); diff --git a/common/lc_view.h b/common/lc_view.h index 3f1e1ca4..69b17c57 100644 --- a/common/lc_view.h +++ b/common/lc_view.h @@ -236,6 +236,7 @@ public: lcModel* GetActiveModel() const; std::vector GetModelEditingPath() const; + void RestoreModelEditingContext(); void SetSubmodelActive(size_t Depth); void SetTopSubmodelActive(); void SetSelectedSubmodelActive(); @@ -395,7 +396,7 @@ protected: lcModel* mModel = nullptr; lcPiece* mActiveSubmodelInstance = nullptr; lcMatrix44 mActiveSubmodelTransform; - std::vector> mSubmodelEditingPath; + std::vector mSubmodelEditingPath; lcCamera* mCamera = nullptr; diff --git a/common/project.cpp b/common/project.cpp index 82cf5c75..1ac7e867 100644 --- a/common/project.cpp +++ b/common/project.cpp @@ -234,12 +234,13 @@ void Project::SetActiveModel(lcModel* ActiveModel, bool UpdateInterface) { gMainWindow->SetCurrentModelTab(mActiveModel, true); - // The tab may still be editing a nested submodel at its current step. - lcModel* EditingModel = gMainWindow->GetActiveModel(); - if (EditingModel) + // Restore the current steps of the editing target and its surrounding ancestors. + lcView* EditingView = gMainWindow->GetActiveView(); + + if (EditingView) { - EditingModel->SetActive(true); - EditingModel->UpdateInterface(); + EditingView->RestoreModelEditingContext(); + EditingView->GetActiveModel()->UpdateInterface(); } } }