Working on detail eraser
This commit is contained in:
parent
60ab8f3c73
commit
c54fef383c
15 changed files with 255 additions and 58 deletions
53
src/DrawingProgram/EditCanvasComponentWorldUndoAction.cpp
Normal file
53
src/DrawingProgram/EditCanvasComponentWorldUndoAction.cpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* InfiniPaint
|
||||
* Copyright (C) 2025-2026 Yousef Khadadeh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "EditCanvasComponentWorldUndoAction.hpp"
|
||||
#include "../CanvasComponents/CanvasComponent.hpp"
|
||||
#include "../World.hpp"
|
||||
|
||||
EditCanvasComponentWorldUndoAction::EditCanvasComponentWorldUndoAction(std::unique_ptr<CanvasComponent> initData, WorldUndoManager::UndoObjectID initUndoID):
|
||||
data(std::move(initData)),
|
||||
undoID(initUndoID)
|
||||
{}
|
||||
|
||||
std::string EditCanvasComponentWorldUndoAction::get_name() const {
|
||||
return "Edit Canvas Component";
|
||||
}
|
||||
|
||||
bool EditCanvasComponentWorldUndoAction::undo(WorldUndoManager& undoMan) {
|
||||
return undo_redo(undoMan);
|
||||
}
|
||||
|
||||
bool EditCanvasComponentWorldUndoAction::redo(WorldUndoManager& undoMan) {
|
||||
return undo_redo(undoMan);
|
||||
}
|
||||
|
||||
bool EditCanvasComponentWorldUndoAction::undo_redo(WorldUndoManager& undoMan) {
|
||||
std::optional<NetworkingObjects::NetObjID> toEditID = undoMan.get_netid_from_undoid(undoID);
|
||||
if(!toEditID.has_value())
|
||||
return false;
|
||||
auto objPtr = undoMan.world.netObjMan.get_obj_temporary_ref_from_id<CanvasComponentContainer>(toEditID.value());
|
||||
std::unique_ptr<CanvasComponent> newData = objPtr->get_comp().get_data_copy();
|
||||
objPtr->get_comp().set_data_from(*data);
|
||||
data = std::move(newData);
|
||||
objPtr->commit_update(undoMan.world.drawProg);
|
||||
objPtr->send_comp_update(undoMan.world.drawProg, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
EditCanvasComponentWorldUndoAction::~EditCanvasComponentWorldUndoAction() {}
|
||||
33
src/DrawingProgram/EditCanvasComponentWorldUndoAction.hpp
Normal file
33
src/DrawingProgram/EditCanvasComponentWorldUndoAction.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* InfiniPaint
|
||||
* Copyright (C) 2025-2026 Yousef Khadadeh
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "../CanvasComponents/CanvasComponentContainer.hpp"
|
||||
|
||||
class EditCanvasComponentWorldUndoAction : public WorldUndoAction {
|
||||
public:
|
||||
EditCanvasComponentWorldUndoAction(std::unique_ptr<CanvasComponent> initData, WorldUndoManager::UndoObjectID initUndoID);
|
||||
std::string get_name() const override;
|
||||
bool undo(WorldUndoManager& undoMan) override;
|
||||
bool redo(WorldUndoManager& undoMan) override;
|
||||
bool undo_redo(WorldUndoManager& undoMan);
|
||||
~EditCanvasComponentWorldUndoAction();
|
||||
|
||||
std::unique_ptr<CanvasComponent> data;
|
||||
WorldUndoManager::UndoObjectID undoID;
|
||||
};
|
||||
|
|
@ -33,7 +33,8 @@ class ToolConfiguration {
|
|||
|
||||
struct EraserToolConfig {
|
||||
float relativeWidth = 15.0f;
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(EraserToolConfig, relativeWidth)
|
||||
bool eraseDetail = false;
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(EraserToolConfig, relativeWidth, eraseDetail)
|
||||
} eraser;
|
||||
|
||||
struct EllipseDrawToolConfig {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "EditTools/ImageEditTool.hpp"
|
||||
#include "EditTools/EllipseDrawEditTool.hpp"
|
||||
#include "EditTools/MeshEditTool.hpp"
|
||||
#include "../EditCanvasComponentWorldUndoAction.hpp"
|
||||
|
||||
#include "../../GUIStuff/ElementHelpers/TextLabelHelpers.hpp"
|
||||
|
||||
|
|
@ -214,42 +215,8 @@ void EditTool::switch_tool(DrawingProgramToolType newTool) {
|
|||
objInfoBeingEdited->obj->commit_update(drawP);
|
||||
objInfoBeingEdited->obj->send_comp_update(drawP, true);
|
||||
|
||||
if(undoAfterEditDone) {
|
||||
class EditCanvasComponentWorldUndoAction : public WorldUndoAction {
|
||||
public:
|
||||
EditCanvasComponentWorldUndoAction(std::unique_ptr<CanvasComponent> initData, WorldUndoManager::UndoObjectID initUndoID):
|
||||
data(std::move(initData)),
|
||||
undoID(initUndoID)
|
||||
{}
|
||||
std::string get_name() const override {
|
||||
return "Edit Canvas Component";
|
||||
}
|
||||
bool undo(WorldUndoManager& undoMan) override {
|
||||
return undo_redo(undoMan);
|
||||
}
|
||||
bool redo(WorldUndoManager& undoMan) override {
|
||||
return undo_redo(undoMan);
|
||||
}
|
||||
bool undo_redo(WorldUndoManager& undoMan) {
|
||||
std::optional<NetworkingObjects::NetObjID> toEditID = undoMan.get_netid_from_undoid(undoID);
|
||||
if(!toEditID.has_value())
|
||||
return false;
|
||||
auto objPtr = undoMan.world.netObjMan.get_obj_temporary_ref_from_id<CanvasComponentContainer>(toEditID.value());
|
||||
std::unique_ptr<CanvasComponent> newData = objPtr->get_comp().get_data_copy();
|
||||
objPtr->get_comp().set_data_from(*data);
|
||||
data = std::move(newData);
|
||||
objPtr->commit_update(undoMan.world.drawProg);
|
||||
objPtr->send_comp_update(undoMan.world.drawProg, true);
|
||||
return true;
|
||||
}
|
||||
~EditCanvasComponentWorldUndoAction() {}
|
||||
|
||||
std::unique_ptr<CanvasComponent> data;
|
||||
WorldUndoManager::UndoObjectID undoID;
|
||||
};
|
||||
|
||||
if(undoAfterEditDone)
|
||||
drawP.world.undo.push(std::make_unique<EditCanvasComponentWorldUndoAction>(std::move(oldData), drawP.world.undo.get_undoid_from_netid(objInfoBeingEdited->obj.get_net_id())));
|
||||
}
|
||||
|
||||
oldData = nullptr;
|
||||
objInfoBeingEdited = nullptr;
|
||||
|
|
|
|||
|
|
@ -23,9 +23,11 @@
|
|||
#include <include/core/SkBlendMode.h>
|
||||
#include <include/core/SkPathBuilder.h>
|
||||
#include "../../CanvasComponents/BrushComponentCode.hpp"
|
||||
#include "../EditCanvasComponentWorldUndoAction.hpp"
|
||||
|
||||
#include "../../GUIStuff/ElementHelpers/TextLabelHelpers.hpp"
|
||||
#include "../../GUIStuff/ElementHelpers/RadioButtonHelpers.hpp"
|
||||
#include "../../GUIStuff/ElementHelpers/CheckBoxHelpers.hpp"
|
||||
|
||||
EraserTool::EraserTool(DrawingProgram& initDrawP):
|
||||
DrawingProgramToolBase(initDrawP)
|
||||
|
|
@ -48,6 +50,7 @@ void EraserTool::gui_toolbox(Toolbar& t) {
|
|||
{"Layer being edited", DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED},
|
||||
{"All visible layers", DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS}
|
||||
});
|
||||
checkbox_boolean_field(gui, "erase details", "Erase details", &drawP.world.main.toolConfig.eraser.eraseDetail);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +67,7 @@ void EraserTool::gui_phone_toolbox(PhoneDrawingProgramScreen& t) {
|
|||
{"Layer being edited", DrawingProgramLayerManager::LayerSelector::LAYER_BEING_EDITED},
|
||||
{"All visible layers", DrawingProgramLayerManager::LayerSelector::ALL_VISIBLE_LAYERS}
|
||||
});
|
||||
checkbox_boolean_field(gui, "erase details", "Erase details", &drawP.world.main.toolConfig.eraser.eraseDetail);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -98,6 +102,11 @@ void EraserTool::erase_between_points() {
|
|||
drawP.drawCache.traverse_bvh_run_function_starting_at_node_no_collision_check(bvhNode, [&](const auto& bvhNodeChild) {
|
||||
drawP.drawCache.node_loop_erase_if_components(bvhNodeChild, [&](auto c) {
|
||||
if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector)) {
|
||||
auto it = updatedComponents.find(c);
|
||||
if(it != updatedComponents.end()) {
|
||||
c->obj->get_comp().set_data_from(*(it->second));
|
||||
updatedComponents.erase(it);
|
||||
}
|
||||
erasedComponents.emplace(c);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -108,10 +117,37 @@ void EraserTool::erase_between_points() {
|
|||
return false;
|
||||
}
|
||||
drawP.drawCache.node_loop_erase_if_components(bvhNode, [&](auto c) {
|
||||
if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, erasePath)) {
|
||||
erasedComponents.emplace(c);
|
||||
drawP.drawCache.invalidate_cache_at_optional_aabb(c->obj->get_world_bounds());
|
||||
return true;
|
||||
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(drawP.world.drawData.cam.c, erasePath);
|
||||
switch(result) {
|
||||
case CanvasComponentEraseDetailResult::NO_CHANGE:
|
||||
return false;
|
||||
case CanvasComponentEraseDetailResult::CHANGED: {
|
||||
if(!updatedComponents.contains(c))
|
||||
updatedComponents.emplace(c, std::move(dataCopy));
|
||||
c->obj->commit_update(drawP);
|
||||
return false;
|
||||
}
|
||||
case CanvasComponentEraseDetailResult::REMOVED:
|
||||
auto it = updatedComponents.find(c);
|
||||
if(it != updatedComponents.end()) {
|
||||
c->obj->get_comp().set_data_from(*(it->second));
|
||||
updatedComponents.erase(it);
|
||||
}
|
||||
erasedComponents.emplace(c);
|
||||
drawP.drawCache.invalidate_cache_at_optional_aabb(c->obj->get_world_bounds());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(drawP.layerMan.component_passes_layer_selector(c, drawP.controls.layerSelector) && c->obj->collides_with(drawP.world.drawData.cam.c, erasePath)) {
|
||||
erasedComponents.emplace(c);
|
||||
drawP.drawCache.invalidate_cache_at_optional_aabb(c->obj->get_world_bounds());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
|
@ -121,10 +157,23 @@ void EraserTool::erase_between_points() {
|
|||
|
||||
void EraserTool::erase_component(CanvasComponentContainer::ObjInfo* erasedComp) {
|
||||
erasedComponents.erase(erasedComp);
|
||||
updatedComponents.erase(erasedComp);
|
||||
}
|
||||
|
||||
void EraserTool::switch_tool(DrawingProgramToolType newTool) {
|
||||
drawP.layerMan.erase_component_container(erasedComponents);
|
||||
bool first = true;
|
||||
for(auto& [comp, oldData] : updatedComponents) {
|
||||
comp->obj->send_comp_update(drawP, true);
|
||||
if(erasedComponents.empty() && first) {
|
||||
first = false;
|
||||
drawP.world.undo.push(std::make_unique<EditCanvasComponentWorldUndoAction>(std::move(oldData), drawP.world.undo.get_undoid_from_netid(comp->obj.get_net_id())));
|
||||
}
|
||||
else
|
||||
drawP.world.undo.push_on_last(std::make_unique<EditCanvasComponentWorldUndoAction>(std::move(oldData), drawP.world.undo.get_undoid_from_netid(comp->obj.get_net_id())));
|
||||
}
|
||||
erasedComponents.clear();
|
||||
updatedComponents.clear();
|
||||
isErasing = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class EraserTool : public DrawingProgramToolBase {
|
|||
virtual void input_mouse_button_on_canvas_callback(const InputManager::MouseButtonCallbackArgs& button) override;
|
||||
virtual void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) override;
|
||||
|
||||
std::unordered_map<CanvasComponentContainer::ObjInfo*, std::unique_ptr<CanvasComponent>> updatedComponents;
|
||||
std::unordered_set<CanvasComponentContainer::ObjInfo*> erasedComponents; // Pointers will be erased from this set if theyre erased in the main list (done by callback)
|
||||
private:
|
||||
SkPath erasePath;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue