2025-07-06 20:43:53 +03:00
# include "EraserTool.hpp"
2025-12-26 10:40:58 +03:00
# include "../DrawingProgram.hpp"
# include "../../MainProgram.hpp"
# include "../../DrawData.hpp"
2026-03-20 16:24:52 +03:00
# include <include/core/SkBlendMode.h>
2025-11-18 18:15:50 +03:00
# include <include/core/SkPathBuilder.h>
2025-07-06 20:43:53 +03:00
EraserTool : : EraserTool ( DrawingProgram & initDrawP ) :
2025-08-02 20:10:33 +03:00
DrawingProgramToolBase ( initDrawP )
{ }
DrawingProgramToolType EraserTool : : get_type ( ) {
return DrawingProgramToolType : : ERASER ;
2025-07-06 20:43:53 +03:00
}
void EraserTool : : gui_toolbox ( ) {
Toolbar & t = drawP . world . main . toolbar ;
t . gui . push_id ( " eraser tool " ) ;
t . gui . text_label_centered ( " Eraser " ) ;
2026-01-29 20:33:22 +03:00
drawP . world . main . toolConfig . relative_width_gui ( drawP , " Size " ) ;
2025-12-28 17:34:33 +03:00
t . gui . text_label ( " Erase from: " ) ;
if ( t . gui . radio_button_field ( " layer edited " , " Layer being edited " , drawP . controls . layerSelector = = DrawingProgramLayerManager : : LayerSelector : : LAYER_BEING_EDITED ) )
drawP . controls . layerSelector = DrawingProgramLayerManager : : LayerSelector : : LAYER_BEING_EDITED ;
if ( t . gui . radio_button_field ( " all visible " , " All visible layers " , drawP . controls . layerSelector = = DrawingProgramLayerManager : : LayerSelector : : ALL_VISIBLE_LAYERS ) )
drawP . controls . layerSelector = DrawingProgramLayerManager : : LayerSelector : : ALL_VISIBLE_LAYERS ;
2025-07-06 20:43:53 +03:00
t . gui . pop_id ( ) ;
}
2025-12-01 17:33:14 +03:00
bool EraserTool : : right_click_popup_gui ( Vector2f popupPos ) {
Toolbar & t = drawP . world . main . toolbar ;
t . paint_popup ( popupPos ) ;
return true ;
}
2026-03-03 12:06:48 +03:00
void EraserTool : : input_mouse_button_on_canvas_callback ( const InputManager : : MouseButtonCallbackArgs & button ) {
if ( button . button = = InputManager : : MouseButton : : LEFT ) {
if ( button . down & & ! isErasing ) {
isErasing = true ;
erase_between_points ( button . pos , button . pos ) ;
}
else if ( ! button . down & & isErasing )
switch_tool ( get_type ( ) ) ;
}
}
void EraserTool : : input_mouse_motion_callback ( const InputManager : : MouseMotionCallbackArgs & motion ) {
if ( isErasing )
erase_between_points ( motion . pos , motion . pos - motion . move ) ;
}
void EraserTool : : erase_between_points ( const Vector2f & start , const Vector2f & end ) {
auto relativeWidthResult = drawP . world . main . toolConfig . get_relative_width_stroke_size ( drawP , drawP . world . drawData . cam . c . inverseScale ) ;
if ( ! relativeWidthResult . first . has_value ( ) ) {
drawP . world . main . toolConfig . print_relative_width_fail_message ( relativeWidthResult . second ) ;
isErasing = false ;
return ;
}
float width = relativeWidthResult . first . value ( ) * 0.5f ;
SCollision : : ColliderCollection < float > cC ;
SCollision : : generate_wide_line ( cC , start , end , width * 2.0f , true ) ;
auto cCWorld = drawP . world . drawData . cam . c . collider_to_world < SCollision : : ColliderCollection < WorldScalar > , SCollision : : ColliderCollection < float > > ( cC ) ;
drawP . drawCache . traverse_bvh_run_function ( cCWorld . bounds , [ & ] ( const auto & bvhNode ) {
if ( bvhNode & &
SCollision : : collide ( cC , drawP . world . drawData . cam . c . to_space ( bvhNode - > bounds . min ) ) & &
SCollision : : collide ( cC , drawP . world . drawData . cam . c . to_space ( bvhNode - > bounds . max ) ) & &
SCollision : : collide ( cC , drawP . world . drawData . cam . c . to_space ( bvhNode - > bounds . top_right ( ) ) ) & &
SCollision : : collide ( cC , drawP . world . drawData . cam . c . to_space ( bvhNode - > bounds . bottom_left ( ) ) ) ) {
drawP . drawCache . invalidate_cache_at_aabb ( bvhNode - > bounds ) ;
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 ) ) {
erasedComponents . emplace ( c ) ;
return true ;
}
return false ;
} ) ;
return true ;
} ) ;
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 , cCWorld , cC ) ) {
erasedComponents . emplace ( c ) ;
drawP . drawCache . invalidate_cache_at_aabb ( c - > obj - > get_world_bounds ( ) ) ;
return true ;
}
return false ;
} ) ;
return true ;
} ) ;
}
2025-12-30 19:46:08 +03:00
void EraserTool : : erase_component ( CanvasComponentContainer : : ObjInfo * erasedComp ) {
2025-12-15 15:36:42 +03:00
erasedComponents . erase ( erasedComp ) ;
}
2025-08-02 20:39:21 +03:00
void EraserTool : : switch_tool ( DrawingProgramToolType newTool ) {
2025-12-31 10:06:26 +03:00
drawP . layerMan . erase_component_container ( erasedComponents ) ;
2026-03-03 12:06:48 +03:00
isErasing = false ;
2025-07-23 16:04:16 +03:00
}
void EraserTool : : tool_update ( ) {
if ( drawP . controls . cursorHoveringOverCanvas )
drawP . world . main . input . hideCursor = true ;
}
2025-07-27 16:53:22 +03:00
bool EraserTool : : prevent_undo_or_redo ( ) {
return drawP . controls . leftClickHeld ;
}
2025-07-06 20:43:53 +03:00
void EraserTool : : draw ( SkCanvas * canvas , const DrawData & drawData ) {
2026-03-05 12:36:34 +03:00
if ( ! drawP . world . main . input . isTouchDevice & & drawData . main - > world - > drawProg . controls . cursorHoveringOverCanvas ) {
2026-03-03 12:06:48 +03:00
if ( ! lastPosOpt . has_value ( ) )
lastPosOpt = drawData . main - > input . mouse . pos ;
auto & lastPos = lastPosOpt . value ( ) ;
2026-01-29 20:33:22 +03:00
auto relativeWidthResult = drawP . world . main . toolConfig . get_relative_width_stroke_size ( drawP , drawP . world . drawData . cam . c . inverseScale ) ;
if ( relativeWidthResult . first . has_value ( ) ) {
float width = relativeWidthResult . first . value ( ) * 0.5f ;
SkPaint linePaint ;
2026-03-20 20:53:59 +03:00
linePaint . setAntiAlias ( drawData . skiaAA ) ;
2026-03-20 16:24:52 +03:00
linePaint . setColor4f ( { 1.0f , 1.0f , 1.0f , 0.5f } ) ;
2026-01-29 20:33:22 +03:00
linePaint . setStyle ( SkPaint : : kStroke_Style ) ;
linePaint . setStrokeCap ( SkPaint : : kRound_Cap ) ;
linePaint . setStrokeWidth ( width * 2.0f ) ;
2026-03-20 16:24:52 +03:00
linePaint . setBlender ( CanvasTheme : : get_visible_blend_mode ( ) ) ;
2026-01-29 20:33:22 +03:00
SkPathBuilder erasePath ;
2026-03-03 12:06:48 +03:00
erasePath . moveTo ( convert_vec2 < SkPoint > ( lastPos ) ) ;
2026-01-29 20:33:22 +03:00
erasePath . lineTo ( convert_vec2 < SkPoint > ( drawData . main - > input . mouse . pos ) ) ;
canvas - > drawPath ( erasePath . detach ( ) , linePaint ) ;
}
2026-03-03 12:06:48 +03:00
lastPos = drawData . main - > input . mouse . pos ;
2025-07-06 20:43:53 +03:00
}
2026-03-03 12:06:48 +03:00
else
lastPosOpt = std : : nullopt ;
2025-07-06 20:43:53 +03:00
}