Fixed bugs with undo and redo of multiple objects

This commit is contained in:
Yousef Khadadeh 2025-07-27 20:04:26 +03:00
parent 2de5c90006
commit 9aa41ddb7f
7 changed files with 74 additions and 52 deletions

View file

@ -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<CollabListType::ObjectInfoPtr> erasedComponents) {
std::unordered_set<ServerClientID> 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<CollabListType::ObjectInfoPtr>& erasedComponents) {
world.undo.push(UndoManager::UndoRedoPair{
[&, erasedComponents = erasedComponents]() {
std::vector<CollabListType::ObjectInfoPtr> 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<ServerClientID> 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();

View file

@ -61,6 +61,7 @@ class DrawingProgram {
Vector4f* get_foreground_color_ptr();
private:
void client_erase_set(std::unordered_set<CollabListType::ObjectInfoPtr> 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<CollabListType::ObjectInfoPtr>& objSetToUndo);
BrushTool brushTool;
EraserTool eraserTool;

View file

@ -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<CollabListType::ObjectInfoPtr>& 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::shared_ptr<DrawingProgramCacheBVHNode>, std::vector<CollabListType::ObjectInfoPtr>::iterator> DrawingProgramCache::get_bvh_node_fully_containing_recursive(const std::shared_ptr<DrawingProgramCacheBVHNode>& 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<SCollision::AABB<WorldScalar>>& 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<WorldScalar>& aabb, uint64_t placementToInvalidateAt) {
std::erase_if(nodesWithCachedSurfaces, [&](auto& n) {
if(placementToInvalidateAt <= n->drawCache.value().lastDrawnComponentPlacement && SCollision::collide(aabb, n->bounds)) {

View file

@ -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<WorldScalar>& aabb, uint64_t placementToInvalidateAt);
void invalidate_cache_at_optional_aabb_before_pos(const std::optional<SCollision::AABB<WorldScalar>>& aabb, uint64_t placementToInvalidateAt);
void clear();
void draw_components_to_canvas(SkCanvas* canvas, const DrawData& drawData, uint64_t* lastDrawnComponentPlacement = nullptr);
const std::vector<CollabListType::ObjectInfoPtr>& get_unsorted_component_list() const;

View file

@ -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<CollabListType::ObjectInfoPtr>(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());

View file

@ -17,6 +17,7 @@ class DrawingProgramSelection {
bool is_selected(const CollabListType::ObjectInfoPtr& objToCheck);
const std::unordered_set<CollabListType::ObjectInfoPtr>& 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();

View file

@ -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<ServerClientID> 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<CollabListType::ObjectInfoPtr> 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<ServerClientID> 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();