From 851eecee506189752529a29ca000ea7ce4b90369 Mon Sep 17 00:00:00 2001 From: Leonardo Zide Date: Wed, 21 Oct 2026 10:48:24 -0700 Subject: [PATCH] Store only changed objects in edit undo history Full before-and-after model snapshots accumulate large amounts of memory during editing, even when an operation changes only one object. Compact completed edits to retain only added, removed, or modified object states, with ordering metadata kept only for structural changes. Restore objects by stable IDs, including group relationships, and detach views before removing cameras. Compose merged edits from their deltas and compare animation keys as well as current property values so keyframe-only edits remain undoable. --- common/camera.cpp | 4 +- common/camera.h | 4 +- common/group.cpp | 25 +----- common/group.h | 10 ++- common/lc_model.cpp | 112 ++++++++++++++++-------- common/lc_model.h | 12 ++- common/lc_modelhistory.cpp | 46 ++++------ common/lc_modelhistory.h | 16 ++-- common/lc_objecthistory.h | 173 +++++++++++++++++++++++++++++++++++++ common/lc_objectproperty.h | 10 +++ common/light.cpp | 4 +- common/light.h | 4 +- common/piece.cpp | 26 ++---- common/piece.h | 8 +- leocad.pro | 1 + 15 files changed, 324 insertions(+), 131 deletions(-) create mode 100644 common/lc_objecthistory.h diff --git a/common/camera.cpp b/common/camera.cpp index 9f10ea64..1b6b0dd8 100644 --- a/common/camera.cpp +++ b/common/camera.cpp @@ -921,7 +921,7 @@ void lcCamera::RemoveKeyFrames() mUpVector.RemoveAllKeys(); } -lcCameraHistoryState lcCamera::GetHistoryState([[maybe_unused]] const lcModel* Model) const +lcCameraHistoryState lcCamera::GetHistoryState() const { lcCameraHistoryState State; @@ -940,7 +940,7 @@ lcCameraHistoryState lcCamera::GetHistoryState([[maybe_unused]] const lcModel* M return State; } -void lcCamera::SetHistoryState(const lcCameraHistoryState& State, [[maybe_unused]] const lcModel* Model) +void lcCamera::SetHistoryState(const lcCameraHistoryState& State) { mId = State.Id; mHidden = State.Hidden; diff --git a/common/camera.h b/common/camera.h index 82a39528..e8af95d6 100644 --- a/common/camera.h +++ b/common/camera.h @@ -191,8 +191,8 @@ public: bool HasKeyFrame(lcObjectPropertyId PropertyId, lcStep Time) const override; bool SetKeyFrame(lcObjectPropertyId PropertyId, lcStep Time, bool KeyFrame) override; void RemoveKeyFrames() override; - lcCameraHistoryState GetHistoryState(const lcModel* Model) const; - void SetHistoryState(const lcCameraHistoryState& State, const lcModel* Model); + lcCameraHistoryState GetHistoryState() const; + void SetHistoryState(const lcCameraHistoryState& State); void InsertTime(lcStep Start, lcStep Time); void RemoveTime(lcStep Start, lcStep Time); diff --git a/common/group.cpp b/common/group.cpp index 2517f5c5..3ae7125d 100644 --- a/common/group.cpp +++ b/common/group.cpp @@ -1,7 +1,6 @@ #include "lc_global.h" #include #include "group.h" -#include "lc_model.h" #include "lc_file.h" lcGroupId lcGroup::mNextId; @@ -63,36 +62,20 @@ void lcGroup::CreateName(const std::vector>& Groups) mName = Prefix + QString::number(Max + 1); } -lcGroupHistoryState lcGroup::GetHistoryState(const lcModel* Model) const +lcGroupHistoryState lcGroup::GetHistoryState() const { lcGroupHistoryState State; State.Id = mId; - State.ParentIndex = ~0U; + State.ParentId = mGroup ? static_cast(mGroup->GetId()) : LC_GROUP_ID_INVALID; State.Name = mName; - if (mGroup) - { - const std::vector>& Groups = Model->GetGroups(); - - for (size_t GroupIndex = 0; GroupIndex < Groups.size(); GroupIndex++) - { - if (Groups[GroupIndex].get() == mGroup) - { - State.ParentIndex = static_cast(GroupIndex); - break; - } - } - } - return State; } -void lcGroup::SetHistoryState(const lcGroupHistoryState& State, const lcModel* Model) +void lcGroup::SetHistoryState(const lcGroupHistoryState& State, lcGroup* Parent) { - const std::vector>& Groups = Model->GetGroups(); - mId = State.Id; mName = State.Name; - mGroup = (State.ParentIndex < Groups.size()) ? Groups[State.ParentIndex].get() : nullptr; + mGroup = Parent; } diff --git a/common/group.h b/common/group.h index ce758a75..651f7c1e 100644 --- a/common/group.h +++ b/common/group.h @@ -4,15 +4,17 @@ enum class lcGroupId : uint32_t; +constexpr uint32_t LC_GROUP_ID_INVALID = std::numeric_limits::max(); + struct lcGroupHistoryState { lcGroupId Id; - uint32_t ParentIndex; + uint32_t ParentId; QString Name; bool operator==(const lcGroupHistoryState& Other) const { - return Id == Other.Id && ParentIndex == Other.ParentIndex && Name == Other.Name; + return Id == Other.Id && ParentId == Other.ParentId && Name == Other.Name; } }; @@ -34,8 +36,8 @@ public: void FileLoad(lcFile* File); void CreateName(const std::vector>& Groups); - lcGroupHistoryState GetHistoryState(const lcModel* Model) const; - void SetHistoryState(const lcGroupHistoryState& State, const lcModel* Model); + lcGroupHistoryState GetHistoryState() const; + void SetHistoryState(const lcGroupHistoryState& State, lcGroup* Parent); lcGroup* mGroup = nullptr; QString mName; diff --git a/common/lc_model.cpp b/common/lc_model.cpp index dccd95ab..cdf53238 100644 --- a/common/lc_model.cpp +++ b/common/lc_model.cpp @@ -2262,43 +2262,43 @@ void lcModel::RemoveFromSelection(const std::vector& Objects) AddSelectionHistory([this, &Objects](){ SetObjectsSelected(Objects, false); }); } -template -void lcModel::LoadObjectHistoryState(const std::vector& ObjectStates, std::vector>& Objects) -{ - std::vector> NewObjects; - std::unordered_map ObjectIndices; - - NewObjects.reserve(ObjectStates.size()); - ObjectIndices.reserve(Objects.size()); - - for (size_t ObjectIndex = 0; ObjectIndex < Objects.size(); ObjectIndex++) - ObjectIndices.emplace(static_cast(Objects[ObjectIndex]->GetId()), ObjectIndex); - - for (const StateType& ObjectState : ObjectStates) - { - const auto ObjectIt = ObjectIndices.find(static_cast(ObjectState.Id)); - - if (ObjectIt != ObjectIndices.end()) - NewObjects.emplace_back(std::move(Objects[ObjectIt->second])); - else - NewObjects.emplace_back(new ObjectType()); - } - - if constexpr(std::is_same_v) - for (const std::unique_ptr& Camera : Objects) - if (Camera) - RemoveCameraFromViews(Camera.get()); - - Objects = std::move(NewObjects); - - for (size_t ObjectIndex = 0; ObjectIndex < Objects.size(); ObjectIndex++) - Objects[ObjectIndex]->SetHistoryState(ObjectStates[ObjectIndex], this); -} - void lcModel::LoadEditHistoryState(const lcModelHistoryEditState& HistoryState) { - LoadObjectHistoryState(HistoryState.Groups, mGroups); - LoadObjectHistoryState(HistoryState.Pieces, mPieces); + const std::unordered_map GroupsById = RestoreGroups(HistoryState.Groups); + RestorePieces(HistoryState.Pieces, GroupsById); + RestoreCameras(HistoryState.Cameras); + RestoreLights(HistoryState.Lights); +} + +std::unordered_map lcModel::RestoreGroups(const lcObjectHistory& History) +{ + const std::unordered_map GroupsById = History.RestoreObjects(mGroups); + + // The lookup uses saved IDs, so a parent can be linked before its state is restored. + for (const lcGroupHistoryState& State : History.States) + { + const auto Group = GroupsById.find(static_cast(State.Id)); + const auto Parent = GroupsById.find(State.ParentId); + + if (Group != GroupsById.end()) + Group->second->SetHistoryState(State, Parent != GroupsById.end() ? Parent->second : nullptr); + } + + return GroupsById; +} + +void lcModel::RestorePieces(const lcObjectHistory& History, const std::unordered_map& GroupsById) +{ + const std::unordered_map PiecesById = History.RestoreObjects(mPieces); + + for (const lcPieceHistoryState& State : History.States) + { + const auto Piece = PiecesById.find(static_cast(State.Id)); + const auto Group = GroupsById.find(State.GroupId); + + if (Piece != PiecesById.end()) + Piece->second->SetHistoryState(State, mProject, Group != GroupsById.end() ? Group->second : nullptr); + } // Model-list changes are not undoable, but older edit states can still name // a submodel that was replaced. Do not restore references that now recurse. @@ -2306,9 +2306,47 @@ void lcModel::LoadEditHistoryState(const lcModelHistoryEditState& HistoryState) { return Piece->mPieceData && Piece->mPieceData->IncludesModel(this); }), mPieces.end()); +} - LoadObjectHistoryState(HistoryState.Cameras, mCameras); - LoadObjectHistoryState(HistoryState.Lights, mLights); +void lcModel::RestoreCameras(const lcObjectHistory& History) +{ + if (History.IsEmpty()) + return; + + if (History.Order) + { + const std::unordered_set CameraIds(History.Order->begin(), History.Order->end()); + + for (const std::unique_ptr& Camera : mCameras) + if (CameraIds.find(static_cast(Camera->GetId())) == CameraIds.end()) + RemoveCameraFromViews(Camera.get()); + } + + const std::unordered_map CamerasById = History.RestoreObjects(mCameras); + + for (const lcCameraHistoryState& State : History.States) + { + const auto Camera = CamerasById.find(static_cast(State.Id)); + + if (Camera != CamerasById.end()) + Camera->second->SetHistoryState(State); + } +} + +void lcModel::RestoreLights(const lcObjectHistory& History) +{ + if (History.IsEmpty()) + return; + + const std::unordered_map LightsById = History.RestoreObjects(mLights); + + for (const lcLightHistoryState& State : History.States) + { + const auto Light = LightsById.find(static_cast(State.Id)); + + if (Light != LightsById.end()) + Light->second->SetHistoryState(State); + } } void lcModel::BeginEditHistory() diff --git a/common/lc_model.h b/common/lc_model.h index 5d800cc7..c332ef7f 100644 --- a/common/lc_model.h +++ b/common/lc_model.h @@ -15,6 +15,11 @@ enum class lcGeometryBoundsMode enum class lcCameraProjection; struct lcSetInventoryItem; struct lcModelHistoryEditState; +struct lcGroupHistoryState; +struct lcPieceHistoryState; +struct lcCameraHistoryState; +struct lcLightHistoryState; +template struct lcObjectHistory; class lcModelHistory; class lcModelHistorySelect; class lcModelHistoryEdit; @@ -291,8 +296,6 @@ public: void InsertStepAction(lcStep Step); void RemoveStepAction(lcStep Step); - template - void LoadObjectHistoryState(const std::vector& ObjectStates, std::vector>& Objects); void LoadEditHistoryState(const lcModelHistoryEditState& HistoryState); void LoadModelPropertiesState(const lcModelProperties& ModelProperties); @@ -478,6 +481,11 @@ protected: void DeleteModel(); + std::unordered_map RestoreGroups(const lcObjectHistory& History); + void RestorePieces(const lcObjectHistory& History, const std::unordered_map& GroupsById); + void RestoreCameras(const lcObjectHistory& History); + void RestoreLights(const lcObjectHistory& History); + void AddSelectionHistory(std::function Callback); void ClearSelection(); void SetFocus(lcObject* FocusObject, uint32_t FocusSection, lcSelectionMode SelectionMode); diff --git a/common/lc_modelhistory.cpp b/common/lc_modelhistory.cpp index 2f9340c1..4617e1fa 100644 --- a/common/lc_modelhistory.cpp +++ b/common/lc_modelhistory.cpp @@ -142,11 +142,6 @@ void lcModelHistorySelect::LoadState(const lcModelHistorySelectState& State, lcM } } -bool operator!=(const lcModelHistoryEditState& a, const lcModelHistoryEditState& b) -{ - return a.Groups != b.Groups || a.Pieces != b.Pieces || a.Cameras != b.Cameras || a.Lights != b.Lights; -} - lcModelHistoryEdit::lcModelHistoryEdit(lcModelHistoryEditMergeKey MergeKey) : mMergeKey(MergeKey) { @@ -158,25 +153,10 @@ lcModelHistoryEdit::~lcModelHistoryEdit() void lcModelHistoryEdit::SaveState(lcModelHistoryEditState& State, const lcModel* Model) { - const std::vector>& Groups = Model->GetGroups(); - - for (const std::unique_ptr& Group : Groups) - State.Groups.emplace_back(Group->GetHistoryState(Model)); - - const std::vector>& Pieces = Model->GetPieces(); - - for (const std::unique_ptr& Piece : Pieces) - State.Pieces.emplace_back(Piece->GetHistoryState(Model)); - - const std::vector>& Cameras = Model->GetCameras(); - - for (const std::unique_ptr& Camera : Cameras) - State.Cameras.emplace_back(Camera->GetHistoryState(Model)); - - const std::vector>& Lights = Model->GetLights(); - - for (const std::unique_ptr& Light : Lights) - State.Lights.emplace_back(Light->GetHistoryState(Model)); + State.Groups.Save(Model->GetGroups()); + State.Pieces.Save(Model->GetPieces()); + State.Cameras.Save(Model->GetCameras()); + State.Lights.Save(Model->GetLights()); } void lcModelHistoryEdit::LoadState(const lcModelHistoryEditState& State, lcModel* Model) @@ -192,6 +172,11 @@ void lcModelHistoryEdit::SaveStartState(const lcModel* Model) void lcModelHistoryEdit::SaveEndState(const lcModel* Model) { SaveState(mEndState, Model); + + lcObjectHistory::Compact(mStartState.Groups, mEndState.Groups); + lcObjectHistory::Compact(mStartState.Pieces, mEndState.Pieces); + lcObjectHistory::Compact(mStartState.Cameras, mEndState.Cameras); + lcObjectHistory::Compact(mStartState.Lights, mEndState.Lights); } void lcModelHistoryEdit::LoadStartState(lcModel* Model) const @@ -206,7 +191,7 @@ void lcModelHistoryEdit::LoadEndState(lcModel* Model) const bool lcModelHistoryEdit::StateChanged() const { - return mStartState != mEndState; + return !mStartState.IsEmpty() || !mEndState.IsEmpty(); } void lcModelHistoryEdit::RenamePieces(const std::vector& Renames, const lcPieceData* ModelPieceData) @@ -215,7 +200,7 @@ void lcModelHistoryEdit::RenamePieces(const std::vector& Ren auto RenamePieceIds = [&Renames](lcModelHistoryEditState& State) { - for (lcPieceHistoryState& Piece : State.Pieces) + for (lcPieceHistoryState& Piece : State.Pieces.States) { for (const lcModelPieceRename& Rename : Renames) { @@ -243,8 +228,13 @@ void lcModelHistoryEdit::MergeWith(lcModelHistory* Other) { lcModelHistoryEdit* OtherModelHistoryEdit = dynamic_cast(Other); - if (OtherModelHistoryEdit) - mEndState = std::move(OtherModelHistoryEdit->mEndState); + if (!OtherModelHistoryEdit) + return; + + lcObjectHistory::Merge(mStartState.Groups, mEndState.Groups, OtherModelHistoryEdit->mStartState.Groups, OtherModelHistoryEdit->mEndState.Groups); + lcObjectHistory::Merge(mStartState.Pieces, mEndState.Pieces, OtherModelHistoryEdit->mStartState.Pieces, OtherModelHistoryEdit->mEndState.Pieces); + lcObjectHistory::Merge(mStartState.Cameras, mEndState.Cameras, OtherModelHistoryEdit->mStartState.Cameras, OtherModelHistoryEdit->mEndState.Cameras); + lcObjectHistory::Merge(mStartState.Lights, mEndState.Lights, OtherModelHistoryEdit->mStartState.Lights, OtherModelHistoryEdit->mEndState.Lights); } void lcModelHistoryProperties::SaveStartState(const lcModel* Model) diff --git a/common/lc_modelhistory.h b/common/lc_modelhistory.h index 36f82413..597087d5 100644 --- a/common/lc_modelhistory.h +++ b/common/lc_modelhistory.h @@ -1,6 +1,7 @@ #pragma once #include "lc_model.h" +#include "lc_objecthistory.h" enum class lcObjectType; enum class lcSelectionMode; @@ -78,13 +79,16 @@ struct lcLightHistoryState; struct lcModelHistoryEditState { - std::vector Groups; - std::vector Pieces; - std::vector Cameras; - std::vector Lights; -}; + lcObjectHistory Groups; + lcObjectHistory Pieces; + lcObjectHistory Cameras; + lcObjectHistory Lights; -bool operator!=(const lcModelHistoryEditState& a, const lcModelHistoryEditState& b); + bool IsEmpty() const + { + return Groups.IsEmpty() && Pieces.IsEmpty() && Cameras.IsEmpty() && Lights.IsEmpty(); + } +}; enum class lcModelHistoryEditMerge { diff --git a/common/lc_objecthistory.h b/common/lc_objecthistory.h new file mode 100644 index 00000000..c96af5e6 --- /dev/null +++ b/common/lc_objecthistory.h @@ -0,0 +1,173 @@ +#pragma once + +// Object states are captured while an edit is in progress, then reduced to the +// changed objects. Ordering is only retained for insertions, deletions or reordering. +template +struct lcObjectHistory +{ + std::vector States; + std::optional> Order; + + template + void Save(const std::vector>& Objects) + { + States.clear(); + States.reserve(Objects.size()); + Order.emplace(); + Order->reserve(Objects.size()); + + for (const std::unique_ptr& Object : Objects) + { + States.emplace_back(Object->GetHistoryState()); + Order->emplace_back(static_cast(Object->GetId())); + } + } + + bool IsEmpty() const + { + return States.empty() && !Order; + } + + static void Compact(lcObjectHistory& Start, lcObjectHistory& End) + { + std::unordered_map StartStates; + std::unordered_map EndStates; + + StartStates.reserve(Start.States.size()); + EndStates.reserve(End.States.size()); + + for (const StateType& State : Start.States) + StartStates.emplace(static_cast(State.Id), &State); + + for (const StateType& State : End.States) + EndStates.emplace(static_cast(State.Id), &State); + + std::unordered_set Changed; + + for (const StateType& State : Start.States) + { + const auto Other = EndStates.find(static_cast(State.Id)); + + if (Other == EndStates.end() || !(State == *Other->second)) + Changed.emplace(static_cast(State.Id)); + } + + for (const StateType& State : End.States) + if (StartStates.find(static_cast(State.Id)) == StartStates.end()) + Changed.emplace(static_cast(State.Id)); + + auto KeepChanges = [&Changed](std::vector& States) + { + std::vector Changes; + Changes.reserve(std::min(States.size(), Changed.size())); + + for (StateType& State : States) + if (Changed.find(static_cast(State.Id)) != Changed.end()) + Changes.emplace_back(std::move(State)); + + States = std::move(Changes); + }; + + KeepChanges(Start.States); + KeepChanges(End.States); + + if (Start.Order == End.Order) + { + Start.Order.reset(); + End.Order.reset(); + } + } + + static void Merge(lcObjectHistory& Start, lcObjectHistory& End, lcObjectHistory& NextStart, lcObjectHistory& NextEnd) + { + std::map Before; + std::map After; + + for (StateType& State : Start.States) + Before.emplace(static_cast(State.Id), std::move(State)); + + for (StateType& State : End.States) + After.emplace(static_cast(State.Id), std::move(State)); + + for (StateType& State : NextStart.States) + { + const uint32_t Id = static_cast(State.Id); + + if (After.find(Id) == After.end()) + Before.emplace(Id, std::move(State)); + } + + for (const StateType& State : NextStart.States) + After.erase(static_cast(State.Id)); + + for (StateType& State : NextEnd.States) + After.insert_or_assign(static_cast(State.Id), std::move(State)); + + Start.States.clear(); + End.States.clear(); + + for (auto& [Id, State] : Before) + Start.States.emplace_back(std::move(State)); + + for (auto& [Id, State] : After) + End.States.emplace_back(std::move(State)); + + if (!Start.Order) + Start.Order = std::move(NextStart.Order); + + if (NextEnd.Order) + End.Order = std::move(NextEnd.Order); + + Compact(Start, End); + } + + template + std::unordered_map RestoreObjects(std::vector>& Objects) const + { + std::unordered_map ObjectsById; + + if (!Order) + { + ObjectsById.reserve(Objects.size()); + + for (const std::unique_ptr& Object : Objects) + ObjectsById.emplace(static_cast(Object->GetId()), Object.get()); + + return ObjectsById; + } + + std::unordered_map ObjectIndices; + ObjectIndices.reserve(Objects.size()); + + for (size_t Index = 0; Index < Objects.size(); Index++) + ObjectIndices.emplace(static_cast(Objects[Index]->GetId()), Index); + + std::unordered_set StateIds; + std::vector> NewObjects; + + for (const StateType& State : States) + StateIds.emplace(static_cast(State.Id)); + + NewObjects.reserve(Order->size()); + ObjectsById.reserve(Order->size()); + + for (uint32_t Id : *Order) + { + const auto Object = ObjectIndices.find(Id); + + if (Object != ObjectIndices.end()) + NewObjects.emplace_back(std::move(Objects[Object->second])); + else if (StateIds.find(Id) != StateIds.end()) + NewObjects.emplace_back(std::make_unique()); + else + continue; // A non-undoable model-list change may have removed it. + + // New objects do not have their saved IDs until their state is restored. + ObjectsById.emplace(Id, NewObjects.back().get()); + } + + Objects = std::move(NewObjects); + + return ObjectsById; + } +}; diff --git a/common/lc_objectproperty.h b/common/lc_objectproperty.h index 432863c5..e9546924 100644 --- a/common/lc_objectproperty.h +++ b/common/lc_objectproperty.h @@ -61,6 +61,11 @@ struct lcObjectPropertyKey { lcStep Step; T Value; + + bool operator==(const lcObjectPropertyKey& Other) const + { + return Step == Other.Step && Value == Other.Value; + } }; template @@ -79,6 +84,11 @@ public: return mValue; } + bool operator==(const lcObjectProperty& Other) const + { + return mValue == Other.mValue && mKeys == Other.mKeys; + } + void SetValue(const T& Value) { mValue = Value; diff --git a/common/light.cpp b/common/light.cpp index 0242b797..37f1360d 100644 --- a/common/light.cpp +++ b/common/light.cpp @@ -1511,7 +1511,7 @@ void lcLight::RemoveKeyFrames() mPOVRayFadePower.RemoveAllKeys(); } -lcLightHistoryState lcLight::GetHistoryState([[maybe_unused]] const lcModel* Model) const +lcLightHistoryState lcLight::GetHistoryState() const { lcLightHistoryState State; @@ -1541,7 +1541,7 @@ lcLightHistoryState lcLight::GetHistoryState([[maybe_unused]] const lcModel* Mod return State; } -void lcLight::SetHistoryState(const lcLightHistoryState& State, [[maybe_unused]] const lcModel* Model) +void lcLight::SetHistoryState(const lcLightHistoryState& State) { mId = State.Id; mHidden = State.Hidden; diff --git a/common/light.h b/common/light.h index a5012ae2..73c4add4 100644 --- a/common/light.h +++ b/common/light.h @@ -197,8 +197,8 @@ public: bool HasKeyFrame(lcObjectPropertyId PropertyId, lcStep Time) const override; bool SetKeyFrame(lcObjectPropertyId PropertyId, lcStep Time, bool KeyFrame) override; void RemoveKeyFrames() override; - lcLightHistoryState GetHistoryState(const lcModel* Model) const; - void SetHistoryState(const lcLightHistoryState& State, const lcModel* Model); + lcLightHistoryState GetHistoryState() const; + void SetHistoryState(const lcLightHistoryState& State); void InsertTime(lcStep Start, lcStep Time); void RemoveTime(lcStep Start, lcStep Time); diff --git a/common/piece.cpp b/common/piece.cpp index 4135f336..0b5e0c5c 100644 --- a/common/piece.cpp +++ b/common/piece.cpp @@ -988,7 +988,7 @@ void lcPiece::RemoveKeyFrames() mRotation.RemoveAllKeys(); } -lcPieceHistoryState lcPiece::GetHistoryState(const lcModel* Model) const +lcPieceHistoryState lcPiece::GetHistoryState() const { lcPieceHistoryState State; @@ -996,7 +996,7 @@ lcPieceHistoryState lcPiece::GetHistoryState(const lcModel* Model) const State.Hidden = mHidden; State.FileLine = mFileLine; State.PieceId = mID; - State.GroupIndex = ~0U; + State.GroupId = mGroup ? static_cast(mGroup->GetId()) : LC_GROUP_ID_INVALID; State.ColorIndex = mColorIndex; State.ColorCode = mColorCode; State.StepShow = mStepShow; @@ -1007,32 +1007,16 @@ lcPieceHistoryState lcPiece::GetHistoryState(const lcModel* Model) const State.Position = mPosition; State.Rotation = mRotation; - const std::vector>& Groups = Model->GetGroups(); - - if (mGroup) - { - for (size_t GroupIndex = 0; GroupIndex < Groups.size(); GroupIndex++) - { - if (mGroup == Groups[GroupIndex].get()) - { - State.GroupIndex = static_cast(GroupIndex); - break; - } - } - } - return State; } -void lcPiece::SetHistoryState(const lcPieceHistoryState& State, const lcModel* Model) +void lcPiece::SetHistoryState(const lcPieceHistoryState& State, Project* Project, lcGroup* Group) { - const std::vector>& Groups = Model->GetGroups(); - mId = State.Id; mHidden = State.Hidden; mFileLine = State.FileLine; mID = State.PieceId; - mGroup = (State.GroupIndex < Groups.size()) ? Groups[State.GroupIndex].get() : nullptr; + mGroup = Group; mColorIndex = State.ColorIndex; mColorCode = State.ColorCode; mStepShow = State.StepShow; @@ -1043,7 +1027,7 @@ void lcPiece::SetHistoryState(const lcPieceHistoryState& State, const lcModel* M mPosition = State.Position; mRotation = State.Rotation; - lcPieceDataRef RestoredData = lcGetPiecesLibrary()->AcquirePieceData(mID.toLatin1(), Model->GetProject(), true, true); + lcPieceDataRef RestoredData = lcGetPiecesLibrary()->AcquirePieceData(mID.toLatin1(), Project, true, true); if (mPieceData != RestoredData) { diff --git a/common/piece.h b/common/piece.h index a548fe5d..ee499993 100644 --- a/common/piece.h +++ b/common/piece.h @@ -35,7 +35,7 @@ struct lcPieceHistoryState bool Hidden; int FileLine; QString PieceId; - uint32_t GroupIndex; + uint32_t GroupId; int ColorIndex; quint32 ColorCode; lcStep StepShow; @@ -49,7 +49,7 @@ struct lcPieceHistoryState bool operator==(const lcPieceHistoryState& Other) const { return Id == Other.Id && Hidden == Other.Hidden && FileLine == Other.FileLine && PieceId == Other.PieceId && - GroupIndex == Other.GroupIndex && ColorIndex == Other.ColorIndex && ColorCode == Other.ColorCode && + GroupId == Other.GroupId && ColorIndex == Other.ColorIndex && ColorCode == Other.ColorCode && StepShow == Other.StepShow && StepHide == Other.StepHide && PivotMatrix == Other.PivotMatrix && PivotPointValid == Other.PivotPointValid && ControlPoints == Other.ControlPoints && Position == Other.Position && Rotation == Other.Rotation; @@ -95,8 +95,8 @@ public: bool HasKeyFrame(lcObjectPropertyId PropertyId, lcStep Time) const override; bool SetKeyFrame(lcObjectPropertyId PropertyId, lcStep Time, bool KeyFrame) override; void RemoveKeyFrames() override; - lcPieceHistoryState GetHistoryState(const lcModel* Model) const; - void SetHistoryState(const lcPieceHistoryState& State, const lcModel* Model); + lcPieceHistoryState GetHistoryState() const; + void SetHistoryState(const lcPieceHistoryState& State, Project* Project, lcGroup* Group); void AddMainModelRenderMeshes(lcScene* Scene, bool Highlight, bool Fade) const; void AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMatrix, int DefaultColorIndex, lcRenderMeshState RenderMeshState, bool ParentActive) const; diff --git a/leocad.pro b/leocad.pro index 3b2ad939..1a46edbf 100644 --- a/leocad.pro +++ b/leocad.pro @@ -304,6 +304,7 @@ HEADERS += \ common/lc_minifigdialog.h \ common/lc_model.h \ common/lc_modellistdialog.h \ + common/lc_objecthistory.h \ common/lc_objectproperty.h \ common/lc_pagesetupdialog.h \ common/lc_partpalettedialog.h \