Give up trying to infer what kind of polygon the user wants.

In many instances there are 3 valid answers: mimic linewidths, use
centerlines, and build a bounding hull.

Fixes https://gitlab.com/kicad/code/kicad/issues/12950
This commit is contained in:
Jeff Young 2022-11-28 20:46:30 +00:00
parent 77770ff313
commit 8165fc6c44
6 changed files with 219 additions and 125 deletions

View File

@ -23,6 +23,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <wx/radiobut.h>
#include <kiface_base.h>
#include <confirm.h>
#include <pcb_edit_frame.h>
@ -124,7 +125,8 @@ private:
std::vector<NETINFO_ITEM*> m_netInfoItemList;
CONVERT_SETTINGS* m_convertSettings;
wxCheckBox* m_cbIgnoreLineWidths;
wxRadioButton* m_rbCenterline;
wxRadioButton* m_rbEnvelope;
wxCheckBox* m_cbDeleteOriginals;
};
@ -184,7 +186,8 @@ DIALOG_COPPER_ZONE::DIALOG_COPPER_ZONE( PCB_BASE_FRAME* aParent, ZONE_SETTINGS*
m_islandThresholdUnits ),
m_hideAutoGeneratedNets{ false },
m_convertSettings( aConvertSettings ),
m_cbIgnoreLineWidths( nullptr ),
m_rbCenterline( nullptr ),
m_rbEnvelope( nullptr ),
m_cbDeleteOriginals( nullptr )
{
m_Parent = aParent;
@ -217,16 +220,18 @@ DIALOG_COPPER_ZONE::DIALOG_COPPER_ZONE( PCB_BASE_FRAME* aParent, ZONE_SETTINGS*
if( aConvertSettings )
{
wxStaticBox* bConvertBox = new wxStaticBox( this, wxID_ANY,
_( "Conversion Settings" ) );
wxStaticBox* bConvertBox = new wxStaticBox( this, wxID_ANY, _( "Conversion Settings" ) );
wxStaticBoxSizer* bConvertSizer = new wxStaticBoxSizer( bConvertBox, wxVERTICAL );
m_cbIgnoreLineWidths = new wxCheckBox( this, wxID_ANY,
_( "Ignore source object line widths" ) );
bConvertSizer->Add( m_cbIgnoreLineWidths, 0, wxLEFT|wxRIGHT, 5 );
m_rbCenterline = new wxRadioButton( this, wxID_ANY, _( "Use centerlines" ) );
bConvertSizer->Add( m_rbCenterline, 0, wxLEFT|wxRIGHT, 5 );
m_cbDeleteOriginals = new wxCheckBox( this, wxID_ANY,
_( "Delete source objects after conversion" ) );
bConvertSizer->AddSpacer( 2 );
m_rbEnvelope = new wxRadioButton( this, wxID_ANY, _( "Create bounding hull" ) );
bConvertSizer->Add( m_rbEnvelope, 0, wxLEFT|wxRIGHT, 5 );
bConvertSizer->AddSpacer( 6 );
m_cbDeleteOriginals = new wxCheckBox( this, wxID_ANY, _( "Delete source objects after conversion" ) );
bConvertSizer->Add( m_cbDeleteOriginals, 0, wxALL, 5 );
GetSizer()->Insert( 0, bConvertSizer, 0, wxALL|wxEXPAND, 10 );
@ -263,7 +268,11 @@ bool DIALOG_COPPER_ZONE::TransferDataToWindow()
{
if( m_convertSettings )
{
m_cbIgnoreLineWidths->SetValue( m_convertSettings->m_IgnoreLineWidths );
if( m_convertSettings->m_Strategy == BOUNDING_HULL )
m_rbEnvelope->SetValue( true );
else
m_rbCenterline->SetValue( true );
m_cbDeleteOriginals->SetValue( m_convertSettings->m_DeleteOriginals );
}
@ -479,7 +488,11 @@ bool DIALOG_COPPER_ZONE::TransferDataFromWindow()
if( m_convertSettings )
{
m_convertSettings->m_IgnoreLineWidths = m_cbIgnoreLineWidths->GetValue();
if( m_rbEnvelope->GetValue() )
m_convertSettings->m_Strategy = BOUNDING_HULL;
else
m_convertSettings->m_Strategy = CENTERLINE;
m_convertSettings->m_DeleteOriginals = m_cbDeleteOriginals->GetValue();
}

View File

@ -29,6 +29,7 @@
#include <pcbnew_settings.h>
#include <widgets/unit_binder.h>
#include <wx/statbox.h>
#include <wx/radiobut.h>
#include <zones.h>
#include <dialog_non_copper_zones_properties_base.h>
@ -61,7 +62,8 @@ private:
UNIT_BINDER m_cornerRadius;
CONVERT_SETTINGS* m_convertSettings;
wxCheckBox* m_cbIgnoreLineWidths;
wxRadioButton* m_rbCenterline;
wxRadioButton* m_rbEnvelope;
wxCheckBox* m_cbDeleteOriginals;
};
@ -89,7 +91,8 @@ DIALOG_NON_COPPER_ZONES_EDITOR::DIALOG_NON_COPPER_ZONES_EDITOR( PCB_BASE_FRAME*
m_cornerSmoothingType( ZONE_SETTINGS::SMOOTHING_UNDEFINED ),
m_cornerRadius( aParent, m_cornerRadiusLabel, m_cornerRadiusCtrl, m_cornerRadiusUnits ),
m_convertSettings( aConvertSettings ),
m_cbIgnoreLineWidths( nullptr ),
m_rbCenterline( nullptr ),
m_rbEnvelope( nullptr ),
m_cbDeleteOriginals( nullptr )
{
m_parent = aParent;
@ -99,16 +102,18 @@ DIALOG_NON_COPPER_ZONES_EDITOR::DIALOG_NON_COPPER_ZONES_EDITOR( PCB_BASE_FRAME*
if( aConvertSettings )
{
wxStaticBox* bConvertBox = new wxStaticBox( this, wxID_ANY,
_( "Conversion Settings" ) );
wxStaticBox* bConvertBox = new wxStaticBox( this, wxID_ANY, _( "Conversion Settings" ) );
wxStaticBoxSizer* bConvertSizer = new wxStaticBoxSizer( bConvertBox, wxVERTICAL );
m_cbIgnoreLineWidths = new wxCheckBox( this, wxID_ANY,
_( "Ignore source object line widths" ) );
bConvertSizer->Add( m_cbIgnoreLineWidths, 0, wxLEFT|wxRIGHT, 5 );
m_rbCenterline = new wxRadioButton( this, wxID_ANY, _( "Use centerlines" ) );
bConvertSizer->Add( m_rbCenterline, 0, wxLEFT|wxRIGHT, 5 );
m_cbDeleteOriginals = new wxCheckBox( this, wxID_ANY,
_( "Delete source objects after conversion" ) );
bConvertSizer->AddSpacer( 2 );
m_rbEnvelope = new wxRadioButton( this, wxID_ANY, _( "Create bounding hull" ) );
bConvertSizer->Add( m_rbEnvelope, 0, wxLEFT|wxRIGHT, 5 );
bConvertSizer->AddSpacer( 6 );
m_cbDeleteOriginals = new wxCheckBox( this, wxID_ANY, _( "Delete source objects after conversion" ) );
bConvertSizer->Add( m_cbDeleteOriginals, 0, wxALL, 5 );
GetSizer()->Insert( 0, bConvertSizer, 0, wxALL|wxEXPAND, 10 );
@ -152,7 +157,11 @@ bool DIALOG_NON_COPPER_ZONES_EDITOR::TransferDataToWindow()
{
if( m_convertSettings )
{
m_cbIgnoreLineWidths->SetValue( m_convertSettings->m_IgnoreLineWidths );
if( m_convertSettings->m_Strategy == BOUNDING_HULL )
m_rbEnvelope->SetValue( true );
else
m_rbCenterline->SetValue( true );
m_cbDeleteOriginals->SetValue( m_convertSettings->m_DeleteOriginals );
}
@ -244,7 +253,11 @@ bool DIALOG_NON_COPPER_ZONES_EDITOR::TransferDataFromWindow()
{
if( m_convertSettings )
{
m_convertSettings->m_IgnoreLineWidths = m_cbIgnoreLineWidths->GetValue();
if( m_rbEnvelope->GetValue() )
m_convertSettings->m_Strategy = BOUNDING_HULL;
else
m_convertSettings->m_Strategy = CENTERLINE;
m_convertSettings->m_DeleteOriginals = m_cbDeleteOriginals->GetValue();
}

View File

@ -31,6 +31,7 @@
#include <dialog_rule_area_properties_base.h>
#include <widgets/unit_binder.h>
#include <wx/statbox.h>
#include <wx/radiobut.h>
#define LAYER_LIST_COLUMN_CHECK 0
#define LAYER_LIST_COLUMN_ICON 1
@ -58,7 +59,8 @@ private:
bool m_isFpEditor;
CONVERT_SETTINGS* m_convertSettings;
wxCheckBox* m_cbIgnoreLineWidths;
wxRadioButton* m_rbCenterline;
wxRadioButton* m_rbEnvelope;
wxCheckBox* m_cbDeleteOriginals;
};
@ -79,7 +81,8 @@ DIALOG_RULE_AREA_PROPERTIES::DIALOG_RULE_AREA_PROPERTIES( PCB_BASE_FRAME* aParen
m_outlineHatchPitch( aParent, m_stBorderHatchPitchText,
m_outlineHatchPitchCtrl, m_outlineHatchUnits ),
m_convertSettings( aConvertSettings ),
m_cbIgnoreLineWidths( nullptr ),
m_rbCenterline( nullptr ),
m_rbEnvelope( nullptr ),
m_cbDeleteOriginals( nullptr )
{
m_parent = aParent;
@ -89,18 +92,18 @@ DIALOG_RULE_AREA_PROPERTIES::DIALOG_RULE_AREA_PROPERTIES( PCB_BASE_FRAME* aParen
if( aConvertSettings )
{
wxBoxSizer* bConvertSizer = new wxBoxSizer( wxVERTICAL );
wxStaticBox* bConvertBox = new wxStaticBox( this, wxID_ANY, _( "Conversion Settings" ) );
wxStaticBoxSizer* bConvertSizer = new wxStaticBoxSizer( bConvertBox, wxVERTICAL );
wxStaticText* conversionSettingsLabel = new wxStaticText( this, wxID_ANY,
_( "Conversion settings:" ) );
bConvertSizer->Add( conversionSettingsLabel, 0, wxLEFT|wxRIGHT|wxEXPAND, 5 );
m_rbCenterline = new wxRadioButton( this, wxID_ANY, _( "Use centerlines" ) );
bConvertSizer->Add( m_rbCenterline, 0, wxLEFT|wxRIGHT, 5 );
m_cbIgnoreLineWidths = new wxCheckBox( this, wxID_ANY,
_( "Ignore source object line widths" ) );
bConvertSizer->Add( m_cbIgnoreLineWidths, 0, wxLEFT|wxRIGHT|wxTOP, 5 );
bConvertSizer->AddSpacer( 2 );
m_rbEnvelope = new wxRadioButton( this, wxID_ANY, _( "Create bounding hull" ) );
bConvertSizer->Add( m_rbEnvelope, 0, wxLEFT|wxRIGHT, 5 );
m_cbDeleteOriginals = new wxCheckBox( this, wxID_ANY,
_( "Delete source objects after conversion" ) );
bConvertSizer->AddSpacer( 6 );
m_cbDeleteOriginals = new wxCheckBox( this, wxID_ANY, _( "Delete source objects after conversion" ) );
bConvertSizer->Add( m_cbDeleteOriginals, 0, wxALL, 5 );
GetSizer()->Insert( 0, bConvertSizer, 0, wxALL|wxEXPAND, 10 );
@ -129,7 +132,11 @@ bool DIALOG_RULE_AREA_PROPERTIES::TransferDataToWindow()
{
if( m_convertSettings )
{
m_cbIgnoreLineWidths->SetValue( m_convertSettings->m_IgnoreLineWidths );
if( m_convertSettings->m_Strategy == BOUNDING_HULL )
m_rbEnvelope->SetValue( true );
else
m_rbCenterline->SetValue( true );
m_cbDeleteOriginals->SetValue( m_convertSettings->m_DeleteOriginals );
}
@ -190,7 +197,11 @@ bool DIALOG_RULE_AREA_PROPERTIES::TransferDataFromWindow()
{
if( m_convertSettings )
{
m_convertSettings->m_IgnoreLineWidths = m_cbIgnoreLineWidths->GetValue();
if( m_rbEnvelope->GetValue() )
m_convertSettings->m_Strategy = BOUNDING_HULL;
else
m_convertSettings->m_Strategy = CENTERLINE;
m_convertSettings->m_DeleteOriginals = m_cbDeleteOriginals->GetValue();
}

View File

@ -32,11 +32,18 @@ namespace PNS
// Settings for the CONVERT_TOOL.
enum CONVERT_STRATEGY
{
COPY_LINEWIDTH,
CENTERLINE,
BOUNDING_HULL
};
struct CONVERT_SETTINGS
{
bool m_StrokeHulls;
bool m_IgnoreLineWidths;
bool m_DeleteOriginals;
CONVERT_STRATEGY m_Strategy;
bool m_DeleteOriginals;
};

View File

@ -27,6 +27,7 @@
#include <wx/statline.h>
#include <wx/checkbox.h>
#include <wx/button.h>
#include <wx/radiobut.h>
#include <board.h>
#include <board_commit.h>
#include <board_design_settings.h>
@ -58,19 +59,28 @@ public:
DIALOG_SHIM( aParent, wxID_ANY, _( "Conversion Settings" ), wxDefaultPosition,
wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
m_settings( aSettings ),
m_cbIgnoreLineWidths( nullptr ),
m_rbMimicLineWidth( nullptr ),
m_rbCenterline( nullptr ),
m_rbEnvelope( nullptr ),
m_cbDeleteOriginals( nullptr )
{
wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* topSizer = new wxBoxSizer( wxVERTICAL );
SetSizer( mainSizer );
m_cbIgnoreLineWidths = new wxCheckBox( this, wxID_ANY,
_( "Ignore source object line widths" ) );
topSizer->Add( m_cbIgnoreLineWidths, 0, wxLEFT|wxRIGHT, 5 );
m_rbMimicLineWidth = new wxRadioButton( this, wxID_ANY, _( "Copy line width of first object" ) );
topSizer->Add( m_rbMimicLineWidth, 0, wxLEFT|wxRIGHT, 5 );
m_cbDeleteOriginals = new wxCheckBox( this, wxID_ANY,
_( "Delete source objects after conversion" ) );
topSizer->AddSpacer( 2 );
m_rbCenterline = new wxRadioButton( this, wxID_ANY, _( "Use centerlines" ) );
topSizer->Add( m_rbCenterline, 0, wxLEFT|wxRIGHT, 5 );
topSizer->AddSpacer( 2 );
m_rbEnvelope = new wxRadioButton( this, wxID_ANY, _( "Create bounding hull" ) );
topSizer->Add( m_rbEnvelope, 0, wxLEFT|wxRIGHT, 5 );
topSizer->AddSpacer( 8 );
m_cbDeleteOriginals = new wxCheckBox( this, wxID_ANY, _( "Delete source objects after conversion" ) );
topSizer->Add( m_cbDeleteOriginals, 0, wxALL, 5 );
wxStaticLine* line = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
@ -103,21 +113,36 @@ public:
protected:
bool TransferDataToWindow() override
{
m_cbIgnoreLineWidths->SetValue( m_settings->m_IgnoreLineWidths );
switch( m_settings->m_Strategy )
{
case COPY_LINEWIDTH: m_rbMimicLineWidth->SetValue( true ); break;
case CENTERLINE: m_rbCenterline->SetValue( true ); break;
case BOUNDING_HULL: m_rbEnvelope->SetValue( true ); break;
}
m_cbDeleteOriginals->SetValue( m_settings->m_DeleteOriginals );
return true;
}
bool TransferDataFromWindow() override
{
m_settings->m_IgnoreLineWidths = m_cbIgnoreLineWidths->GetValue();
if( m_rbEnvelope->GetValue() )
m_settings->m_Strategy = BOUNDING_HULL;
else if( m_rbCenterline->GetValue() )
m_settings->m_Strategy = CENTERLINE;
else
m_settings->m_Strategy = COPY_LINEWIDTH;
m_settings->m_DeleteOriginals = m_cbDeleteOriginals->GetValue();
return true;
}
private:
CONVERT_SETTINGS* m_settings;
wxCheckBox* m_cbIgnoreLineWidths;
wxRadioButton* m_rbMimicLineWidth;
wxRadioButton* m_rbCenterline;
wxRadioButton* m_rbEnvelope;
wxCheckBox* m_cbDeleteOriginals;
};
@ -203,7 +228,9 @@ int CONVERT_TOOL::CreatePolys( const TOOL_EVENT& aEvent )
CONVERT_SETTINGS convertSettings;
PCB_LAYER_ID destLayer = m_frame->GetActiveLayer();
FOOTPRINT* parentFootprint = nullptr;
bool foundChainedSegs = false;
convertSettings.m_Strategy = BOUNDING_HULL; // Required for preflight at least
convertSettings.m_DeleteOriginals = true;
PCB_SELECTION& selection = m_selectionTool->RequestSelection(
[]( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
@ -222,25 +249,15 @@ int CONVERT_TOOL::CreatePolys( const TOOL_EVENT& aEvent )
item->ClearTempFlags();
SHAPE_POLY_SET polySet;
SHAPE_POLY_SET temp;
polySet.Append( makePolysFromClosedGraphics( selection.GetItems(),
convertSettings.m_IgnoreLineWidths ) );
convertSettings.m_Strategy) );
temp = makePolysFromChainedSegs( selection.GetItems() );
polySet.Append( makePolysFromChainedSegs( selection.GetItems(),
convertSettings.m_Strategy) );
if( !temp.IsEmpty() )
{
polySet.Append( temp );
foundChainedSegs = true;
}
else
{
for( EDA_ITEM* item : selection )
item->ClearTempFlags();
}
polySet.Append( makePolysFromOpenGraphics( selection.GetItems() ) );
if( convertSettings.m_Strategy == BOUNDING_HULL )
polySet.Append( makePolysFromOpenGraphics( selection.GetItems() ) );
if( polySet.IsEmpty() )
return false;
@ -258,18 +275,10 @@ int CONVERT_TOOL::CreatePolys( const TOOL_EVENT& aEvent )
return true;
};
// Pre-flight getPolys(). If we find any chained segments then we default m_IgnoreLineWidths
// to true.
// We also use the pre-flight to keep from putting up any of the dialogs if there's nothing
// to convert.
convertSettings.m_IgnoreLineWidths = false;
convertSettings.m_DeleteOriginals = true;
// Pre-flight getPolys() to see if there's anything to convert.
if( !getPolys() )
return 0;
convertSettings.m_IgnoreLineWidths = foundChainedSegs;
bool isFootprint = m_frame->IsType( FRAME_FOOTPRINT_EDITOR );
if( isFootprint )
@ -282,7 +291,9 @@ int CONVERT_TOOL::CreatePolys( const TOOL_EVENT& aEvent )
wxFAIL_MSG( wxT( "Unimplemented footprint parent in CONVERT_TOOL::CreatePolys" ) );
}
BOARD_COMMIT commit( m_frame );
BOARD_DESIGN_SETTINGS& bds = m_frame->GetBoard()->GetDesignSettings();
PCB_LAYER_ID layer = m_frame->GetActiveLayer();
BOARD_COMMIT commit( m_frame );
if( aEvent.IsAction( &PCB_ACTIONS::convertToPoly ) )
{
@ -297,9 +308,36 @@ int CONVERT_TOOL::CreatePolys( const TOOL_EVENT& aEvent )
for( const SHAPE_POLY_SET& poly : polys )
{
PCB_SHAPE* graphic = isFootprint ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
int width = bds.m_LineThickness[ bds.GetLayerClass( layer ) ];
if( convertSettings.m_Strategy == COPY_LINEWIDTH )
{
BOARD_ITEM* topLeftItem = nullptr;
VECTOR2I pos;
for( EDA_ITEM* item : selection )
{
if( BOARD_ITEM* candidate = dynamic_cast<BOARD_ITEM*>( item ) )
{
if( candidate->HasLineStroke() )
{
pos = candidate->GetPosition();
if( !topLeftItem
|| ( pos.x < topLeftItem->GetPosition().x )
|| ( topLeftItem->GetPosition().x == pos.x
&& pos.y < topLeftItem->GetPosition().y ) )
{
topLeftItem = candidate;
width = topLeftItem->GetStroke().GetWidth();
}
}
}
}
}
graphic->SetShape( SHAPE_T::POLY );
graphic->SetStroke( STROKE_PARAMS( 0, PLOT_DASH_TYPE::SOLID, COLOR4D::UNSPECIFIED ) );
graphic->SetStroke( STROKE_PARAMS( width, PLOT_DASH_TYPE::SOLID, COLOR4D::UNSPECIFIED ) );
graphic->SetLayer( destLayer );
graphic->SetPolyShape( poly );
@ -375,25 +413,26 @@ int CONVERT_TOOL::CreatePolys( const TOOL_EVENT& aEvent )
}
SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM*>& aItems )
SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM*>& aItems,
CONVERT_STRATEGY aStrategy )
{
// TODO: This code has a somewhat-similar purpose to ConvertOutlineToPolygon but is slightly
// different, so this remains a separate algorithm. It might be nice to analyze the dfiferences
// in requirements and refactor this.
// Very tight epsilon used here to account for rounding errors in import, not sloppy drawing
const int chainingEpsilonSquared = SEG::Square( 100 );
int chainingEpsilon = pcbIUScale.mmToIU( 0.02 ); // max dist from one endPt to next startPt
SHAPE_POLY_SET poly;
BOARD_DESIGN_SETTINGS& bds = m_frame->GetBoard()->GetDesignSettings();
SHAPE_POLY_SET poly;
// Stores pairs of (anchor, item) where anchor == 0 -> SEG.A, anchor == 1 -> SEG.B
std::map<VECTOR2I, std::vector<std::pair<int, EDA_ITEM*>>> connections;
std::deque<EDA_ITEM*> toCheck;
auto closeEnough =
[]( VECTOR2I aLeft, VECTOR2I aRight, unsigned aLimit )
[]( VECTOR2I aLeft, VECTOR2I aRight, int aLimit )
{
return ( aLeft - aRight ).SquaredEuclideanNorm() <= aLimit;
return ( aLeft - aRight ).SquaredEuclideanNorm() <= SEG::Square( aLimit );
};
auto findInsertionPoint =
@ -404,7 +443,7 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM
for( const auto& candidatePair : connections )
{
if( closeEnough( aPoint, candidatePair.first, chainingEpsilonSquared ) )
if( closeEnough( aPoint, candidatePair.first, chainingEpsilon ) )
return candidatePair.first;
}
@ -413,9 +452,7 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM
for( EDA_ITEM* item : aItems )
{
item->ClearFlags( SKIP_STRUCT );
if( std::optional<SEG> seg = getStartEndPoints( item, nullptr ) )
if( std::optional<SEG> seg = getStartEndPoints( item ) )
{
toCheck.push_back( item );
connections[findInsertionPoint( seg->A )].emplace_back( std::make_pair( 0, item ) );
@ -425,13 +462,14 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM
while( !toCheck.empty() )
{
std::vector<BOARD_ITEM*> insertedItems;
EDA_ITEM* candidate = toCheck.front();
toCheck.pop_front();
if( candidate->GetFlags() & SKIP_STRUCT )
continue;
int width = -1;
SHAPE_LINE_CHAIN outline;
auto insert =
@ -459,6 +497,8 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM
outline.Append( aAnchor == arc.GetP0() ? arc : arc.Reversed() );
else
outline.Insert( 0, aAnchor == arc.GetP0() ? arc : arc.Reversed() );
insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
}
else if( aItem->IsType( { PCB_SHAPE_LOCATE_BEZIER_T } ) )
{
@ -489,18 +529,19 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM
outline.Insert( 0, *it );
}
}
}
else
{
std::optional<SEG> nextSeg = getStartEndPoints( aItem, &width );
wxASSERT( nextSeg );
insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
}
else if( std::optional<SEG> nextSeg = getStartEndPoints( aItem ) )
{
VECTOR2I& point = ( aAnchor == nextSeg->A ) ? nextSeg->B : nextSeg->A;
if( aDirection )
outline.Append( point );
else
outline.Insert( 0, point );
insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
}
};
@ -516,7 +557,7 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM
insert( aItem, aAnchor, aDirection );
std::optional<SEG> anchors = getStartEndPoints( aItem, &width );
std::optional<SEG> anchors = getStartEndPoints( aItem );
wxASSERT( anchors );
VECTOR2I nextAnchor = ( aAnchor == anchors->A ) ? anchors->B : anchors->A;
@ -530,7 +571,7 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM
}
};
std::optional<SEG> anchors = getStartEndPoints( candidate, &width );
std::optional<SEG> anchors = getStartEndPoints( candidate );
wxASSERT( anchors );
// Start with the first object and walk "right"
@ -560,16 +601,30 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM
if( left )
process( left, anchors->A, false );
if( outline.PointCount() < 3 )
if( outline.PointCount() < 3
|| !closeEnough( outline.GetPoint( 0 ), outline.GetPoint( -1 ), chainingEpsilon ) )
{
for( EDA_ITEM* item : insertedItems )
item->ClearFlags( SKIP_STRUCT );
continue;
}
outline.SetClosed( true );
outline.Simplify();
if( width >= 0 )
outline.SetWidth( width );
poly.AddOutline( outline );
if( aStrategy == BOUNDING_HULL )
{
for( BOARD_ITEM* item : insertedItems )
{
item->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError,
ERROR_INSIDE, false );
}
}
insertedItems.clear();
}
return poly;
@ -591,14 +646,15 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromOpenGraphics( const std::deque<EDA_ITE
case PCB_SHAPE_T:
case PCB_FP_SHAPE_T:
{
PCB_SHAPE* temp = static_cast<PCB_SHAPE*>( item->Clone() );
PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
if( temp->IsClosed() )
if( shape->IsClosed() )
continue;
temp->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError, ERROR_INSIDE,
false );
item->SetFlags( SKIP_STRUCT );
shape->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError, ERROR_INSIDE,
false );
shape->SetFlags( SKIP_STRUCT );
break;
}
@ -612,7 +668,7 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromOpenGraphics( const std::deque<EDA_ITE
SHAPE_POLY_SET CONVERT_TOOL::makePolysFromClosedGraphics( const std::deque<EDA_ITEM*>& aItems,
bool aIgnoreLineWidths )
CONVERT_STRATEGY aStrategy )
{
BOARD_DESIGN_SETTINGS& bds = m_frame->GetBoard()->GetDesignSettings();
SHAPE_POLY_SET poly;
@ -627,15 +683,18 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromClosedGraphics( const std::deque<EDA_I
case PCB_SHAPE_T:
case PCB_FP_SHAPE_T:
{
PCB_SHAPE* temp = static_cast<PCB_SHAPE*>( item->Clone() );
PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
FILL_T wasFilled = shape->GetFillMode();
if( !temp->IsClosed() )
if( !shape->IsClosed() )
continue;
temp->SetFilled( true );
temp->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError, ERROR_INSIDE,
aIgnoreLineWidths );
item->SetFlags( SKIP_STRUCT );
shape->SetFilled( true );
shape->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError, ERROR_INSIDE,
aStrategy == COPY_LINEWIDTH || aStrategy == CENTERLINE );
shape->SetFillMode( wasFilled );
shape->SetFlags( SKIP_STRUCT );
break;
}
@ -917,7 +976,7 @@ int CONVERT_TOOL::SegmentToArc( const TOOL_EVENT& aEvent )
// Offset the midpoint along the normal a little bit so that it's more obviously an arc
const double offsetRatio = 0.1;
if( std::optional<SEG> seg = getStartEndPoints( source, nullptr ) )
if( std::optional<SEG> seg = getStartEndPoints( source ) )
{
start = seg->A;
end = seg->B;
@ -981,7 +1040,7 @@ int CONVERT_TOOL::SegmentToArc( const TOOL_EVENT& aEvent )
}
std::optional<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem, int* aWidth )
std::optional<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem )
{
switch( aItem->Type() )
{
@ -999,9 +1058,6 @@ std::optional<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem, int* aWidth
if( shape->GetStart() == shape->GetEnd() )
return std::nullopt;
if( aWidth )
*aWidth = shape->GetWidth();
return std::make_optional<SEG>( VECTOR2I( shape->GetStart() ),
VECTOR2I( shape->GetEnd() ) );
@ -1013,20 +1069,12 @@ std::optional<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem, int* aWidth
case PCB_TRACE_T:
{
PCB_TRACK* line = static_cast<PCB_TRACK*>( aItem );
if( aWidth )
*aWidth = line->GetWidth();
return std::make_optional<SEG>( VECTOR2I( line->GetStart() ), VECTOR2I( line->GetEnd() ) );
}
case PCB_ARC_T:
{
PCB_ARC* arc = static_cast<PCB_ARC*>( aItem );
if( aWidth )
*aWidth = arc->GetWidth();
return std::make_optional<SEG>( VECTOR2I( arc->GetStart() ), VECTOR2I( arc->GetEnd() ) );
}

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
* @author Jon Evans <jon@craftyjon.com>
*
* This program is free software; you can redistribute it and/or
@ -27,6 +27,7 @@
#include <geometry/shape_poly_set.h>
#include <tool/tool_interactive.h>
#include <pcbnew_settings.h>
class CONDITIONAL_MENU;
class PCB_SELECTION_TOOL;
@ -70,7 +71,7 @@ private:
* @param aItem is an item that has a start and end point.
* @return a segment from start to end, or std::nullopt if invalid.
*/
static std::optional<SEG> getStartEndPoints( EDA_ITEM* aItem, int* aWidth );
static std::optional<SEG> getStartEndPoints( EDA_ITEM* aItem );
/**
* Try to make polygons from chained segments in the selected items.
@ -82,7 +83,8 @@ private:
* @param aItems is a list of items to process.
* @return a #SHAPE_POLY_SET containing any polygons that were created.
*/
SHAPE_POLY_SET makePolysFromChainedSegs( const std::deque<EDA_ITEM*>& aItems );
SHAPE_POLY_SET makePolysFromChainedSegs( const std::deque<EDA_ITEM*>& aItems,
CONVERT_STRATEGY aStrategy );
/**
* Make polygons from graphic shapes and zones.
@ -92,7 +94,7 @@ private:
*/
SHAPE_POLY_SET makePolysFromOpenGraphics( const std::deque<EDA_ITEM*>& aItems );
SHAPE_POLY_SET makePolysFromClosedGraphics( const std::deque<EDA_ITEM*>& aItems,
bool aIgnoreLineWidths );
CONVERT_STRATEGY aStrategy );
PCB_SELECTION_TOOL* m_selectionTool;
CONDITIONAL_MENU* m_menu;