Generalise fillet tool
Describe the actions of the fillet tools is a generic way, so that the same general pattern can be used for other tools that modify shapes on the BOARD. Basically, an "ITEM_MODIFICATION_ROUTINE" is defined, which is configured and called multiple times, calling back to the EDIT_TOOL when it modifies or creates an item. The motivation here is to make it easier to slot in new line-based tools like chamfer, extend and so on without having to redo the complicated item, selection and commit handling each time, and keep the core "routines" simple and decoupled from the EDIT_TOOL's internals. This also resolves #15094 because the new commit handling does the right thing when items were "conjured up" for the fillet (e.g. when a rectangle is decomposed into lines). Fixes: #15094
This commit is contained in:
parent
3030c80de7
commit
8e0e9ce752
|
@ -341,6 +341,7 @@ set( PCBNEW_CLASS_SRCS
|
|||
tools/global_edit_tool.cpp
|
||||
tools/group_tool.cpp
|
||||
tools/footprint_editor_control.cpp
|
||||
tools/item_modification_routine.cpp
|
||||
tools/pad_tool.cpp
|
||||
tools/pcb_control.cpp
|
||||
tools/pcb_picker_tool.cpp
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include <tools/pcb_actions.h>
|
||||
#include <tools/pcb_selection_tool.h>
|
||||
#include <tools/edit_tool.h>
|
||||
#include <tools/item_modification_routine.h>
|
||||
#include <tools/pcb_picker_tool.h>
|
||||
#include <tools/tool_event_utils.h>
|
||||
#include <tools/pcb_grid_helper.h>
|
||||
|
@ -1016,12 +1017,40 @@ int EDIT_TOOL::FilletTracks( const TOOL_EVENT& aEvent )
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int EDIT_TOOL::FilletLines( const TOOL_EVENT& aEvent )
|
||||
/**
|
||||
* Prompt the user for the fillet radius and return it.
|
||||
*
|
||||
* @param aFrame
|
||||
* @param aErrorMsg filled with an error message if the parameter is invalid somehow
|
||||
* @return std::optional<int> the fillet radius or std::nullopt if no
|
||||
* valid fillet specified
|
||||
*/
|
||||
static std::optional<int> GetFilletParams( PCB_BASE_EDIT_FRAME& aFrame, wxString& aErrorMsg )
|
||||
{
|
||||
// Store last used fillet radius to allow pressing "enter" if repeat fillet is required
|
||||
static long long filletRadiusIU = 0;
|
||||
|
||||
WX_UNIT_ENTRY_DIALOG dia( &aFrame, _( "Enter fillet radius:" ), _( "Fillet Lines" ),
|
||||
filletRadiusIU );
|
||||
|
||||
if( dia.ShowModal() == wxID_CANCEL )
|
||||
return std::nullopt;
|
||||
|
||||
filletRadiusIU = dia.GetValue();
|
||||
|
||||
if( filletRadiusIU == 0 )
|
||||
{
|
||||
aErrorMsg = _( "A radius of zero was entered.\n"
|
||||
"The fillet operation was not performed." );
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return filletRadiusIU;
|
||||
}
|
||||
|
||||
|
||||
int EDIT_TOOL::FilletLines( const TOOL_EVENT& aEvent )
|
||||
{
|
||||
PCB_SELECTION& selection = m_selectionTool->RequestSelection(
|
||||
[]( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
|
||||
{
|
||||
|
@ -1108,168 +1137,113 @@ int EDIT_TOOL::FilletLines( const TOOL_EVENT& aEvent )
|
|||
return 0;
|
||||
}
|
||||
|
||||
WX_UNIT_ENTRY_DIALOG dia( frame(), _( "Enter fillet radius:" ), _( "Fillet Lines" ),
|
||||
filletRadiusIU );
|
||||
BOARD_COMMIT commit{ this };
|
||||
|
||||
if( dia.ShowModal() == wxID_CANCEL )
|
||||
return 0;
|
||||
// List of thing to select at the end of the operation
|
||||
// (doing it as we go will invalidate the iterator)
|
||||
std::vector<PCB_SHAPE*> items_to_select_on_success;
|
||||
|
||||
filletRadiusIU = dia.GetValue();
|
||||
|
||||
if( filletRadiusIU == 0 )
|
||||
// Handle modifications to existing items by the routine
|
||||
// How to deal with this depends on whether we're in the footprint editor or not
|
||||
// and whether the item was conjured up by decomposing a polygon or rectangle
|
||||
const auto item_modification_handler = [&]( PCB_SHAPE& aItem )
|
||||
{
|
||||
frame()->ShowInfoBarMsg( _( "A radius of zero was entered.\n"
|
||||
"The fillet operation was not performed." ) );
|
||||
return 0;
|
||||
if( !m_isFootprintEditor )
|
||||
{
|
||||
// If the item was "conjured up" it will be added later separately
|
||||
if( std::find( lines_to_add.begin(), lines_to_add.end(), &aItem )
|
||||
== lines_to_add.end() )
|
||||
{
|
||||
commit.Modify( &aItem );
|
||||
items_to_select_on_success.push_back( &aItem );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool any_items_created = false;
|
||||
const auto item_creation_handler = [&]( std::unique_ptr<PCB_SHAPE> aItem )
|
||||
{
|
||||
any_items_created = true;
|
||||
items_to_select_on_success.push_back( aItem.get() );
|
||||
commit.Add( aItem.release() );
|
||||
};
|
||||
|
||||
// Construct an appropriate tool
|
||||
std::unique_ptr<PAIRWISE_LINE_ROUTINE> pairwise_line_routine;
|
||||
wxString error_message;
|
||||
|
||||
if( aEvent.IsAction( &PCB_ACTIONS::filletLines ) )
|
||||
{
|
||||
const std::optional<int> filletRadiusIU = GetFilletParams( *frame(), error_message );
|
||||
|
||||
if( filletRadiusIU.has_value() )
|
||||
{
|
||||
pairwise_line_routine = std::make_unique<LINE_FILLET_ROUTINE>(
|
||||
frame()->GetModel(), item_creation_handler, item_modification_handler,
|
||||
*filletRadiusIU );
|
||||
}
|
||||
}
|
||||
|
||||
BOARD_COMMIT commit( this );
|
||||
bool operationPerformedOnAtLeastOne = false;
|
||||
bool didOneAttemptFail = false;
|
||||
std::vector<BOARD_ITEM*> itemsToAddToSelection;
|
||||
if( !pairwise_line_routine )
|
||||
{
|
||||
// Didn't construct any mofication routine - could be an error or cancellation
|
||||
if( !error_message.empty() )
|
||||
{
|
||||
frame()->ShowInfoBarMsg( error_message );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Only modify one parent in FP editor
|
||||
if( m_isFootprintEditor )
|
||||
commit.Modify( selection.Front() );
|
||||
|
||||
alg::for_all_pairs( selection.begin(), selection.end(), [&]( EDA_ITEM* a, EDA_ITEM* b )
|
||||
// Apply the tool to every line pair
|
||||
alg::for_all_pairs( selection.begin(), selection.end(),
|
||||
[&]( EDA_ITEM* a, EDA_ITEM* b )
|
||||
{
|
||||
PCB_SHAPE* line_a = static_cast<PCB_SHAPE*>( a );
|
||||
PCB_SHAPE* line_b = static_cast<PCB_SHAPE*>( b );
|
||||
|
||||
if( line_a->GetLength() == 0.0 || line_b->GetLength() == 0 )
|
||||
return;
|
||||
|
||||
SEG seg_a( line_a->GetStart(), line_a->GetEnd() );
|
||||
SEG seg_b( line_b->GetStart(), line_b->GetEnd() );
|
||||
VECTOR2I* a_pt;
|
||||
VECTOR2I* b_pt;
|
||||
|
||||
if (seg_a.A == seg_b.A)
|
||||
{
|
||||
a_pt = &seg_a.A;
|
||||
b_pt = &seg_b.A;
|
||||
}
|
||||
else if (seg_a.A == seg_b.B)
|
||||
{
|
||||
a_pt = &seg_a.A;
|
||||
b_pt = &seg_b.B;
|
||||
}
|
||||
else if (seg_a.B == seg_b.A)
|
||||
{
|
||||
a_pt = &seg_a.B;
|
||||
b_pt = &seg_b.A;
|
||||
}
|
||||
else if (seg_a.B == seg_b.B)
|
||||
{
|
||||
a_pt = &seg_a.B;
|
||||
b_pt = &seg_b.B;
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
|
||||
SHAPE_ARC sArc( seg_a, seg_b, filletRadiusIU );
|
||||
VECTOR2I t1newPoint, t2newPoint;
|
||||
|
||||
auto setIfPointOnSeg =
|
||||
[]( VECTOR2I& aPointToSet, SEG aSegment, VECTOR2I aVecToTest )
|
||||
{
|
||||
VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
|
||||
|
||||
// Find out if we are on the segment (minimum precision)
|
||||
if( segToVec.EuclideanNorm() < SHAPE_ARC::MIN_PRECISION_IU )
|
||||
{
|
||||
aPointToSet.x = aVecToTest.x;
|
||||
aPointToSet.y = aVecToTest.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
//Do not draw a fillet if the end points of the arc are not within the track segments
|
||||
if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.GetP0() )
|
||||
&& !setIfPointOnSeg( t2newPoint, seg_b, sArc.GetP0() ) )
|
||||
{
|
||||
didOneAttemptFail = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.GetP1() )
|
||||
&& !setIfPointOnSeg( t2newPoint, seg_b, sArc.GetP1() ) )
|
||||
{
|
||||
didOneAttemptFail = true;
|
||||
return;
|
||||
}
|
||||
|
||||
PCB_SHAPE* tArc = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::ARC );
|
||||
|
||||
tArc->SetArcGeometry( sArc.GetP0(), sArc.GetArcMid(), sArc.GetP1() );
|
||||
tArc->SetWidth( line_a->GetWidth() );
|
||||
tArc->SetLayer( line_a->GetLayer() );
|
||||
tArc->SetLocked( line_a->IsLocked() );
|
||||
|
||||
if( lines_to_add.count( line_a ) )
|
||||
{
|
||||
lines_to_add.erase( line_a );
|
||||
itemsToAddToSelection.push_back( line_a );
|
||||
}
|
||||
else if( !m_isFootprintEditor )
|
||||
{
|
||||
commit.Modify( line_a );
|
||||
}
|
||||
|
||||
if( lines_to_add.count( line_b ) )
|
||||
{
|
||||
lines_to_add.erase( line_b );
|
||||
itemsToAddToSelection.push_back( line_b );
|
||||
}
|
||||
else if( !m_isFootprintEditor )
|
||||
{
|
||||
commit.Modify( line_b );
|
||||
}
|
||||
|
||||
itemsToAddToSelection.push_back( tArc );
|
||||
*a_pt = t1newPoint;
|
||||
*b_pt = t2newPoint;
|
||||
line_a->SetStart( seg_a.A );
|
||||
line_a->SetEnd( seg_a.B );
|
||||
line_b->SetStart( seg_b.A );
|
||||
line_b->SetEnd( seg_b.B );
|
||||
|
||||
operationPerformedOnAtLeastOne = true;
|
||||
|
||||
pairwise_line_routine->ProcessLinePair( *line_a, *line_b );
|
||||
} );
|
||||
|
||||
// Items created like lines from a rectangle
|
||||
// Some of these might have been changed by the tool, but we need to
|
||||
// add *all* of them to the selection and commit them
|
||||
for( PCB_SHAPE* item : lines_to_add )
|
||||
{
|
||||
m_selectionTool->AddItemToSel( item, true );
|
||||
commit.Add( item );
|
||||
}
|
||||
|
||||
// Remove items like rectangles that we decomposed into lines
|
||||
for( PCB_SHAPE* item : items_to_remove )
|
||||
{
|
||||
commit.Remove( item );
|
||||
m_selectionTool->RemoveItemFromSel( item, true );
|
||||
}
|
||||
|
||||
//select the newly created arcs
|
||||
for( BOARD_ITEM* item : itemsToAddToSelection )
|
||||
{
|
||||
commit.Add( item );
|
||||
// Select added and modified items
|
||||
for( PCB_SHAPE* item : items_to_select_on_success )
|
||||
m_selectionTool->AddItemToSel( item, true );
|
||||
}
|
||||
|
||||
if( !items_to_remove.empty() )
|
||||
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
|
||||
|
||||
if( !itemsToAddToSelection.empty() )
|
||||
if( !any_items_created )
|
||||
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
||||
|
||||
// Notify other tools of the changes
|
||||
m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
|
||||
|
||||
commit.Push( _( "Fillet Lines" ) );
|
||||
commit.Push( pairwise_line_routine->GetCommitDescription() );
|
||||
|
||||
if( !operationPerformedOnAtLeastOne )
|
||||
frame()->ShowInfoBarMsg( _( "Unable to fillet the selected lines." ) );
|
||||
else if( didOneAttemptFail )
|
||||
frame()->ShowInfoBarMsg( _( "Some of the lines could not be filleted." ) );
|
||||
if( pairwise_line_routine->GetSuccesses() == 0 )
|
||||
frame()->ShowInfoBarMsg( pairwise_line_routine->GetCompleteFailureMessage() );
|
||||
else if( pairwise_line_routine->GetFailures() > 0 )
|
||||
frame()->ShowInfoBarMsg( pairwise_line_routine->GetSomeFailuresMessage() );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* 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 2
|
||||
* 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, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "item_modification_routine.h"
|
||||
|
||||
|
||||
wxString LINE_FILLET_ROUTINE::GetCommitDescription() const
|
||||
{
|
||||
return _( "Fillet Lines" );
|
||||
}
|
||||
|
||||
wxString LINE_FILLET_ROUTINE::GetCompleteFailureMessage() const
|
||||
{
|
||||
return _( "Unable to fillet the selected lines." );
|
||||
}
|
||||
|
||||
wxString LINE_FILLET_ROUTINE::GetSomeFailuresMessage() const
|
||||
{
|
||||
return _( "Some of the lines could not be filleted." );
|
||||
}
|
||||
|
||||
void LINE_FILLET_ROUTINE::ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB )
|
||||
{
|
||||
if( aLineA.GetLength() == 0.0 || aLineB.GetLength() == 0.0 )
|
||||
return;
|
||||
|
||||
SEG seg_a( aLineA.GetStart(), aLineA.GetEnd() );
|
||||
SEG seg_b( aLineB.GetStart(), aLineB.GetEnd() );
|
||||
VECTOR2I* a_pt;
|
||||
VECTOR2I* b_pt;
|
||||
|
||||
if( seg_a.A == seg_b.A )
|
||||
{
|
||||
a_pt = &seg_a.A;
|
||||
b_pt = &seg_b.A;
|
||||
}
|
||||
else if( seg_a.A == seg_b.B )
|
||||
{
|
||||
a_pt = &seg_a.A;
|
||||
b_pt = &seg_b.B;
|
||||
}
|
||||
else if( seg_a.B == seg_b.A )
|
||||
{
|
||||
a_pt = &seg_a.B;
|
||||
b_pt = &seg_b.A;
|
||||
}
|
||||
else if( seg_a.B == seg_b.B )
|
||||
{
|
||||
a_pt = &seg_a.B;
|
||||
b_pt = &seg_b.B;
|
||||
}
|
||||
else
|
||||
// Nothing to do
|
||||
return;
|
||||
|
||||
|
||||
SHAPE_ARC sArc( seg_a, seg_b, m_filletRadiusIU );
|
||||
VECTOR2I t1newPoint, t2newPoint;
|
||||
|
||||
auto setIfPointOnSeg = []( VECTOR2I& aPointToSet, SEG aSegment, VECTOR2I aVecToTest )
|
||||
{
|
||||
VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
|
||||
|
||||
// Find out if we are on the segment (minimum precision)
|
||||
if( segToVec.EuclideanNorm() < SHAPE_ARC::MIN_PRECISION_IU )
|
||||
{
|
||||
aPointToSet.x = aVecToTest.x;
|
||||
aPointToSet.y = aVecToTest.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
//Do not draw a fillet if the end points of the arc are not within the track segments
|
||||
if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.GetP0() )
|
||||
&& !setIfPointOnSeg( t2newPoint, seg_b, sArc.GetP0() ) )
|
||||
{
|
||||
AddFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.GetP1() )
|
||||
&& !setIfPointOnSeg( t2newPoint, seg_b, sArc.GetP1() ) )
|
||||
{
|
||||
AddFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
auto tArc = std::make_unique<PCB_SHAPE>( GetBoard(), SHAPE_T::ARC );
|
||||
|
||||
tArc->SetArcGeometry( sArc.GetP0(), sArc.GetArcMid(), sArc.GetP1() );
|
||||
|
||||
// Copy properties from one of the source lines
|
||||
tArc->SetWidth( aLineA.GetWidth() );
|
||||
tArc->SetLayer( aLineA.GetLayer() );
|
||||
tArc->SetLocked( aLineA.IsLocked() );
|
||||
|
||||
MarkItemModified( aLineA );
|
||||
MarkItemModified( aLineB );
|
||||
|
||||
AddNewItem( std::move( tArc ) );
|
||||
|
||||
*a_pt = t1newPoint;
|
||||
*b_pt = t2newPoint;
|
||||
aLineA.SetStart( seg_a.A );
|
||||
aLineA.SetEnd( seg_a.B );
|
||||
aLineB.SetStart( seg_b.A );
|
||||
aLineB.SetEnd( seg_b.B );
|
||||
|
||||
AddSuccess();
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* 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 2
|
||||
* 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, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef ITEM_MODIFICATION_ROUTINE_H_
|
||||
#define ITEM_MODIFICATION_ROUTINE_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <board_item.h>
|
||||
#include <pcb_shape.h>
|
||||
|
||||
/**
|
||||
* @brief An object that has the ability to modify items on a board
|
||||
*
|
||||
* For example, such an object could take pairs of lines and fillet them,
|
||||
* or produce other modification to items.
|
||||
*
|
||||
* Deliberately not called a "tool" to distinguish it from true
|
||||
* tools that are used interactively by the user.
|
||||
*/
|
||||
class ITEM_MODIFICATION_ROUTINE
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* Handlers for receiving changes from the tool
|
||||
*
|
||||
* These is used to allow the tool's caller to make changes to
|
||||
* affected board items using extra information that the tool
|
||||
* does not have access to (e.g. is this an FP editor, was
|
||||
* the line created from a rectangle and needs to be added, not
|
||||
* modified, etc).
|
||||
*
|
||||
* We can't store them up until the end, because modifications
|
||||
* need the old state to be known.
|
||||
*
|
||||
* @param PCB_SHAPE& the line to modify
|
||||
* @param bool true if the shape was created, false if it was only modified
|
||||
*/
|
||||
using CREATION_HANDLER = std::function<void( std::unique_ptr<PCB_SHAPE> )>;
|
||||
using MODIFICATION_HANDLER = std::function<void( PCB_SHAPE& )>;
|
||||
|
||||
ITEM_MODIFICATION_ROUTINE( BOARD_ITEM* aBoard, CREATION_HANDLER aCreationHandler,
|
||||
MODIFICATION_HANDLER aModificationHandler ) :
|
||||
m_board( aBoard ),
|
||||
m_creationHandler( aCreationHandler ), m_modificationHandler( aModificationHandler ),
|
||||
m_numSuccesses( 0 ), m_numFailures( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
unsigned GetSuccesses() const { return m_numSuccesses; }
|
||||
|
||||
unsigned GetFailures() const { return m_numFailures; }
|
||||
|
||||
virtual wxString GetCommitDescription() const = 0;
|
||||
|
||||
virtual wxString GetCompleteFailureMessage() const = 0;
|
||||
virtual wxString GetSomeFailuresMessage() const = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The BOARD used when creating new shapes
|
||||
*/
|
||||
BOARD_ITEM* GetBoard() const { return m_board; }
|
||||
|
||||
/**
|
||||
* Mark that one of the actions succeeded.
|
||||
*/
|
||||
void AddSuccess() { ++m_numSuccesses; }
|
||||
|
||||
/**
|
||||
* Mark that one of the actions failed.
|
||||
*/
|
||||
void AddFailure() { ++m_numFailures; }
|
||||
|
||||
/**
|
||||
* @brief Report that the tools wants to add a new item to the board
|
||||
*
|
||||
* @param aItem the new item
|
||||
*/
|
||||
void AddNewItem( std::unique_ptr<PCB_SHAPE> aItem ) { m_creationHandler( std::move( aItem ) ); }
|
||||
|
||||
/**
|
||||
* @brief Report that the tool has modified an item on the board
|
||||
*
|
||||
* @param aItem the modified item
|
||||
*/
|
||||
void MarkItemModified( PCB_SHAPE& aItem ) { m_modificationHandler( aItem ); }
|
||||
|
||||
private:
|
||||
BOARD_ITEM* m_board;
|
||||
CREATION_HANDLER m_creationHandler;
|
||||
MODIFICATION_HANDLER m_modificationHandler;
|
||||
|
||||
unsigned m_numSuccesses;
|
||||
unsigned m_numFailures;
|
||||
};
|
||||
|
||||
/**
|
||||
* A tool that acts on a pair of lines. For example, fillets, chamfers, extensions, etc
|
||||
*/
|
||||
class PAIRWISE_LINE_ROUTINE : public ITEM_MODIFICATION_ROUTINE
|
||||
{
|
||||
public:
|
||||
PAIRWISE_LINE_ROUTINE( BOARD_ITEM* aBoard, CREATION_HANDLER aCreationHandler,
|
||||
MODIFICATION_HANDLER aModificationHandler ) :
|
||||
ITEM_MODIFICATION_ROUTINE( aBoard, aCreationHandler, aModificationHandler )
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform the action on the pair of lines given
|
||||
*
|
||||
* The routine will be called repeatedly with all possible pairs of lines
|
||||
* in the selection. The tools should handle the ones it's interested in.
|
||||
* This means that the same line can appear multiple times with different
|
||||
* partners.
|
||||
*
|
||||
* The routine can skip lines that it's not interested in by returning without
|
||||
* adding to the success or failure count.
|
||||
*
|
||||
* @param aLineA the first line
|
||||
* @param aLineB the second line
|
||||
* @return did the action succeed
|
||||
*/
|
||||
virtual void ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB ) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pairwise line tool that adds a fillet to the lines.
|
||||
*/
|
||||
class LINE_FILLET_ROUTINE : public PAIRWISE_LINE_ROUTINE
|
||||
{
|
||||
public:
|
||||
LINE_FILLET_ROUTINE( BOARD_ITEM* aBoard, CREATION_HANDLER aCreationHandler,
|
||||
MODIFICATION_HANDLER aModificationHandler, int filletRadiusIU ) :
|
||||
PAIRWISE_LINE_ROUTINE( aBoard, aCreationHandler, aModificationHandler ),
|
||||
m_filletRadiusIU( filletRadiusIU )
|
||||
{
|
||||
}
|
||||
|
||||
wxString GetCommitDescription() const override;
|
||||
wxString GetCompleteFailureMessage() const override;
|
||||
wxString GetSomeFailuresMessage() const override;
|
||||
|
||||
void ProcessLinePair( PCB_SHAPE& aLineA, PCB_SHAPE& aLineB ) override;
|
||||
|
||||
private:
|
||||
int m_filletRadiusIU;
|
||||
};
|
||||
|
||||
#endif /* ITEM_MODIFICATION_ROUTINE_H_ */
|
Loading…
Reference in New Issue