From 9aa41ddb7f503e008230fb68af55d9befe1610da Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sun, 27 Jul 2025 20:04:26 +0300 Subject: [PATCH] Fixed bugs with undo and redo of multiple objects --- src/DrawingProgram/DrawingProgram.cpp | 48 ++++++++++++++++++- src/DrawingProgram/DrawingProgram.hpp | 2 + src/DrawingProgram/DrawingProgramCache.cpp | 16 ++++--- src/DrawingProgram/DrawingProgramCache.hpp | 1 + .../DrawingProgramSelection.cpp | 13 ++++- .../DrawingProgramSelection.hpp | 1 + src/DrawingProgram/EraserTool.cpp | 45 +---------------- 7 files changed, 74 insertions(+), 52 deletions(-) diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index 516edf0..2cd5579 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -519,6 +519,15 @@ void DrawingProgram::initialize_draw_data(cereal::PortableBinaryInputArchive& a) compCache.test_rebuild(components.client_list(), true); } +void DrawingProgram::client_erase_set(std::unordered_set erasedComponents) { + std::unordered_set idsToErase; + for(auto& c : erasedComponents) + idsToErase.emplace(c->id); + DrawComponent::client_send_erase_set(*this, idsToErase); + components.client_erase_set(erasedComponents); + add_undo_erase_components(erasedComponents); +} + void DrawingProgram::add_undo_place_component(const CollabListType::ObjectInfoPtr& objToUndo) { world.undo.push(UndoManager::UndoRedoPair{ [&, objToUndo]() { @@ -531,7 +540,6 @@ void DrawingProgram::add_undo_place_component(const CollabListType::ObjectInfoPt if(objToUndo->obj->collabListInfo.lock()) return false; components.client_insert(objToUndo); - objToUndo->obj->final_update(*this); // Method to recover after an undo that happened while drawing a component objToUndo->obj->client_send_place(*this); reset_tools(); return true; @@ -539,6 +547,44 @@ void DrawingProgram::add_undo_place_component(const CollabListType::ObjectInfoPt }); } +void DrawingProgram::add_undo_erase_components(const std::unordered_set& erasedComponents) { + world.undo.push(UndoManager::UndoRedoPair{ + [&, erasedComponents = erasedComponents]() { + std::vector sortedObjects(erasedComponents.begin(), erasedComponents.end()); + std::sort(sortedObjects.begin(), sortedObjects.end(), [](auto& a, auto& b) { + return a->pos < b->pos; + }); + + for(auto& comp : sortedObjects) + if(comp->obj->collabListInfo.lock()) + return false; + + components.client_insert_ordered_vector(sortedObjects); + DrawComponent::client_send_place_many(*this, sortedObjects); + + reset_tools(); + + return true; + }, + [&, erasedComponents = erasedComponents]() { + for(auto& comp : erasedComponents) + if(!comp->obj->collabListInfo.lock()) + return false; + + std::unordered_set idsToErase; + for(auto& c : erasedComponents) + idsToErase.emplace(c->id); + + DrawComponent::client_send_erase_set(*this, idsToErase); + components.client_erase_set(erasedComponents); + + reset_tools(); + + return true; + } + }); +} + ClientPortionID DrawingProgram::get_max_id(ServerPortionID serverID) { ClientPortionID maxClientID = 0; auto& cList = components.client_list(); diff --git a/src/DrawingProgram/DrawingProgram.hpp b/src/DrawingProgram/DrawingProgram.hpp index c36c73f..c669c4a 100644 --- a/src/DrawingProgram/DrawingProgram.hpp +++ b/src/DrawingProgram/DrawingProgram.hpp @@ -61,6 +61,7 @@ class DrawingProgram { Vector4f* get_foreground_color_ptr(); private: + void client_erase_set(std::unordered_set erasedComponents); // The set might be modified while this is being called, so dont pass by reference void check_delayed_update_timers(); void check_updateable_components(); @@ -79,6 +80,7 @@ class DrawingProgram { void draw_drag_circle(SkCanvas* canvas, const Vector2f& pos, const SkColor4f& c, const DrawData& drawData, float radiusMultiplier = 1.0f); void add_undo_place_component(const CollabListType::ObjectInfoPtr& objToUndo); + void add_undo_erase_components(const std::unordered_set& objSetToUndo); BrushTool brushTool; EraserTool eraserTool; diff --git a/src/DrawingProgram/DrawingProgramCache.cpp b/src/DrawingProgram/DrawingProgramCache.cpp index 82105d8..fa72628 100644 --- a/src/DrawingProgram/DrawingProgramCache.cpp +++ b/src/DrawingProgram/DrawingProgramCache.cpp @@ -51,7 +51,7 @@ void DrawingProgramCache::clear_own_cached_surfaces() { } bool DrawingProgramCache::check_if_rebuild_should_occur() { - return (unsortedComponents.size() >= MINIMUM_COMPONENTS_TO_START_REBUILD && (std::chrono::steady_clock::now() - lastBvhBuildTime) >= std::chrono::seconds(5)); + return (unsortedComponents.size() >= MINIMUM_COMPONENTS_TO_START_REBUILD); } void DrawingProgramCache::test_rebuild(const std::vector& comps, bool force) { @@ -105,10 +105,7 @@ const std::chrono::steady_clock::time_point& DrawingProgramCache::get_last_bvh_b void DrawingProgramCache::add_component(const CollabListType::ObjectInfoPtr& c) { unsortedComponents.emplace_back(c); - if(c->obj->worldAABB) - invalidate_cache_at_aabb_before_pos(c->obj->worldAABB.value(), c->pos); - else - invalidate_cache_before_pos(c->pos); + invalidate_cache_at_optional_aabb_before_pos(c->obj->worldAABB, c->pos); } void DrawingProgramCache::erase_component(const CollabListType::ObjectInfoPtr& c) { @@ -119,7 +116,7 @@ void DrawingProgramCache::erase_component(const CollabListType::ObjectInfoPtr& c } else std::erase(unsortedComponents, c); - invalidate_cache_before_pos(c->pos); + invalidate_cache_at_optional_aabb_before_pos(c->obj->worldAABB, c->pos); } std::pair, std::vector::iterator> DrawingProgramCache::get_bvh_node_fully_containing_recursive(const std::shared_ptr& bvhNode, DrawComponent* c) { @@ -261,6 +258,13 @@ void DrawingProgramCache::invalidate_cache_before_pos(uint64_t placementToInvali }); } +void DrawingProgramCache::invalidate_cache_at_optional_aabb_before_pos(const std::optional>& aabb, uint64_t placementToInvalidateAt) { + if(aabb) + invalidate_cache_at_aabb_before_pos(aabb.value(), placementToInvalidateAt); + else + invalidate_cache_before_pos(placementToInvalidateAt); +} + void DrawingProgramCache::invalidate_cache_at_aabb_before_pos(const SCollision::AABB& aabb, uint64_t placementToInvalidateAt) { std::erase_if(nodesWithCachedSurfaces, [&](auto& n) { if(placementToInvalidateAt <= n->drawCache.value().lastDrawnComponentPlacement && SCollision::collide(aabb, n->bounds)) { diff --git a/src/DrawingProgram/DrawingProgramCache.hpp b/src/DrawingProgram/DrawingProgramCache.hpp index 62caa63..14f60a0 100644 --- a/src/DrawingProgram/DrawingProgramCache.hpp +++ b/src/DrawingProgram/DrawingProgramCache.hpp @@ -55,6 +55,7 @@ class DrawingProgramCache { void preupdate_component(const CollabListType::ObjectInfoPtr& c); void invalidate_cache_before_pos(uint64_t placementToInvalidateAt); void invalidate_cache_at_aabb_before_pos(const SCollision::AABB& aabb, uint64_t placementToInvalidateAt); + void invalidate_cache_at_optional_aabb_before_pos(const std::optional>& aabb, uint64_t placementToInvalidateAt); void clear(); void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, uint64_t* lastDrawnComponentPlacement = nullptr); const std::vector& get_unsorted_component_list() const; diff --git a/src/DrawingProgram/DrawingProgramSelection.cpp b/src/DrawingProgram/DrawingProgramSelection.cpp index 6798633..7ff4eb0 100644 --- a/src/DrawingProgram/DrawingProgramSelection.cpp +++ b/src/DrawingProgram/DrawingProgramSelection.cpp @@ -172,14 +172,25 @@ bool DrawingProgramSelection::is_selected(const CollabListType::ObjectInfoPtr& o return selectedSet.contains(objToCheck); } +void DrawingProgramSelection::delete_all() { + if(is_something_selected()) { + drawP.client_erase_set(selectedSet); + selectedSet.clear(); + cache.clear(); + transformOpHappening = TransformOperation::NONE; + selectionTransformCoords = CoordSpaceHelper(); + } +} + void DrawingProgramSelection::update() { if(is_something_selected()) { - if(cache.check_if_rebuild_should_occur()) cache.test_rebuild(std::vector(selectedSet.begin(), selectedSet.end()), true); if(drawP.world.main.input.key(InputManager::KEY_DRAW_UNSELECT).pressed) deselect_all(); + else if(drawP.world.main.input.key(InputManager::KEY_DRAW_DELETE).pressed) + delete_all(); selectionRectPoints[0] = selectionTransformCoords.from_space_world(initialSelectionAABB.min); selectionRectPoints[1] = selectionTransformCoords.from_space_world(initialSelectionAABB.bottom_left()); diff --git a/src/DrawingProgram/DrawingProgramSelection.hpp b/src/DrawingProgram/DrawingProgramSelection.hpp index 15561ed..0a313cf 100644 --- a/src/DrawingProgram/DrawingProgramSelection.hpp +++ b/src/DrawingProgram/DrawingProgramSelection.hpp @@ -17,6 +17,7 @@ class DrawingProgramSelection { bool is_selected(const CollabListType::ObjectInfoPtr& objToCheck); const std::unordered_set& get_selected_set(); private: + void delete_all(); bool mouse_collided_with_selection_aabb(); bool mouse_collided_with_scale_point(); bool mouse_collided_with_rotate_center_handle_point(); diff --git a/src/DrawingProgram/EraserTool.cpp b/src/DrawingProgram/EraserTool.cpp index 070d238..55d49bc 100644 --- a/src/DrawingProgram/EraserTool.cpp +++ b/src/DrawingProgram/EraserTool.cpp @@ -23,50 +23,7 @@ void EraserTool::gui_toolbox() { void EraserTool::reset_tool() { if(!erasedComponents.empty()) { DrawingProgramCache::move_components_from_bvh_nodes_to_set(erasedComponents, erasedBVHNodes); - - std::unordered_set idsToErase1; - for(auto& c : erasedComponents) - idsToErase1.emplace(c->id); - DrawComponent::client_send_erase_set(drawP, idsToErase1); - drawP.components.client_erase_set(erasedComponents); - - drawP.world.undo.push(UndoManager::UndoRedoPair{ - [&, erasedComponents = erasedComponents]() { - std::vector sortedObjects(erasedComponents.begin(), erasedComponents.end()); - std::sort(sortedObjects.begin(), sortedObjects.end(), [](auto& a, auto& b) { - return a->pos < b->pos; - }); - - for(auto& comp : sortedObjects) - if(comp->obj->collabListInfo.lock()) - return false; - - drawP.components.client_insert_ordered_vector(sortedObjects); - DrawComponent::client_send_place_many(drawP, sortedObjects); - - drawP.reset_tools(); - - return true; - }, - [&, erasedComponents = erasedComponents]() { - for(auto& comp : erasedComponents) - if(!comp->obj->collabListInfo.lock()) - return false; - - std::unordered_set idsToErase; - for(auto& c : erasedComponents) - idsToErase.emplace(c->id); - - DrawComponent::client_send_erase_set(drawP, idsToErase); - drawP.components.client_erase_set(erasedComponents); - - drawP.reset_tools(); - - drawP.force_rebuild_cache(); - - return true; - } - }); + drawP.client_erase_set(erasedComponents); } erasedComponents.clear(); erasedBVHNodes.clear();