KiCad PCB EDA Suite
Loading...
Searching...
No Matches
convert_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 * @author Jon Evans <[email protected]>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <bitmaps.h>
26#include <dialog_shim.h>
27#include <wx/statline.h>
28#include <wx/checkbox.h>
29#include <wx/button.h>
30#include <wx/radiobut.h>
31#include <widgets/unit_binder.h>
32#include <board.h>
33#include <board_commit.h>
35#include <collectors.h>
36#include <confirm.h>
39#include <footprint.h>
42#include <pcb_edit_frame.h>
43#include <pcb_shape.h>
44#include <pcb_track.h>
45#include <pad.h>
46#include <tool/tool_manager.h>
47#include <tools/edit_tool.h>
48#include <tools/pcb_actions.h>
51#include <trigo.h>
52#include <macros.h>
53#include <zone.h>
54
55#include "convert_tool.h"
56
57
59{
60public:
62 bool aShowCopyLineWidthOption, bool aShowCenterlineOption,
63 bool aShowBoundingHullOption ) :
64 DIALOG_SHIM( aParent, wxID_ANY, _( "Conversion Settings" ), wxDefaultPosition,
65 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
66 m_settings( aSettings )
67 {
68 // Allow each distinct version of the dialog to have a different size
69 m_hash_key = TO_UTF8( wxString::Format( wxS( "%s%c%c%c" ),
70 GetTitle(),
71 aShowCopyLineWidthOption,
72 aShowCenterlineOption,
73 aShowBoundingHullOption ) );
74
75 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
76 wxBoxSizer* topSizer = new wxBoxSizer( wxVERTICAL );
77 SetSizer( mainSizer );
78
79 m_rbMimicLineWidth = new wxRadioButton( this, wxID_ANY, _( "Copy line width of first object" ) );
80
81 if( aShowCopyLineWidthOption )
82 topSizer->Add( m_rbMimicLineWidth, 0, wxLEFT|wxRIGHT, 5 );
83 else
84 m_rbMimicLineWidth->Hide();
85
86 m_rbCenterline = new wxRadioButton( this, wxID_ANY, _( "Use centerlines" ) );
87
88 if( aShowCenterlineOption )
89 {
90 topSizer->AddSpacer( 6 );
91 topSizer->Add( m_rbCenterline, 0, wxLEFT|wxRIGHT, 5 );
92 }
93 else
94 {
95 m_rbCenterline->Hide();
96 }
97
98 m_rbBoundingHull = new wxRadioButton( this, wxID_ANY, _( "Create bounding hull" ) );
99
100 m_gapLabel = new wxStaticText( this, wxID_ANY, _( "Gap:" ) );
101 m_gapCtrl = new wxTextCtrl( this, wxID_ANY );
102 m_gapUnits = new wxStaticText( this, wxID_ANY, _( "mm" ) );
103 m_gap = new UNIT_BINDER( aParent, m_gapLabel, m_gapCtrl, m_gapUnits );
104
105 m_widthLabel = new wxStaticText( this, wxID_ANY, _( "Line width:" ) );
106 m_widthCtrl = new wxTextCtrl( this, wxID_ANY );
107 m_widthUnits = new wxStaticText( this, wxID_ANY, _( "mm" ) );
109
110 if( aShowBoundingHullOption )
111 {
112 topSizer->AddSpacer( 6 );
113 topSizer->Add( m_rbBoundingHull, 0, wxLEFT|wxRIGHT, 5 );
114
115 wxBoxSizer* hullParamsSizer = new wxBoxSizer( wxHORIZONTAL );
116 hullParamsSizer->Add( m_gapLabel, 0, wxALIGN_CENTRE_VERTICAL, 5 );
117 hullParamsSizer->Add( m_gapCtrl, 1, wxALIGN_CENTRE_VERTICAL|wxLEFT|wxRIGHT, 3 );
118 hullParamsSizer->Add( m_gapUnits, 0, wxALIGN_CENTRE_VERTICAL, 5 );
119 hullParamsSizer->AddSpacer( 18 );
120 hullParamsSizer->Add( m_widthLabel, 0, wxALIGN_CENTRE_VERTICAL, 5 );
121 hullParamsSizer->Add( m_widthCtrl, 1, wxALIGN_CENTRE_VERTICAL|wxLEFT|wxRIGHT, 3 );
122 hullParamsSizer->Add( m_widthUnits, 0, wxALIGN_CENTRE_VERTICAL, 5 );
123
124 topSizer->AddSpacer( 2 );
125 topSizer->Add( hullParamsSizer, 0, wxLEFT, 26 );
126
127 topSizer->AddSpacer( 15 );
128 }
129 else
130 {
131 m_rbBoundingHull->Hide();
132 m_gap->Show( false, true );
133 m_width->Show( false, true );
134 }
135
136 m_cbDeleteOriginals = new wxCheckBox( this, wxID_ANY, _( "Delete source objects after conversion" ) );
137 topSizer->Add( m_cbDeleteOriginals, 0, wxALL, 5 );
138
139 mainSizer->Add( topSizer, 1, wxALL|wxEXPAND, 10 );
140
141 wxBoxSizer* buttonsSizer = new wxBoxSizer( wxHORIZONTAL );
142 buttonsSizer->AddStretchSpacer();
143
144 wxStdDialogButtonSizer* sdbSizer = new wxStdDialogButtonSizer();
145 wxButton* sdbSizerOK = new wxButton( this, wxID_OK );
146 sdbSizer->AddButton( sdbSizerOK );
147 wxButton* sdbSizerCancel = new wxButton( this, wxID_CANCEL );
148 sdbSizer->AddButton( sdbSizerCancel );
149 sdbSizer->Realize();
150
151 buttonsSizer->Add( sdbSizer, 1, 0, 5 );
152 mainSizer->Add( buttonsSizer, 0, wxALL|wxEXPAND, 5 );
153
155
156 m_rbMimicLineWidth->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED,
157 wxCommandEventHandler( CONVERT_SETTINGS_DIALOG::onRadioButton ),
158 nullptr, this );
159 m_rbCenterline->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED,
160 wxCommandEventHandler( CONVERT_SETTINGS_DIALOG::onRadioButton ),
161 nullptr, this );
162 m_rbBoundingHull->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED,
163 wxCommandEventHandler( CONVERT_SETTINGS_DIALOG::onRadioButton ),
164 nullptr, this );
165
167 }
168
170 {
171 delete m_gap;
172 delete m_width;
173 };
174
175protected:
176 bool TransferDataToWindow() override
177 {
178 switch( m_settings->m_Strategy )
179 {
180 case COPY_LINEWIDTH: m_rbMimicLineWidth->SetValue( true ); break;
181 case CENTERLINE: m_rbCenterline->SetValue( true ); break;
182 case BOUNDING_HULL: m_rbBoundingHull->SetValue( true ); break;
183 }
184
185 m_gap->Enable( m_rbBoundingHull->GetValue() );
186 m_width->Enable( m_rbBoundingHull->GetValue() );
189
191 return true;
192 }
193
195 {
196 if( m_rbBoundingHull->GetValue() )
198 else if( m_rbCenterline->GetValue() )
200 else
202
205
207 return true;
208 }
209
210 void onRadioButton( wxCommandEvent& aEvent )
211 {
212 m_gap->Enable( m_rbBoundingHull->GetValue() );
213 m_width->Enable( m_rbBoundingHull->GetValue() );
214 }
215
216private:
218
219 wxRadioButton* m_rbMimicLineWidth;
220 wxRadioButton* m_rbCenterline;
221 wxRadioButton* m_rbBoundingHull;
222 wxStaticText* m_gapLabel;
223 wxTextCtrl* m_gapCtrl;
224 wxStaticText* m_gapUnits;
226 wxStaticText* m_widthLabel;
227 wxTextCtrl* m_widthCtrl;
228 wxStaticText* m_widthUnits;
231};
232
233
235 PCB_TOOL_BASE( "pcbnew.Convert" ),
236 m_selectionTool( nullptr ),
237 m_menu( nullptr ),
238 m_frame( nullptr )
239{
241}
242
243
245{
246 delete m_menu;
247}
248
249
252
253
255{
257 m_frame = getEditFrame<PCB_BASE_FRAME>();
258
259 // Create a context menu and make it available through selection tool
260 m_menu = new CONDITIONAL_MENU( this );
261 m_menu->SetIcon( BITMAPS::convert );
262 m_menu->SetTitle( _( "Create from Selection" ) );
263
264 static const std::vector<KICAD_T> padTypes = { PCB_PAD_T };
265 static const std::vector<KICAD_T> toArcTypes = { PCB_ARC_T,
268 static const std::vector<KICAD_T> shapeTypes = { PCB_SHAPE_LOCATE_SEGMENT_T,
274 PCB_TEXT_T };
275 static const std::vector<KICAD_T> trackTypes = { PCB_TRACE_T,
276 PCB_ARC_T,
277 PCB_VIA_T };
278 static const std::vector<KICAD_T> toTrackTypes = { PCB_SHAPE_LOCATE_SEGMENT_T,
280 static const std::vector<KICAD_T> polyTypes = { PCB_ZONE_T,
283 static const std::vector<KICAD_T> outsetTypes = { PCB_PAD_T, PCB_SHAPE_T };
284
285 auto shapes = S_C::OnlyTypes( shapeTypes ) && P_S_C::SameLayer();
286 auto graphicToTrack = S_C::OnlyTypes( toTrackTypes );
287 auto anyTracks = S_C::MoreThan( 0 ) && S_C::OnlyTypes( trackTypes ) && P_S_C::SameLayer();
288 auto anyPolys = S_C::OnlyTypes( polyTypes );
289 auto anyPads = S_C::OnlyTypes( padTypes );
290
291 auto canCreateArcs = S_C::Count( 1 ) && S_C::OnlyTypes( toArcTypes );
292 auto canCreateArray = S_C::MoreThan( 0 );
293 auto canCreatePoly = shapes || anyPolys || anyTracks;
294
295 auto canCreateOutset = S_C::OnlyTypes( outsetTypes );
296
298 canCreatePoly = shapes || anyPolys || anyTracks || anyPads;
299
300 auto canCreateLines = anyPolys;
301 auto canCreateTracks = anyPolys || graphicToTrack;
302 auto canCreate = canCreatePoly
303 || canCreateLines
304 || canCreateTracks
305 || canCreateArcs
306 || canCreateArray
307 || canCreateOutset;
308
309 m_menu->AddItem( PCB_ACTIONS::convertToPoly, canCreatePoly );
310
312 m_menu->AddItem( PCB_ACTIONS::convertToZone, canCreatePoly );
313
315 m_menu->AddItem( PCB_ACTIONS::convertToLines, canCreateLines );
316 m_menu->AddItem( PCB_ACTIONS::outsetItems, canCreateOutset );
318
319 // Currently the code exists, but tracks are not really existing in footprints
320 // only segments on copper layers
322 m_menu->AddItem( PCB_ACTIONS::convertToTracks, canCreateTracks );
323
324 m_menu->AddItem( PCB_ACTIONS::convertToArc, canCreateArcs );
325
327 m_menu->AddItem( PCB_ACTIONS::createArray, canCreateArray );
328
330 selToolMenu.AddMenu( m_menu, canCreate, 100 );
331
332 return true;
333}
334
335
337{
342}
343
344
346{
348 std::vector<SHAPE_POLY_SET> polys;
349 PCB_LAYER_ID destLayer = m_frame->GetActiveLayer();
350 FOOTPRINT* parentFootprint = nullptr;
351
353 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
354 {
355 } );
356
357 if( selection.Empty() )
358 return 0;
359
360 auto getPolys =
361 [&]( CONVERT_SETTINGS cfg )
362 {
363 polys.clear();
364
365 for( EDA_ITEM* item : selection )
366 item->ClearTempFlags();
367
368 SHAPE_POLY_SET polySet;
369
370 polySet.Append( makePolysFromClosedGraphics( selection.GetItems(), cfg.m_Strategy ) );
371
372 if( cfg.m_Strategy == BOUNDING_HULL )
373 {
375
376 polySet.ClearArcs();
377 polySet.Simplify();
378
379 // Now inflate the bounding hull by cfg.m_Gap
380 polySet.Inflate( cfg.m_Gap, CORNER_STRATEGY::ROUND_ALL_CORNERS, bds.m_MaxError,
382 }
383 else
384 {
385 polySet.Append( makePolysFromChainedSegs( selection.GetItems(), cfg.m_Strategy ) );
386 }
387
388 if( polySet.IsEmpty() )
389 return false;
390
391 for( int ii = 0; ii < polySet.OutlineCount(); ++ii )
392 {
393 polys.emplace_back( SHAPE_POLY_SET( polySet.COutline( ii ) ) );
394
395 for( int jj = 0; jj < polySet.HoleCount( ii ); ++jj )
396 polys.back().AddHole( polySet.Hole( ii, jj ) );
397 }
398
399 return true;
400 };
401
402 // Pre-flight getPolys() to see if there's anything to convert.
403 CONVERT_SETTINGS preflightSettings = m_userSettings;
404 preflightSettings.m_Strategy = BOUNDING_HULL;
405
406 if( !getPolys( preflightSettings ) )
407 return 0;
408
410 parentFootprint = static_cast<BOARD_ITEM*>( selection.Front() )->GetParentFootprint();
411
413 BOARD_COMMIT commit( m_frame );
414
415 if( aEvent.IsAction( &PCB_ACTIONS::convertToPoly ) )
416 {
417 bool showCopyLineWidth = true;
418
419 // No copy-line-width option for pads
420 if( selection.Front()->Type() == PCB_PAD_T )
421 {
424
425 showCopyLineWidth = false;
426 }
427
428 CONVERT_SETTINGS previousSettings = m_userSettings;
429
430 CONVERT_SETTINGS_DIALOG dlg( m_frame, &m_userSettings, showCopyLineWidth, true, true );
431
432 if( dlg.ShowModal() != wxID_OK )
433 return 0;
434
435 CONVERT_SETTINGS resolvedSettings = m_userSettings;
436
437 if( resolvedSettings.m_Strategy != CENTERLINE )
438 {
439 if( resolvedSettings.m_LineWidth == 0 )
440 resolvedSettings.m_LineWidth = bds.m_LineThickness[ bds.GetLayerClass( layer ) ];
441 }
442
443 if( resolvedSettings.m_Strategy == BOUNDING_HULL )
444 {
445 if( resolvedSettings.m_Gap > 0 )
446 resolvedSettings.m_Gap += KiROUND( (double) resolvedSettings.m_LineWidth / 2 );
447 }
448
449 if( !getPolys( resolvedSettings ) )
450 {
451 wxString msg;
452
453 if( resolvedSettings.m_Strategy == BOUNDING_HULL )
454 msg = _( "Resulting polygon would be empty" );
455 else
456 msg = _( "Objects must form a closed shape" );
457
458 DisplayErrorMessage( m_frame, _( "Could not convert selection" ), msg );
459
460 m_userSettings = previousSettings;
461 return 0;
462 }
463
464 for( const SHAPE_POLY_SET& poly : polys )
465 {
466 PCB_SHAPE* graphic = new PCB_SHAPE( parentFootprint );
467
468 if( resolvedSettings.m_Strategy == COPY_LINEWIDTH )
469 {
470 BOARD_ITEM* topLeftItem = nullptr;
471 VECTOR2I pos;
472
473 for( EDA_ITEM* item : selection )
474 {
475 if( !item->IsBOARD_ITEM() )
476 continue;
477
478 BOARD_ITEM* candidate = static_cast<BOARD_ITEM*>( item );
479
480 if( candidate->HasLineStroke() )
481 {
482 pos = candidate->GetPosition();
483
484 if( !topLeftItem
485 || ( pos.x < topLeftItem->GetPosition().x )
486 || ( topLeftItem->GetPosition().x == pos.x
487 && pos.y < topLeftItem->GetPosition().y ) )
488 {
489 topLeftItem = candidate;
490 resolvedSettings.m_LineWidth = topLeftItem->GetStroke().GetWidth();
491 }
492 }
493 }
494 }
495
496 graphic->SetShape( SHAPE_T::POLY );
497 graphic->SetStroke( STROKE_PARAMS( resolvedSettings.m_LineWidth, LINE_STYLE::SOLID,
498 COLOR4D::UNSPECIFIED ) );
499 graphic->SetFilled( resolvedSettings.m_Strategy == CENTERLINE );
500 graphic->SetLayer( destLayer );
501 graphic->SetPolyShape( poly );
502
503 commit.Add( graphic );
504 }
505 }
506 else
507 {
508 // Creating zone or keepout
509 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
510 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
511 ZONE_SETTINGS zoneInfo = bds.GetDefaultZoneSettings();
512
513 bool nonCopper = IsNonCopperLayer( destLayer );
514 zoneInfo.m_Layers.reset().set( destLayer );
515 zoneInfo.m_Name.Empty();
516
517 int ret;
518
519 // No copy-line-width option for zones/keepouts
522
524 {
525 zoneInfo.SetIsRuleArea( true );
526 ret = InvokeRuleAreaEditor( frame, &zoneInfo, board(), &m_userSettings );
527 }
528 else if( nonCopper )
529 {
530 zoneInfo.SetIsRuleArea( false );
531 ret = InvokeNonCopperZonesEditor( frame, &zoneInfo, &m_userSettings );
532 }
533 else
534 {
535 zoneInfo.SetIsRuleArea( false );
536 ret = InvokeCopperZonesEditor( frame, &zoneInfo, &m_userSettings );
537 }
538
539 if( ret == wxID_CANCEL )
540 return 0;
541
542 if( !getPolys( m_userSettings ) )
543 return 0;
544
545 for( const SHAPE_POLY_SET& poly : polys )
546 {
547 ZONE* zone = new ZONE( parent );
548
549 *zone->Outline() = poly;
550 zone->HatchBorder();
551
552 zoneInfo.ExportSetting( *zone );
553
554 commit.Add( zone );
555 }
556 }
557
559 {
560 PCB_SELECTION selectionCopy = selection;
562
563 for( EDA_ITEM* item : selectionCopy )
564 {
565 if( item->GetFlags() & SKIP_STRUCT )
566 commit.Remove( item );
567 }
568 }
569
570 if( aEvent.IsAction( &PCB_ACTIONS::convertToPoly ) )
571 {
573 commit.Push( _( "Convert to Polygon" ) );
574 else
575 commit.Push( _( "Create Polygon" ) );
576 }
577 else
578 {
580 commit.Push( _( "Convert to Zone" ) );
581 else
582 commit.Push( _( "Create Zone" ) );
583 }
584
585 return 0;
586}
587
588
589SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM*>& aItems,
590 CONVERT_STRATEGY aStrategy )
591{
592 // TODO: This code has a somewhat-similar purpose to ConvertOutlineToPolygon but is slightly
593 // different, so this remains a separate algorithm. It might be nice to analyze the dfiferences
594 // in requirements and refactor this.
595
596 // Using a large epsilon here to allow for sloppy drawing can cause the algorithm to miss very
597 // short segments in a converted bezier. So use an epsilon only large enough to cover for
598 // rouding errors in the conversion.
599 int chainingEpsilon = 100; // max dist from one endPt to next startPt in IU
600
602 SHAPE_POLY_SET poly;
603
604 // Stores pairs of (anchor, item) where anchor == 0 -> SEG.A, anchor == 1 -> SEG.B
605 std::map<VECTOR2I, std::vector<std::pair<int, EDA_ITEM*>>> connections;
606 std::deque<EDA_ITEM*> toCheck;
607
608 auto closeEnough =
609 []( const VECTOR2I& aLeft, const VECTOR2I& aRight, int aLimit )
610 {
611 return ( aLeft - aRight ).SquaredEuclideanNorm() <= SEG::Square( aLimit );
612 };
613
614 auto findInsertionPoint =
615 [&]( const VECTOR2I& aPoint ) -> VECTOR2I
616 {
617 if( connections.count( aPoint ) )
618 return aPoint;
619
620 for( const auto& candidatePair : connections )
621 {
622 if( closeEnough( aPoint, candidatePair.first, chainingEpsilon ) )
623 return candidatePair.first;
624 }
625
626 return aPoint;
627 };
628
629 for( EDA_ITEM* item : aItems )
630 {
631 if( std::optional<SEG> seg = getStartEndPoints( item ) )
632 {
633 toCheck.push_back( item );
634 connections[findInsertionPoint( seg->A )].emplace_back( std::make_pair( 0, item ) );
635 connections[findInsertionPoint( seg->B )].emplace_back( std::make_pair( 1, item ) );
636 }
637 }
638
639 while( !toCheck.empty() )
640 {
641 std::vector<BOARD_ITEM*> insertedItems;
642
643 EDA_ITEM* candidate = toCheck.front();
644 toCheck.pop_front();
645
646 if( candidate->GetFlags() & SKIP_STRUCT )
647 continue;
648
649 SHAPE_LINE_CHAIN outline;
650
651 auto insert =
652 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
653 {
654 if( aItem->IsType( { PCB_ARC_T, PCB_SHAPE_LOCATE_ARC_T } ) )
655 {
656 SHAPE_ARC arc;
657
658 if( aItem->Type() == PCB_ARC_T )
659 {
660 PCB_ARC* pcb_arc = static_cast<PCB_ARC*>( aItem );
661 arc = *static_cast<SHAPE_ARC*>( pcb_arc->GetEffectiveShape().get() );
662 }
663 else
664 {
665 PCB_SHAPE* pcb_shape = static_cast<PCB_SHAPE*>( aItem );
666 arc = SHAPE_ARC( pcb_shape->GetStart(), pcb_shape->GetArcMid(),
667 pcb_shape->GetEnd(), pcb_shape->GetWidth() );
668 }
669
670 if( aDirection )
671 outline.Append( aAnchor == arc.GetP0() ? arc : arc.Reversed() );
672 else
673 outline.Insert( 0, aAnchor == arc.GetP0() ? arc : arc.Reversed() );
674
675 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
676 }
677 else if( aItem->IsType( { PCB_SHAPE_LOCATE_BEZIER_T } ) )
678 {
679 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
680
681 if( aAnchor == graphic->GetStart() )
682 {
683 for( auto it = graphic->GetBezierPoints().begin();
684 it != graphic->GetBezierPoints().end();
685 ++it )
686 {
687 if( aDirection )
688 outline.Append( *it );
689 else
690 outline.Insert( 0, *it );
691 }
692
693 }
694 else
695 {
696 for( auto it = graphic->GetBezierPoints().rbegin();
697 it != graphic->GetBezierPoints().rend();
698 ++it )
699 {
700 if( aDirection )
701 outline.Append( *it );
702 else
703 outline.Insert( 0, *it );
704 }
705 }
706
707 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
708 }
709 else if( std::optional<SEG> nextSeg = getStartEndPoints( aItem ) )
710 {
711 VECTOR2I& point = ( aAnchor == nextSeg->A ) ? nextSeg->B : nextSeg->A;
712
713 if( aDirection )
714 outline.Append( point );
715 else
716 outline.Insert( 0, point );
717
718 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
719 }
720 };
721
722 // aDirection == true for walking "right" and appending to the end of points
723 // false for walking "left" and prepending to the beginning
724 std::function<void( EDA_ITEM*, const VECTOR2I&, bool )> process =
725 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
726 {
727 if( aItem->GetFlags() & SKIP_STRUCT )
728 return;
729
730 aItem->SetFlags( SKIP_STRUCT );
731
732 insert( aItem, aAnchor, aDirection );
733
734 std::optional<SEG> anchors = getStartEndPoints( aItem );
735 wxASSERT( anchors );
736
737 VECTOR2I nextAnchor = ( aAnchor == anchors->A ) ? anchors->B : anchors->A;
738
739 for( std::pair<int, EDA_ITEM*> pair : connections[nextAnchor] )
740 {
741 if( pair.second == aItem )
742 continue;
743
744 process( pair.second, nextAnchor, aDirection );
745 }
746 };
747
748 std::optional<SEG> anchors = getStartEndPoints( candidate );
749 wxASSERT( anchors );
750
751 // Start with the first object and walk "right"
752 // Note if the first object is an arc, we don't need to insert its first point here, the
753 // whole arc will be inserted at anchor B inside process()
754 if( !candidate->IsType( { PCB_ARC_T, PCB_SHAPE_LOCATE_ARC_T } ) )
755 insert( candidate, anchors->A, true );
756
757 process( candidate, anchors->B, true );
758
759 // check for any candidates on the "left"
760 EDA_ITEM* left = nullptr;
761
762 for( std::pair<int, EDA_ITEM*> possibleLeft : connections[anchors->A] )
763 {
764 if( possibleLeft.second != candidate )
765 {
766 left = possibleLeft.second;
767 break;
768 }
769 }
770
771 if( left )
772 process( left, anchors->A, false );
773
774 if( outline.PointCount() < 3
775 || !closeEnough( outline.GetPoint( 0 ), outline.GetPoint( -1 ), chainingEpsilon ) )
776 {
777 for( EDA_ITEM* item : insertedItems )
778 item->ClearFlags( SKIP_STRUCT );
779
780 continue;
781 }
782
783 outline.SetClosed( true );
784
785 poly.AddOutline( outline );
786
787 if( aStrategy == BOUNDING_HULL )
788 {
789 for( BOARD_ITEM* item : insertedItems )
790 {
791 item->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError, ERROR_INSIDE,
792 false );
793 }
794 }
795
796 insertedItems.clear();
797 }
798
799 return poly;
800}
801
802
803SHAPE_POLY_SET CONVERT_TOOL::makePolysFromOpenGraphics( const std::deque<EDA_ITEM*>& aItems, int aGap )
804{
806 SHAPE_POLY_SET poly;
807
808 for( EDA_ITEM* item : aItems )
809 {
810 if( item->GetFlags() & SKIP_STRUCT )
811 continue;
812
813 switch( item->Type() )
814 {
815 case PCB_SHAPE_T:
816 {
817 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
818
819 if( shape->IsClosed() )
820 continue;
821
823 false );
824 shape->SetFlags( SKIP_STRUCT );
825
826 break;
827 }
828
829 case PCB_TRACE_T:
830 case PCB_ARC_T:
831 case PCB_VIA_T:
832 {
833 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
834
836 false );
837 track->SetFlags( SKIP_STRUCT );
838
839 break;
840 }
841
842 default:
843 continue;
844 }
845 }
846
847 return poly;
848}
849
850
852 CONVERT_STRATEGY aStrategy )
853{
855 SHAPE_POLY_SET poly;
856
857 for( EDA_ITEM* item : aItems )
858 {
859 if( item->GetFlags() & SKIP_STRUCT )
860 continue;
861
862 switch( item->Type() )
863 {
864 case PCB_SHAPE_T:
865 {
866 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
867 FILL_T wasFilled = shape->GetFillMode();
868
869 if( !shape->IsClosed() )
870 continue;
871
872 if( shape->GetShape() == SHAPE_T::CIRCLE && aStrategy != BOUNDING_HULL )
873 {
874 VECTOR2I c = shape->GetCenter();
875 int R = shape->GetRadius();
876 SHAPE_ARC arc( c - VECTOR2I( R, 0 ), c + VECTOR2I( R, 0 ), c - VECTOR2I( R, 0 ), 0 );
877
878 poly.NewOutline();
879 poly.Append( arc );
880 }
881 else
882 {
883 if( aStrategy != BOUNDING_HULL )
884 shape->SetFilled( true );
885
887 aStrategy != BOUNDING_HULL );
888
889 if( aStrategy != BOUNDING_HULL )
890 shape->SetFillMode( wasFilled );
891 }
892
893 shape->SetFlags( SKIP_STRUCT );
894 break;
895 }
896
897 case PCB_ZONE_T:
898 poly.Append( *static_cast<ZONE*>( item )->Outline() );
899 item->SetFlags( SKIP_STRUCT );
900 break;
901
902 case PCB_FIELD_T:
903 case PCB_TEXT_T:
904 {
905 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
906 text->TransformTextToPolySet( poly, 0, bds.m_MaxError, ERROR_INSIDE );
907 text->SetFlags( SKIP_STRUCT );
908 break;
909 }
910
911 case PCB_PAD_T:
912 {
913 PAD* pad = static_cast<PAD*>( item );
914 pad->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError, ERROR_INSIDE, false );
915 pad->SetFlags( SKIP_STRUCT );
916 break;
917 }
918
919
920 default:
921 continue;
922 }
923 }
924
925 return poly;
926}
927
928
930{
932 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
933 {
934 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
935 {
936 BOARD_ITEM* item = aCollector[i];
937
938 switch( item->Type() )
939 {
940 case PCB_SHAPE_T:
941 switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
942 {
943 case SHAPE_T::SEGMENT:
944 case SHAPE_T::ARC:
945 case SHAPE_T::POLY:
946 case SHAPE_T::RECTANGLE:
947 break;
948
949 default:
950 aCollector.Remove( item );
951 }
952
953 break;
954
955 case PCB_ZONE_T:
956 break;
957
958 default:
959 aCollector.Remove( item );
960 }
961 }
962 } );
963
964 if( selection.Empty() )
965 return 0;
966
967 auto getPolySet =
968 []( EDA_ITEM* aItem )
969 {
970 SHAPE_POLY_SET set;
971
972 switch( aItem->Type() )
973 {
974 case PCB_ZONE_T:
975 set = *static_cast<ZONE*>( aItem )->Outline();
976 break;
977
978 case PCB_SHAPE_T:
979 {
980 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
981
982 if( graphic->GetShape() == SHAPE_T::POLY )
983 {
984 set = graphic->GetPolyShape();
985 }
986 else if( graphic->GetShape() == SHAPE_T::RECTANGLE )
987 {
988 SHAPE_LINE_CHAIN outline;
989 VECTOR2I start( graphic->GetStart() );
990 VECTOR2I end( graphic->GetEnd() );
991
992 outline.Append( start );
993 outline.Append( VECTOR2I( end.x, start.y ) );
994 outline.Append( end );
995 outline.Append( VECTOR2I( start.x, end.y ) );
996 outline.SetClosed( true );
997
998 set.AddOutline( outline );
999 }
1000 else
1001 {
1002 wxFAIL_MSG( wxT( "Unhandled graphic shape type in PolyToLines - getPolySet" ) );
1003 }
1004 break;
1005 }
1006
1007 default:
1008 wxFAIL_MSG( wxT( "Unhandled type in PolyToLines - getPolySet" ) );
1009 break;
1010 }
1011
1012 return set;
1013 };
1014
1015 auto getSegList =
1016 []( SHAPE_POLY_SET& aPoly )
1017 {
1018 std::vector<SEG> segs;
1019
1020 // Our input should be valid polys, so OK to assert here
1021 wxASSERT( aPoly.VertexCount() >= 2 );
1022
1023 for( int i = 1; i < aPoly.VertexCount(); i++ )
1024 segs.emplace_back( SEG( aPoly.CVertex( i - 1 ), aPoly.CVertex( i ) ) );
1025
1026 segs.emplace_back( SEG( aPoly.CVertex( aPoly.VertexCount() - 1 ),
1027 aPoly.CVertex( 0 ) ) );
1028
1029 return segs;
1030 };
1031
1032 BOARD_COMMIT commit( m_frame );
1033 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1034 FOOTPRINT_EDIT_FRAME* fpEditor = dynamic_cast<FOOTPRINT_EDIT_FRAME*>( m_frame );
1035 FOOTPRINT* footprint = nullptr;
1036 PCB_LAYER_ID targetLayer = m_frame->GetActiveLayer();
1037 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1038
1039 if( fpEditor )
1040 footprint = fpEditor->GetBoard()->GetFirstFootprint();
1041
1042 auto handleGraphicSeg =
1043 [&]( EDA_ITEM* aItem )
1044 {
1045 if( aItem->Type() != PCB_SHAPE_T )
1046 return false;
1047
1048 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
1049
1050 if( graphic->GetShape() == SHAPE_T::SEGMENT )
1051 {
1052 PCB_TRACK* track = new PCB_TRACK( parent );
1053
1054 track->SetLayer( targetLayer );
1055 track->SetStart( graphic->GetStart() );
1056 track->SetEnd( graphic->GetEnd() );
1057 track->SetWidth( graphic->GetWidth() );
1058 commit.Add( track );
1059
1060 return true;
1061 }
1062 else if( graphic->GetShape() == SHAPE_T::ARC )
1063 {
1064 PCB_ARC* arc = new PCB_ARC( parent );
1065
1066 arc->SetLayer( targetLayer );
1067 arc->SetStart( graphic->GetStart() );
1068 arc->SetEnd( graphic->GetEnd() );
1069 arc->SetMid( graphic->GetArcMid() );
1070 arc->SetWidth( graphic->GetWidth() );
1071 commit.Add( arc );
1072
1073 return true;
1074 }
1075
1076 return false;
1077 };
1078
1079 if( aEvent.IsAction( &PCB_ACTIONS::convertToTracks ) )
1080 {
1081 if( !IsCopperLayer( targetLayer ) )
1082 {
1083 targetLayer = frame->SelectOneLayer( F_Cu, LSET::AllNonCuMask() );
1084
1085 if( targetLayer == UNDEFINED_LAYER ) // User canceled
1086 return true;
1087 }
1088 }
1089 else
1090 {
1091 CONVERT_SETTINGS_DIALOG dlg( m_frame, &m_userSettings, false, false, false );
1092
1093 if( dlg.ShowModal() != wxID_OK )
1094 return true;
1095 }
1096
1097 for( EDA_ITEM* item : selection )
1098 {
1099 if( handleGraphicSeg( item ) )
1100 continue;
1101
1102 BOARD_ITEM& boardItem = static_cast<BOARD_ITEM&>( *item );
1103 SHAPE_POLY_SET polySet = getPolySet( item );
1104 std::vector<SEG> segs = getSegList( polySet );
1105
1106 std::optional<int> itemWidth = GetBoardItemWidth( boardItem );
1107
1108 if( aEvent.IsAction( &PCB_ACTIONS::convertToLines ) )
1109 {
1110 for( SEG& seg : segs )
1111 {
1112 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1113
1114 graphic->SetLayer( targetLayer );
1115 graphic->SetStart( VECTOR2I( seg.A ) );
1116 graphic->SetEnd( VECTOR2I( seg.B ) );
1117
1118 // The width can exist but be 0 for filled, unstroked shapes
1119 if( itemWidth && *itemWidth > 0 )
1120 graphic->SetWidth( *itemWidth );
1121
1122 commit.Add( graphic );
1123 }
1124 }
1125 else
1126 {
1127 // I am really unsure converting a polygon to "tracks" (i.e. segments on
1128 // copper layers) make sense for footprints, but anyway this code exists
1129 if( fpEditor )
1130 {
1131 // Creating segments on copper layer
1132 for( SEG& seg : segs )
1133 {
1134 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1135 graphic->SetLayer( targetLayer );
1136 graphic->SetStart( VECTOR2I( seg.A ) );
1137 graphic->SetEnd( VECTOR2I( seg.B ) );
1138
1139 if( itemWidth )
1140 graphic->SetWidth( *itemWidth );
1141
1142 commit.Add( graphic );
1143 }
1144 }
1145 else
1146 {
1147 // Creating tracks
1148 for( SEG& seg : segs )
1149 {
1150 PCB_TRACK* track = new PCB_TRACK( parent );
1151
1152 track->SetLayer( targetLayer );
1153 track->SetStart( VECTOR2I( seg.A ) );
1154 track->SetEnd( VECTOR2I( seg.B ) );
1155 commit.Add( track );
1156 }
1157 }
1158 }
1159 }
1160
1161 if( m_userSettings.m_DeleteOriginals )
1162 {
1163 PCB_SELECTION selectionCopy = selection;
1164 m_selectionTool->ClearSelection();
1165
1166 for( EDA_ITEM* item : selectionCopy )
1167 commit.Remove( item );
1168 }
1169
1170 commit.Push( _( "Create Lines" ) );
1171
1172 return 0;
1173}
1174
1175
1177{
1179 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1180 {
1181 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1182 {
1183 BOARD_ITEM* item = aCollector[i];
1184
1185 if( !( item->Type() == PCB_SHAPE_T ||
1186 item->Type() == PCB_TRACE_T ||
1187 item->Type() == PCB_ARC_T ) )
1188 {
1189 aCollector.Remove( item );
1190 }
1191 }
1192 } );
1193
1194 if( selection.Empty() || !selection.Front()->IsBOARD_ITEM() )
1195 return -1;
1196
1197 BOARD_ITEM* source = static_cast<BOARD_ITEM*>( selection.Front() );
1198 VECTOR2I start, end, mid;
1199
1200 // Offset the midpoint along the normal a little bit so that it's more obviously an arc
1201 const double offsetRatio = 0.1;
1202
1203 if( std::optional<SEG> seg = getStartEndPoints( source ) )
1204 {
1205 start = seg->A;
1206 end = seg->B;
1207
1208 VECTOR2I normal = ( seg->B - seg->A ).Perpendicular().Resize( offsetRatio * seg->Length() );
1209 mid = seg->Center() + normal;
1210 }
1211 else
1212 {
1213 return -1;
1214 }
1215
1216 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1217 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1218 PCB_LAYER_ID layer = source->GetLayer();
1219 BOARD_COMMIT commit( m_frame );
1220
1221 if( source->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( source )->GetShape() == SHAPE_T::SEGMENT )
1222 {
1223 PCB_SHAPE* line = static_cast<PCB_SHAPE*>( source );
1224 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1225
1226 VECTOR2I center = CalcArcCenter( start, mid, end );
1227
1228 arc->SetFilled( false );
1229 arc->SetLayer( layer );
1230 arc->SetStroke( line->GetStroke() );
1231
1232 arc->SetCenter( center );
1233 arc->SetStart( start );
1234 arc->SetEnd( end );
1235
1236 commit.Add( arc );
1237 }
1238 else if( source->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( source )->GetShape() == SHAPE_T::ARC )
1239 {
1240 PCB_SHAPE* source_arc = static_cast<PCB_SHAPE*>( source );
1241 PCB_ARC* arc = new PCB_ARC( parent );
1242
1243 arc->SetLayer( layer );
1244 arc->SetWidth( source_arc->GetWidth() );
1245 arc->SetStart( start );
1246 arc->SetMid( source_arc->GetArcMid() );
1247 arc->SetEnd( end );
1248
1249 commit.Add( arc );
1250 }
1251 else if( source->Type() == PCB_TRACE_T )
1252 {
1253 PCB_TRACK* line = static_cast<PCB_TRACK*>( source );
1254 PCB_ARC* arc = new PCB_ARC( parent );
1255
1256 arc->SetLayer( layer );
1257 arc->SetWidth( line->GetWidth() );
1258 arc->SetStart( start );
1259 arc->SetMid( mid );
1260 arc->SetEnd( end );
1261
1262 commit.Add( arc );
1263 }
1264 else if( source->Type() == PCB_ARC_T )
1265 {
1266 PCB_ARC* source_arc = static_cast<PCB_ARC*>( source );
1267 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1268
1269 arc->SetFilled( false );
1270 arc->SetLayer( layer );
1271 arc->SetWidth( source_arc->GetWidth() );
1272
1273 arc->SetArcGeometry( source_arc->GetStart(), source_arc->GetMid(), source_arc->GetEnd() );
1274 commit.Add( arc );
1275 }
1276
1277 commit.Push( _( "Create Arc" ) );
1278
1279 return 0;
1280}
1281
1282
1283std::optional<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem )
1284{
1285 switch( aItem->Type() )
1286 {
1287 case PCB_SHAPE_T:
1288 {
1289 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
1290
1291 switch( shape->GetShape() )
1292 {
1293 case SHAPE_T::SEGMENT:
1294 case SHAPE_T::ARC:
1295 case SHAPE_T::POLY:
1296 case SHAPE_T::BEZIER:
1297 if( shape->GetStart() == shape->GetEnd() )
1298 return std::nullopt;
1299
1300 return std::make_optional<SEG>( VECTOR2I( shape->GetStart() ),
1301 VECTOR2I( shape->GetEnd() ) );
1302
1303 default:
1304 return std::nullopt;
1305 }
1306 }
1307
1308 case PCB_TRACE_T:
1309 {
1310 PCB_TRACK* line = static_cast<PCB_TRACK*>( aItem );
1311 return std::make_optional<SEG>( VECTOR2I( line->GetStart() ), VECTOR2I( line->GetEnd() ) );
1312 }
1313
1314 case PCB_ARC_T:
1315 {
1316 PCB_ARC* arc = static_cast<PCB_ARC*>( aItem );
1317 return std::make_optional<SEG>( VECTOR2I( arc->GetStart() ), VECTOR2I( arc->GetEnd() ) );
1318 }
1319
1320 default:
1321 return std::nullopt;
1322 }
1323}
1324
1325
1327{
1328 PCB_BASE_EDIT_FRAME& frame = *getEditFrame<PCB_BASE_EDIT_FRAME>();
1330 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1331 {
1332 // Iterate from the back so we don't have to worry about removals.
1333 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1334 {
1335 BOARD_ITEM* item = aCollector[i];
1336
1337 // We've converted the polygon and rectangle to segments, so drop everything
1338 // that isn't a segment at this point
1339 if( !item->IsType( { PCB_PAD_T, PCB_SHAPE_T } ) )
1340 {
1341 aCollector.Remove( item );
1342 }
1343 }
1344 },
1345 true /* prompt user regarding locked items */ );
1346
1347 BOARD_COMMIT commit( this );
1348
1349 for( EDA_ITEM* item : selection )
1350 item->ClearFlags( STRUCT_DELETED );
1351
1352 // List of thing to select at the end of the operation
1353 // (doing it as we go will invalidate the iterator)
1354 std::vector<BOARD_ITEM*> items_to_select_on_success;
1355
1356 // Handle modifications to existing items by the routine
1357 // How to deal with this depends on whether we're in the footprint editor or not
1358 // and whether the item was conjured up by decomposing a polygon or rectangle
1359 auto item_modification_handler =
1360 [&]( BOARD_ITEM& aItem )
1361 {
1362 };
1363
1364 bool any_items_created = false;
1365
1366 auto item_creation_handler =
1367 [&]( std::unique_ptr<BOARD_ITEM> aItem )
1368 {
1369 any_items_created = true;
1370 items_to_select_on_success.push_back( aItem.get() );
1371 commit.Add( aItem.release() );
1372 };
1373
1374 auto item_removal_handler =
1375 [&]( BOARD_ITEM& aItem )
1376 {
1377 // If you do an outset on a FP pad, do you really want to delete
1378 // the parent?
1379 if( !aItem.GetParentFootprint() )
1380 {
1381 commit.Remove( &aItem );
1382 }
1383 };
1384
1385 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1386 ITEM_MODIFICATION_ROUTINE::CALLABLE_BASED_HANDLER change_handler( item_creation_handler,
1387 item_modification_handler,
1388 item_removal_handler );
1389
1390 // Persistent settings between dialog invocations
1391 // Init with some sensible defaults
1392 static OUTSET_ROUTINE::PARAMETERS outset_params_fp_edit{
1393 pcbIUScale.mmToIU( 0.25 ), // A common outset value
1394 false,
1395 false,
1396 true,
1397 F_CrtYd,
1399 pcbIUScale.mmToIU( 0.01 ),
1400 false,
1401 };
1402
1403 static OUTSET_ROUTINE::PARAMETERS outset_params_pcb_edit{
1404 pcbIUScale.mmToIU( 1 ),
1405 true,
1406 true,
1407 true,
1408 Edge_Cuts, // Outsets often for slots?
1410 std::nullopt,
1411 false,
1412 };
1413
1414 OUTSET_ROUTINE::PARAMETERS& outset_params = IsFootprintEditor() ? outset_params_fp_edit
1415 : outset_params_pcb_edit;
1416
1417 {
1418 DIALOG_OUTSET_ITEMS dlg( frame, outset_params );
1419 if( dlg.ShowModal() == wxID_CANCEL )
1420 {
1421 return 0;
1422 }
1423 }
1424
1425 OUTSET_ROUTINE outset_routine( frame.GetModel(), change_handler, outset_params );
1426
1427 for( EDA_ITEM* item : selection )
1428 {
1429 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
1430 outset_routine.ProcessItem( *board_item );
1431 }
1432
1433 // Deselect all the original items
1434 m_selectionTool->ClearSelection();
1435
1436 // Select added and modified items
1437 for( BOARD_ITEM* item : items_to_select_on_success )
1438 m_selectionTool->AddItemToSel( item, true );
1439
1440 if( any_items_created )
1441 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1442
1443 // Notify other tools of the changes
1444 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1445
1446 commit.Push( outset_routine.GetCommitDescription() );
1447
1448 if( const std::optional<wxString> msg = outset_routine.GetStatusMessage() )
1449 frame.ShowInfoBarMsg( *msg );
1450
1451 return 0;
1452}
1453
1454
1456{
1457 // clang-format off
1465 // clang-format on
1466}
@ ERROR_OUTSIDE
Definition: approximation.h:33
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:110
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
void SetTitle(const wxString &aTitle) override
Set title for the menu.
Definition: action_menu.cpp:92
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
Definition: action_menu.cpp:78
BASE_SET & reset(size_t pos)
Definition: base_set.h:143
BASE_SET & set(size_t pos)
Definition: base_set.h:116
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
Container for design settings for a BOARD object.
int GetLayerClass(PCB_LAYER_ID aLayer) const
int m_LineThickness[LAYER_CLASS_COUNT]
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
ZONE_SETTINGS & GetDefaultZoneSettings()
Abstract interface for BOARD_ITEMs capable of storing other items inside.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:78
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:229
virtual STROKE_PARAMS GetStroke() const
Definition: board_item.cpp:87
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:280
virtual bool HasLineStroke() const
Check if this item has line stoke properties.
Definition: board_item.h:219
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:463
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:955
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:82
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition: collector.h:110
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition: commit.h:92
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition: commit.h:80
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
CONVERT_SETTINGS * m_settings
bool TransferDataToWindow() override
wxRadioButton * m_rbBoundingHull
wxStaticText * m_gapLabel
wxRadioButton * m_rbMimicLineWidth
wxRadioButton * m_rbCenterline
void onRadioButton(wxCommandEvent &aEvent)
wxStaticText * m_gapUnits
bool TransferDataFromWindow() override
wxCheckBox * m_cbDeleteOriginals
CONVERT_SETTINGS_DIALOG(EDA_DRAW_FRAME *aParent, CONVERT_SETTINGS *aSettings, bool aShowCopyLineWidthOption, bool aShowCenterlineOption, bool aShowBoundingHullOption)
wxStaticText * m_widthLabel
wxStaticText * m_widthUnits
int CreateLines(const TOOL_EVENT &aEvent)
Convert selected polygon-like object to graphic lines, if possible.
bool Init() override
Init() is called once upon a registration of the tool.
int SegmentToArc(const TOOL_EVENT &aEvent)
Convert selected segment (graphic or track) to an arc of the same type.
void initUserSettings()
Initialize the user settings for the tool.
SHAPE_POLY_SET makePolysFromChainedSegs(const std::deque< EDA_ITEM * > &aItems, CONVERT_STRATEGY aStrategy)
Try to make polygons from chained segments in the selected items.
SHAPE_POLY_SET makePolysFromOpenGraphics(const std::deque< EDA_ITEM * > &aItems, int aGap)
Make polygons from graphic shapes and zones.
static std::optional< SEG > getStartEndPoints(EDA_ITEM *aItem)
Retrieve the start and end points for a generic item.
SHAPE_POLY_SET makePolysFromClosedGraphics(const std::deque< EDA_ITEM * > &aItems, CONVERT_STRATEGY aStrategy)
int CreatePolys(const TOOL_EVENT &aEvent)
Convert selected lines to a polygon, if possible.
PCB_SELECTION_TOOL * m_selectionTool
Definition: convert_tool.h:110
virtual ~CONVERT_TOOL()
CONVERT_SETTINGS m_userSettings
Definition: convert_tool.h:113
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
CONDITIONAL_MENU * m_menu
Definition: convert_tool.h:111
PCB_BASE_FRAME * m_frame
Definition: convert_tool.h:112
int OutsetItems(const TOOL_EVENT &aEvent)
Convert selected items to outset versions of themselves.
DIALOG_OUTSET_ITEMS, derived from DIALOG_OUTSET_ITEMS_BASE, created by wxFormBuilder.
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:52
void SetupStandardButtons(std::map< int, wxString > aLabels={})
std::string m_hash_key
Definition: dialog_shim.h:194
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
int ShowModal() override
void ShowInfoBarMsg(const wxString &aMsg, bool aShowCloseButton=false)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an info icon on the left of...
bool IsType(FRAME_T aType) const
The base class for create windows for drawing purpose.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:96
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:256
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:138
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:108
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition: eda_item.h:188
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:141
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:964
FILL_T GetFillMode() const
Definition: eda_shape.h:142
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:337
int GetRadius() const
Definition: eda_shape.cpp:1013
SHAPE_T GetShape() const
Definition: eda_shape.h:168
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:345
virtual void SetFilled(bool aFlag)
Definition: eda_shape.h:136
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:215
bool IsClosed() const
Definition: eda_shape.cpp:516
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:177
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:173
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:167
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition: eda_shape.h:320
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:219
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:1046
void SetWidth(int aWidth)
Definition: eda_shape.cpp:2372
void SetFillMode(FILL_T aFill)
Definition: eda_shape.cpp:547
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:983
static const TOOL_EVENT SelectedEvent
Definition: actions.h:341
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:348
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:202
A handler that is based on a set of callbacks provided by the user of the ITEM_MODIFICATION_ROUTINE.
bool IsBOARD_ITEM() const
Definition: view_item.h:102
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition: lset.cpp:613
Definition: pad.h:54
static TOOL_ACTION convertToKeepout
Definition: pcb_actions.h:578
static TOOL_ACTION convertToTracks
Definition: pcb_actions.h:581
static TOOL_ACTION convertToLines
Definition: pcb_actions.h:579
static TOOL_ACTION convertToZone
Definition: pcb_actions.h:577
static TOOL_ACTION convertToPoly
Definition: pcb_actions.h:576
static TOOL_ACTION outsetItems
Create outset items from selection.
Definition: pcb_actions.h:158
static TOOL_ACTION convertToArc
Definition: pcb_actions.h:580
static TOOL_ACTION createArray
Tool for creating an array of objects.
Definition: pcb_actions.h:495
void SetMid(const VECTOR2I &aMid)
Definition: pcb_track.h:343
const VECTOR2I & GetMid() const
Definition: pcb_track.h:344
std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: pcb_track.cpp:2230
Common, abstract interface for edit frames.
virtual PCB_LAYER_ID GetActiveLayer() const
BOARD * GetBoard() const
virtual BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Return the BOARD_DESIGN_SETTINGS for the open project.
virtual BOARD_ITEM_CONTAINER * GetModel() const =0
PCB_LAYER_ID SelectOneLayer(PCB_LAYER_ID aDefaultLayer, const LSET &aNotAllowedLayersMask=LSET(), wxPoint aDlgPosition=wxDefaultPosition)
Show the dialog box for a layer selection.
Definition: sel_layer.cpp:302
static SELECTION_CONDITION SameLayer()
Creates a functor that tests if selection contains items that belong exclusively to the same layer.
The selection tool: currently supports:
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter, bool aConfirmLockedItems=false)
Return the current selection, filtered according to aClientFilter.
int ClearSelection(const TOOL_EVENT &aEvent)
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:81
int GetWidth() const override
Definition: pcb_shape.cpp:380
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:177
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the shape to a closed polygon.
Definition: pcb_shape.cpp:848
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:91
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:92
T * frame() const
BOARD * board() const
const PCB_SELECTION & selection() const
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:148
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:151
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the track shape to a closed polygon.
Definition: pcb_track.cpp:2246
const VECTOR2I & GetStart() const
Definition: pcb_track.h:152
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:149
virtual void SetWidth(int aWidth)
Definition: pcb_track.h:145
virtual int GetWidth() const
Definition: pcb_track.h:146
Definition: seg.h:42
static SEG::ecoord Square(int a)
Definition: seg.h:123
Class that groups generic conditions for selected items.
static SELECTION_CONDITION MoreThan(int aNumber)
Create a functor that tests if the number of selected items is greater than the value given as parame...
static SELECTION_CONDITION Count(int aNumber)
Create a functor that tests if the number of selected items is equal to the value given as parameter.
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
const std::deque< EDA_ITEM * > GetItems() const
Definition: selection.h:121
virtual void Remove(EDA_ITEM *aItem)
Definition: selection.cpp:60
EDA_ITEM * Front() const
Definition: selection.h:172
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:110
SHAPE_ARC Reversed() const
Definition: shape_arc.cpp:1005
const VECTOR2I & GetP0() const
Definition: shape_arc.h:116
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
virtual const VECTOR2I GetPoint(int aIndex) const override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
void Insert(size_t aVertex, const VECTOR2I &aP)
Represent a set of closed polygons.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Simple container to manage line stroke parameters.
Definition: stroke_params.h:94
int GetWidth() const
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:220
Generic, UI-independent tool event.
Definition: tool_event.h:168
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
Definition: tool_event.cpp:82
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_MENU & GetToolMenu()
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
int GetIntValue()
Definition: unit_binder.h:129
void Enable(bool aEnable)
Enable/disable the label, widget and units label.
virtual void SetValue(long long int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
void Show(bool aShow, bool aResize=false)
Show/hide the label, widget and units label.
ZONE_SETTINGS handles zones parameters.
Definition: zone_settings.h:88
void SetIsRuleArea(bool aEnable)
void ExportSetting(ZONE &aTarget, bool aFullExport=true) const
Function ExportSetting copy settings to a given zone.
wxString m_Name
Handle a list of polygons defining a copper zone.
Definition: zone.h:74
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:1258
SHAPE_POLY_SET * Outline()
Definition: zone.h:368
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:194
This file is part of the common library.
int InvokeCopperZonesEditor(PCB_BASE_FRAME *aCaller, ZONE_SETTINGS *aSettings, CONVERT_SETTINGS *aConvertSettings)
Function InvokeCopperZonesEditor invokes up a modal dialog window for copper zone editing.
int InvokeNonCopperZonesEditor(PCB_BASE_FRAME *aParent, ZONE_SETTINGS *aSettings, CONVERT_SETTINGS *aConvertSettings)
Function InvokeNonCopperZonesEditor invokes up a modal dialog window for non-copper zone editing.
int InvokeRuleAreaEditor(PCB_BASE_FRAME *aCaller, ZONE_SETTINGS *aZoneSettings, BOARD *aBoard, CONVERT_SETTINGS *aConvertSettings)
Function InvokeRuleAreaEditor invokes up a modal dialog window for copper zone editing.
#define _(s)
#define STRUCT_DELETED
flag indication structures to be erased
#define SKIP_STRUCT
flag indicating that the structure should be ignored
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
FILL_T
Definition: eda_shape.h:56
static const std::vector< KICAD_T > padTypes
Definition: edit_tool.cpp:79
static const std::vector< KICAD_T > trackTypes
Definition: edit_tool.cpp:85
@ FRAME_PCB_EDITOR
Definition: frame_type.h:42
@ FRAME_FOOTPRINT_EDITOR
Definition: frame_type.h:43
bool IsNonCopperLayer(int aLayerId)
Test whether a layer is a non copper layer.
Definition: layer_ids.h:696
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:663
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_CrtYd
Definition: layer_ids.h:116
@ Edge_Cuts
Definition: layer_ids.h:112
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ F_Cu
Definition: layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
std::optional< int > GetBoardItemWidth(const BOARD_ITEM &aItem)
Gets the width of a BOARD_ITEM, for items that have a meaningful width.
Utility functions that can be shared by PCB tools.
CONVERT_STRATEGY
@ COPY_LINEWIDTH
@ CENTERLINE
@ BOUNDING_HULL
static PGM_BASE * process
Definition: pgm_base.cpp:1068
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:429
CONVERT_STRATEGY m_Strategy
constexpr int mmToIU(double mm) const
Definition: base_units.h:90
VECTOR2I center
VECTOR2I end
const VECTOR2I CalcArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition: trigo.cpp:521
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:107
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition: typeinfo.h:90
@ PCB_SHAPE_LOCATE_CIRCLE_T
Definition: typeinfo.h:135
@ PCB_SHAPE_LOCATE_SEGMENT_T
Definition: typeinfo.h:133
@ PCB_SHAPE_LOCATE_RECT_T
Definition: typeinfo.h:134
@ PCB_SHAPE_LOCATE_BEZIER_T
Definition: typeinfo.h:138
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_SHAPE_LOCATE_POLY_T
Definition: typeinfo.h:137
@ PCB_SHAPE_LOCATE_ARC_T
Definition: typeinfo.h:136
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695