From 121a6b05aa41521900ff16df34e263d83d2d30cf Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Fri, 29 May 2026 18:20:06 +0300 Subject: [PATCH] Added more limits to eraser to decrease chance of generating bad geometry --- src/CanvasComponents/CanvasComponentContainer.cpp | 9 +++++---- src/CanvasComponents/CanvasComponentContainer.hpp | 3 +-- src/DrawingProgram/Tools/EraserTool.cpp | 5 ++++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/CanvasComponents/CanvasComponentContainer.cpp b/src/CanvasComponents/CanvasComponentContainer.cpp index 384504f..078609c 100644 --- a/src/CanvasComponents/CanvasComponentContainer.cpp +++ b/src/CanvasComponents/CanvasComponentContainer.cpp @@ -148,12 +148,14 @@ void CanvasComponentContainer::commit_transform_dont_invalidate_cache() { calculate_world_bounds(); } -CanvasComponentEraseDetailResult CanvasComponentContainer::collides_with_erase_detail(const CoordSpaceHelper& camCoords, const SkPath& checkAgainstCam) const { +// camCoordsScaleToCheckAgainst is different from camCoords.inverseScale if we locked the eraser's scale. +CanvasComponentEraseDetailResult CanvasComponentContainer::collides_with_erase_detail(const CoordSpaceHelper& camCoords, const WorldScalar& camCoordsScaleToCheckAgainst, const SkPath& checkAgainstCam) const { + constexpr static int COMP_MAX_SHIFT_BEFORE_STOP_ERASE = 3; if(!worldAABB.has_value()) return CanvasComponentEraseDetailResult::NO_CHANGE; - if((camCoords.inverseScale << COMP_MAX_SHIFT_BEFORE_STOP_ERASE) < coords.inverseScale) // Object is too large, just dismiss the collision + if((camCoordsScaleToCheckAgainst << COMP_MAX_SHIFT_BEFORE_STOP_ERASE) < coords.inverseScale) // Object is too large, just dismiss the collision return CanvasComponentEraseDetailResult::NO_CHANGE; - else if(coords.inverseScale < (camCoords.inverseScale >> COMP_COLLIDE_MIN_SHIFT_TINY)) { + else if(coords.inverseScale < (camCoordsScaleToCheckAgainst >> COMP_COLLIDE_MIN_SHIFT_TINY)) { if(checkAgainstCam.contains(convert_vec2(camCoords.to_space(worldAABB.value().min)))) return CanvasComponentEraseDetailResult::REMOVED; return CanvasComponentEraseDetailResult::NO_CHANGE; @@ -170,7 +172,6 @@ CanvasComponentEraseDetailResult CanvasComponentContainer::collides_with_erase_d return CanvasComponentEraseDetailResult::NO_CHANGE; } -// We could just send one of the checkAgainst colliders, since they represent the same thing, but sending both saves on redundant transformations, since we can save both of the versions on the executor side bool CanvasComponentContainer::collides_with(const CoordSpaceHelper& camCoords, const SkPath& checkAgainstCam) const { if(!worldAABB.has_value()) return false; diff --git a/src/CanvasComponents/CanvasComponentContainer.hpp b/src/CanvasComponents/CanvasComponentContainer.hpp index 991e866..e38390d 100644 --- a/src/CanvasComponents/CanvasComponentContainer.hpp +++ b/src/CanvasComponents/CanvasComponentContainer.hpp @@ -44,7 +44,6 @@ class CanvasComponentContainer { typedef NetworkingObjects::NetObjOrderedListObjectInfo ObjInfo; typedef NetworkingObjects::NetObjOrderedListIterator ObjInfoIterator; - constexpr static int COMP_MAX_SHIFT_BEFORE_STOP_ERASE = 7; constexpr static int COMP_MAX_SHIFT_BEFORE_STOP_COLLISIONS = 14; constexpr static int COMP_MAX_SHIFT_BEFORE_STOP_SCALING = 14; constexpr static float COMP_MAX_BEFORE_STOP_SCALING = 1 << COMP_MAX_SHIFT_BEFORE_STOP_SCALING; @@ -91,7 +90,7 @@ class CanvasComponentContainer { void commit_transform(DrawingProgram& drawP); void commit_update_dont_invalidate_cache(DrawingProgram& drawP); // Must be thread safe bool should_draw(const DrawData& drawData) const; - CanvasComponentEraseDetailResult collides_with_erase_detail(const CoordSpaceHelper& camCoords, const SkPath& checkAgainstCam) const; + CanvasComponentEraseDetailResult collides_with_erase_detail(const CoordSpaceHelper& camCoords, const WorldScalar& camCoordsScaleToCheckAgainst, const SkPath& checkAgainstCam) const; bool collides_with(const CoordSpaceHelper& camCoords, const SkPath& checkAgainstCam) const; bool collides_with_point(const CoordSpaceHelper& camCoords, const Vector2f& checkAgainstCam) const; void send_comp_update(DrawingProgram& drawP, bool finalUpdate); diff --git a/src/DrawingProgram/Tools/EraserTool.cpp b/src/DrawingProgram/Tools/EraserTool.cpp index 085140f..e577ca0 100644 --- a/src/DrawingProgram/Tools/EraserTool.cpp +++ b/src/DrawingProgram/Tools/EraserTool.cpp @@ -124,6 +124,9 @@ void EraserTool::input_pen_axis_callback(const InputManager::PenAxisCallbackArgs void EraserTool::erase_on_path() { auto cCWorldBounds = genData.coords.collider_to_world, SCollision::AABB>(erasePath.getBounds()); + WorldScalar eraseScaleToCheckAgainst = WorldScalar(drawP.world.main.toolConfig.get_relative_width_stroke_size(drawP, genData.coords.inverseScale).first.value()) * genData.coords.inverseScale; + if(eraseScaleToCheckAgainst == WorldScalar(0)) + return; drawP.drawCache.traverse_bvh_run_function(cCWorldBounds, [&](const auto& bvhNode) { if(bvhNode && erasePath.contains(convert_vec2(genData.coords.to_space(bvhNode->bounds.min))) && @@ -152,7 +155,7 @@ void EraserTool::erase_on_path() { if(drawP.world.main.toolConfig.eraser.eraseDetail) { if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector)) { auto dataCopy = c->obj->get_comp().get_data_copy(); - CanvasComponentEraseDetailResult result = c->obj->collides_with_erase_detail(genData.coords, erasePath); + CanvasComponentEraseDetailResult result = c->obj->collides_with_erase_detail(genData.coords, eraseScaleToCheckAgainst, erasePath); switch(result) { case CanvasComponentEraseDetailResult::NO_CHANGE: return false;