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->Type() == PCB_ARC_T
655 || ( aItem->Type() == PCB_SHAPE_T
656 && static_cast<PCB_SHAPE*>( aItem )->GetShape() == SHAPE_T::ARC ) )
657 {
658 SHAPE_ARC arc;
659
660 if( aItem->Type() == PCB_ARC_T )
661 {
662 PCB_ARC* pcb_arc = static_cast<PCB_ARC*>( aItem );
663 arc = *static_cast<SHAPE_ARC*>( pcb_arc->GetEffectiveShape().get() );
664 }
665 else
666 {
667 PCB_SHAPE* pcb_shape = static_cast<PCB_SHAPE*>( aItem );
668 arc = SHAPE_ARC( pcb_shape->GetStart(), pcb_shape->GetArcMid(),
669 pcb_shape->GetEnd(), pcb_shape->GetWidth() );
670 }
671
672 if( aDirection )
673 outline.Append( aAnchor == arc.GetP0() ? arc : arc.Reversed() );
674 else
675 outline.Insert( 0, aAnchor == arc.GetP0() ? arc : arc.Reversed() );
676
677 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
678 }
679 else if( aItem->IsType( { PCB_SHAPE_LOCATE_BEZIER_T } ) )
680 {
681 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
682
683 if( aAnchor == graphic->GetStart() )
684 {
685 for( auto it = graphic->GetBezierPoints().begin();
686 it != graphic->GetBezierPoints().end();
687 ++it )
688 {
689 if( aDirection )
690 outline.Append( *it );
691 else
692 outline.Insert( 0, *it );
693 }
694
695 }
696 else
697 {
698 for( auto it = graphic->GetBezierPoints().rbegin();
699 it != graphic->GetBezierPoints().rend();
700 ++it )
701 {
702 if( aDirection )
703 outline.Append( *it );
704 else
705 outline.Insert( 0, *it );
706 }
707 }
708
709 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
710 }
711 else if( std::optional<SEG> nextSeg = getStartEndPoints( aItem ) )
712 {
713 VECTOR2I& point = ( aAnchor == nextSeg->A ) ? nextSeg->B : nextSeg->A;
714
715 if( aDirection )
716 outline.Append( point );
717 else
718 outline.Insert( 0, point );
719
720 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
721 }
722 };
723
724 // aDirection == true for walking "right" and appending to the end of points
725 // false for walking "left" and prepending to the beginning
726 std::function<void( EDA_ITEM*, const VECTOR2I&, bool )> process =
727 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
728 {
729 if( aItem->GetFlags() & SKIP_STRUCT )
730 return;
731
732 aItem->SetFlags( SKIP_STRUCT );
733
734 insert( aItem, aAnchor, aDirection );
735
736 std::optional<SEG> anchors = getStartEndPoints( aItem );
737 wxASSERT( anchors );
738
739 VECTOR2I nextAnchor = ( aAnchor == anchors->A ) ? anchors->B : anchors->A;
740
741 for( std::pair<int, EDA_ITEM*> pair : connections[nextAnchor] )
742 {
743 if( pair.second == aItem )
744 continue;
745
746 process( pair.second, nextAnchor, aDirection );
747 }
748 };
749
750 std::optional<SEG> anchors = getStartEndPoints( candidate );
751 wxASSERT( anchors );
752
753 // Start with the first object and walk "right"
754 // Note if the first object is an arc, we don't need to insert its first point here, the
755 // whole arc will be inserted at anchor B inside process()
756 if( !( candidate->Type() == PCB_ARC_T
757 || ( candidate->Type() == PCB_SHAPE_T
758 && static_cast<PCB_SHAPE*>( candidate )->GetShape() == SHAPE_T::ARC ) ) )
759 {
760 insert( candidate, anchors->A, true );
761 }
762
763 process( candidate, anchors->B, true );
764
765 // check for any candidates on the "left"
766 EDA_ITEM* left = nullptr;
767
768 for( std::pair<int, EDA_ITEM*> possibleLeft : connections[anchors->A] )
769 {
770 if( possibleLeft.second != candidate )
771 {
772 left = possibleLeft.second;
773 break;
774 }
775 }
776
777 if( left )
778 process( left, anchors->A, false );
779
780 if( outline.PointCount() < 3
781 || !closeEnough( outline.GetPoint( 0 ), outline.GetPoint( -1 ), chainingEpsilon ) )
782 {
783 for( EDA_ITEM* item : insertedItems )
784 item->ClearFlags( SKIP_STRUCT );
785
786 continue;
787 }
788
789 outline.SetClosed( true );
790
791 poly.AddOutline( outline );
792
793 if( aStrategy == BOUNDING_HULL )
794 {
795 for( BOARD_ITEM* item : insertedItems )
796 {
797 item->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError,
798 ERROR_INSIDE, false );
799 }
800 }
801
802 insertedItems.clear();
803 }
804
805 return poly;
806}
807
808
809SHAPE_POLY_SET CONVERT_TOOL::makePolysFromOpenGraphics( const std::deque<EDA_ITEM*>& aItems,
810 int aGap )
811{
813 SHAPE_POLY_SET poly;
814
815 for( EDA_ITEM* item : aItems )
816 {
817 if( item->GetFlags() & SKIP_STRUCT )
818 continue;
819
820 switch( item->Type() )
821 {
822 case PCB_SHAPE_T:
823 {
824 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
825
826 if( shape->IsClosed() )
827 continue;
828
829 shape->TransformShapeToPolygon( poly, UNDEFINED_LAYER, aGap, bds.m_MaxError,
830 ERROR_INSIDE, false );
831 shape->SetFlags( SKIP_STRUCT );
832
833 break;
834 }
835
836 case PCB_TRACE_T:
837 case PCB_ARC_T:
838 case PCB_VIA_T:
839 {
840 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
841
842 track->TransformShapeToPolygon( poly, UNDEFINED_LAYER, aGap, bds.m_MaxError,
843 ERROR_INSIDE, false );
844 track->SetFlags( SKIP_STRUCT );
845
846 break;
847 }
848
849 default:
850 continue;
851 }
852 }
853
854 return poly;
855}
856
857
859 CONVERT_STRATEGY aStrategy )
860{
862 SHAPE_POLY_SET poly;
863
864 for( EDA_ITEM* item : aItems )
865 {
866 if( item->GetFlags() & SKIP_STRUCT )
867 continue;
868
869 switch( item->Type() )
870 {
871 case PCB_SHAPE_T:
872 {
873 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
874 FILL_T wasFilled = shape->GetFillMode();
875
876 if( !shape->IsClosed() )
877 continue;
878
879 if( aStrategy != BOUNDING_HULL )
880 shape->SetFilled( true );
881
883 aStrategy == COPY_LINEWIDTH
884 || aStrategy == CENTERLINE );
885
886 if( aStrategy != BOUNDING_HULL )
887 shape->SetFillMode( wasFilled );
888
889 shape->SetFlags( SKIP_STRUCT );
890 break;
891 }
892
893 case PCB_ZONE_T:
894 poly.Append( *static_cast<ZONE*>( item )->Outline() );
895 item->SetFlags( SKIP_STRUCT );
896 break;
897
898 case PCB_FIELD_T:
899 case PCB_TEXT_T:
900 {
901 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
902 text->TransformTextToPolySet( poly, 0, bds.m_MaxError, ERROR_INSIDE );
903 text->SetFlags( SKIP_STRUCT );
904 break;
905 }
906
907 case PCB_PAD_T:
908 {
909 PAD* pad = static_cast<PAD*>( item );
910 pad->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError, ERROR_INSIDE,
911 false );
912 pad->SetFlags( SKIP_STRUCT );
913 break;
914 }
915
916
917 default:
918 continue;
919 }
920 }
921
922 return poly;
923}
924
925
927{
929 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
930 {
931 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
932 {
933 BOARD_ITEM* item = aCollector[i];
934
935 switch( item->Type() )
936 {
937 case PCB_SHAPE_T:
938 switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
939 {
940 case SHAPE_T::SEGMENT:
941 case SHAPE_T::ARC:
942 case SHAPE_T::POLY:
943 case SHAPE_T::RECTANGLE:
944 break;
945
946 default:
947 aCollector.Remove( item );
948 }
949
950 break;
951
952 case PCB_ZONE_T:
953 break;
954
955 default:
956 aCollector.Remove( item );
957 }
958 }
959 } );
960
961 if( selection.Empty() )
962 return 0;
963
964 auto getPolySet =
965 []( EDA_ITEM* aItem )
966 {
967 SHAPE_POLY_SET set;
968
969 switch( aItem->Type() )
970 {
971 case PCB_ZONE_T:
972 set = *static_cast<ZONE*>( aItem )->Outline();
973 break;
974
975 case PCB_SHAPE_T:
976 {
977 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
978
979 if( graphic->GetShape() == SHAPE_T::POLY )
980 {
981 set = graphic->GetPolyShape();
982 }
983 else if( graphic->GetShape() == SHAPE_T::RECTANGLE )
984 {
985 SHAPE_LINE_CHAIN outline;
986 VECTOR2I start( graphic->GetStart() );
987 VECTOR2I end( graphic->GetEnd() );
988
989 outline.Append( start );
990 outline.Append( VECTOR2I( end.x, start.y ) );
991 outline.Append( end );
992 outline.Append( VECTOR2I( start.x, end.y ) );
993 outline.SetClosed( true );
994
995 set.AddOutline( outline );
996 }
997 else
998 {
999 wxFAIL_MSG( wxT( "Unhandled graphic shape type in PolyToLines - getPolySet" ) );
1000 }
1001 break;
1002 }
1003
1004 default:
1005 wxFAIL_MSG( wxT( "Unhandled type in PolyToLines - getPolySet" ) );
1006 break;
1007 }
1008
1009 return set;
1010 };
1011
1012 auto getSegList =
1013 []( SHAPE_POLY_SET& aPoly )
1014 {
1015 std::vector<SEG> segs;
1016
1017 // Our input should be valid polys, so OK to assert here
1018 wxASSERT( aPoly.VertexCount() >= 2 );
1019
1020 for( int i = 1; i < aPoly.VertexCount(); i++ )
1021 segs.emplace_back( SEG( aPoly.CVertex( i - 1 ), aPoly.CVertex( i ) ) );
1022
1023 segs.emplace_back( SEG( aPoly.CVertex( aPoly.VertexCount() - 1 ),
1024 aPoly.CVertex( 0 ) ) );
1025
1026 return segs;
1027 };
1028
1029 BOARD_COMMIT commit( m_frame );
1030 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1031 FOOTPRINT_EDIT_FRAME* fpEditor = dynamic_cast<FOOTPRINT_EDIT_FRAME*>( m_frame );
1032 FOOTPRINT* footprint = nullptr;
1033 PCB_LAYER_ID targetLayer = m_frame->GetActiveLayer();
1034 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1035
1036 if( fpEditor )
1037 footprint = fpEditor->GetBoard()->GetFirstFootprint();
1038
1039 auto handleGraphicSeg =
1040 [&]( EDA_ITEM* aItem )
1041 {
1042 if( aItem->Type() != PCB_SHAPE_T )
1043 return false;
1044
1045 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
1046
1047 if( graphic->GetShape() == SHAPE_T::SEGMENT )
1048 {
1049 PCB_TRACK* track = new PCB_TRACK( parent );
1050
1051 track->SetLayer( targetLayer );
1052 track->SetStart( graphic->GetStart() );
1053 track->SetEnd( graphic->GetEnd() );
1054 track->SetWidth( graphic->GetWidth() );
1055 commit.Add( track );
1056
1057 return true;
1058 }
1059 else if( graphic->GetShape() == SHAPE_T::ARC )
1060 {
1061 PCB_ARC* arc = new PCB_ARC( parent );
1062
1063 arc->SetLayer( targetLayer );
1064 arc->SetStart( graphic->GetStart() );
1065 arc->SetEnd( graphic->GetEnd() );
1066 arc->SetMid( graphic->GetArcMid() );
1067 arc->SetWidth( graphic->GetWidth() );
1068 commit.Add( arc );
1069
1070 return true;
1071 }
1072
1073 return false;
1074 };
1075
1076 if( aEvent.IsAction( &PCB_ACTIONS::convertToTracks ) )
1077 {
1078 if( !IsCopperLayer( targetLayer ) )
1079 {
1080 targetLayer = frame->SelectOneLayer( F_Cu, LSET::AllNonCuMask() );
1081
1082 if( targetLayer == UNDEFINED_LAYER ) // User canceled
1083 return true;
1084 }
1085 }
1086 else
1087 {
1088 CONVERT_SETTINGS_DIALOG dlg( m_frame, &m_userSettings, false, false, false );
1089
1090 if( dlg.ShowModal() != wxID_OK )
1091 return true;
1092 }
1093
1094 for( EDA_ITEM* item : selection )
1095 {
1096 if( handleGraphicSeg( item ) )
1097 continue;
1098
1099 BOARD_ITEM& boardItem = static_cast<BOARD_ITEM&>( *item );
1100 SHAPE_POLY_SET polySet = getPolySet( item );
1101 std::vector<SEG> segs = getSegList( polySet );
1102
1103 std::optional<int> itemWidth = GetBoardItemWidth( boardItem );
1104
1105 if( aEvent.IsAction( &PCB_ACTIONS::convertToLines ) )
1106 {
1107 for( SEG& seg : segs )
1108 {
1109 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1110
1111 graphic->SetLayer( targetLayer );
1112 graphic->SetStart( VECTOR2I( seg.A ) );
1113 graphic->SetEnd( VECTOR2I( seg.B ) );
1114
1115 // The width can exist but be 0 for filled, unstroked shapes
1116 if( itemWidth && *itemWidth > 0 )
1117 graphic->SetWidth( *itemWidth );
1118
1119 commit.Add( graphic );
1120 }
1121 }
1122 else
1123 {
1124 // I am really unsure converting a polygon to "tracks" (i.e. segments on
1125 // copper layers) make sense for footprints, but anyway this code exists
1126 if( fpEditor )
1127 {
1128 // Creating segments on copper layer
1129 for( SEG& seg : segs )
1130 {
1131 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1132 graphic->SetLayer( targetLayer );
1133 graphic->SetStart( VECTOR2I( seg.A ) );
1134 graphic->SetEnd( VECTOR2I( seg.B ) );
1135
1136 if( itemWidth )
1137 graphic->SetWidth( *itemWidth );
1138
1139 commit.Add( graphic );
1140 }
1141 }
1142 else
1143 {
1144 // Creating tracks
1145 for( SEG& seg : segs )
1146 {
1147 PCB_TRACK* track = new PCB_TRACK( parent );
1148
1149 track->SetLayer( targetLayer );
1150 track->SetStart( VECTOR2I( seg.A ) );
1151 track->SetEnd( VECTOR2I( seg.B ) );
1152 commit.Add( track );
1153 }
1154 }
1155 }
1156 }
1157
1158 if( m_userSettings.m_DeleteOriginals )
1159 {
1160 PCB_SELECTION selectionCopy = selection;
1161 m_selectionTool->ClearSelection();
1162
1163 for( EDA_ITEM* item : selectionCopy )
1164 commit.Remove( item );
1165 }
1166
1167 commit.Push( _( "Create Lines" ) );
1168
1169 return 0;
1170}
1171
1172
1174{
1176 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1177 {
1178 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1179 {
1180 BOARD_ITEM* item = aCollector[i];
1181
1182 if( !( item->Type() == PCB_SHAPE_T ||
1183 item->Type() == PCB_TRACE_T ||
1184 item->Type() == PCB_ARC_T ) )
1185 {
1186 aCollector.Remove( item );
1187 }
1188 }
1189 } );
1190
1191 if( selection.Empty() || !selection.Front()->IsBOARD_ITEM() )
1192 return -1;
1193
1194 BOARD_ITEM* source = static_cast<BOARD_ITEM*>( selection.Front() );
1195 VECTOR2I start, end, mid;
1196
1197 // Offset the midpoint along the normal a little bit so that it's more obviously an arc
1198 const double offsetRatio = 0.1;
1199
1200 if( std::optional<SEG> seg = getStartEndPoints( source ) )
1201 {
1202 start = seg->A;
1203 end = seg->B;
1204
1205 VECTOR2I normal = ( seg->B - seg->A ).Perpendicular().Resize( offsetRatio * seg->Length() );
1206 mid = seg->Center() + normal;
1207 }
1208 else
1209 {
1210 return -1;
1211 }
1212
1213 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1214 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1215 PCB_LAYER_ID layer = source->GetLayer();
1216 BOARD_COMMIT commit( m_frame );
1217
1218 if( source->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( source )->GetShape() == SHAPE_T::SEGMENT )
1219 {
1220 PCB_SHAPE* line = static_cast<PCB_SHAPE*>( source );
1221 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1222
1223 VECTOR2I center = CalcArcCenter( start, mid, end );
1224
1225 arc->SetFilled( false );
1226 arc->SetLayer( layer );
1227 arc->SetStroke( line->GetStroke() );
1228
1229 arc->SetCenter( center );
1230 arc->SetStart( start );
1231 arc->SetEnd( end );
1232
1233 commit.Add( arc );
1234 }
1235 else if( source->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( source )->GetShape() == SHAPE_T::ARC )
1236 {
1237 PCB_SHAPE* source_arc = static_cast<PCB_SHAPE*>( source );
1238 PCB_ARC* arc = new PCB_ARC( parent );
1239
1240 arc->SetLayer( layer );
1241 arc->SetWidth( source_arc->GetWidth() );
1242 arc->SetStart( start );
1243 arc->SetMid( source_arc->GetArcMid() );
1244 arc->SetEnd( end );
1245
1246 commit.Add( arc );
1247 }
1248 else if( source->Type() == PCB_TRACE_T )
1249 {
1250 PCB_TRACK* line = static_cast<PCB_TRACK*>( source );
1251 PCB_ARC* arc = new PCB_ARC( parent );
1252
1253 arc->SetLayer( layer );
1254 arc->SetWidth( line->GetWidth() );
1255 arc->SetStart( start );
1256 arc->SetMid( mid );
1257 arc->SetEnd( end );
1258
1259 commit.Add( arc );
1260 }
1261 else if( source->Type() == PCB_ARC_T )
1262 {
1263 PCB_ARC* source_arc = static_cast<PCB_ARC*>( source );
1264 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1265
1266 arc->SetFilled( false );
1267 arc->SetLayer( layer );
1268 arc->SetWidth( source_arc->GetWidth() );
1269
1270 arc->SetArcGeometry( source_arc->GetStart(), source_arc->GetMid(), source_arc->GetEnd() );
1271 commit.Add( arc );
1272 }
1273
1274 commit.Push( _( "Create Arc" ) );
1275
1276 return 0;
1277}
1278
1279
1280std::optional<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem )
1281{
1282 switch( aItem->Type() )
1283 {
1284 case PCB_SHAPE_T:
1285 {
1286 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
1287
1288 switch( shape->GetShape() )
1289 {
1290 case SHAPE_T::SEGMENT:
1291 case SHAPE_T::ARC:
1292 case SHAPE_T::POLY:
1293 case SHAPE_T::BEZIER:
1294 if( shape->GetStart() == shape->GetEnd() )
1295 return std::nullopt;
1296
1297 return std::make_optional<SEG>( VECTOR2I( shape->GetStart() ),
1298 VECTOR2I( shape->GetEnd() ) );
1299
1300 default:
1301 return std::nullopt;
1302 }
1303 }
1304
1305 case PCB_TRACE_T:
1306 {
1307 PCB_TRACK* line = static_cast<PCB_TRACK*>( aItem );
1308 return std::make_optional<SEG>( VECTOR2I( line->GetStart() ), VECTOR2I( line->GetEnd() ) );
1309 }
1310
1311 case PCB_ARC_T:
1312 {
1313 PCB_ARC* arc = static_cast<PCB_ARC*>( aItem );
1314 return std::make_optional<SEG>( VECTOR2I( arc->GetStart() ), VECTOR2I( arc->GetEnd() ) );
1315 }
1316
1317 default:
1318 return std::nullopt;
1319 }
1320}
1321
1322
1324{
1325 PCB_BASE_EDIT_FRAME& frame = *getEditFrame<PCB_BASE_EDIT_FRAME>();
1327 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1328 {
1329 // Iterate from the back so we don't have to worry about removals.
1330 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1331 {
1332 BOARD_ITEM* item = aCollector[i];
1333
1334 // We've converted the polygon and rectangle to segments, so drop everything
1335 // that isn't a segment at this point
1336 if( !item->IsType( { PCB_PAD_T, PCB_SHAPE_T } ) )
1337 {
1338 aCollector.Remove( item );
1339 }
1340 }
1341 },
1342 true /* prompt user regarding locked items */ );
1343
1344 BOARD_COMMIT commit( this );
1345
1346 for( EDA_ITEM* item : selection )
1347 item->ClearFlags( STRUCT_DELETED );
1348
1349 // List of thing to select at the end of the operation
1350 // (doing it as we go will invalidate the iterator)
1351 std::vector<BOARD_ITEM*> items_to_select_on_success;
1352
1353 // Handle modifications to existing items by the routine
1354 // How to deal with this depends on whether we're in the footprint editor or not
1355 // and whether the item was conjured up by decomposing a polygon or rectangle
1356 auto item_modification_handler = [&]( BOARD_ITEM& aItem )
1357 {
1358 };
1359
1360 bool any_items_created = false;
1361 auto item_creation_handler = [&]( std::unique_ptr<BOARD_ITEM> aItem )
1362 {
1363 any_items_created = true;
1364 items_to_select_on_success.push_back( aItem.get() );
1365 commit.Add( aItem.release() );
1366 };
1367
1368 auto item_removal_handler = [&]( BOARD_ITEM& aItem )
1369 {
1370 // If you do an outset on a FP pad, do you really want to delete
1371 // the parent?
1372 if( !aItem.GetParentFootprint() )
1373 {
1374 commit.Remove( &aItem );
1375 }
1376 };
1377
1378 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1380 item_creation_handler, item_modification_handler, item_removal_handler );
1381
1382 // Persistent settings between dialog invocations
1383 // Init with some sensible defaults
1384 static OUTSET_ROUTINE::PARAMETERS outset_params_fp_edit{
1385 pcbIUScale.mmToIU( 0.25 ), // A common outset value
1386 false,
1387 false,
1388 true,
1389 F_CrtYd,
1391 pcbIUScale.mmToIU( 0.01 ),
1392 false,
1393 };
1394
1395 static OUTSET_ROUTINE::PARAMETERS outset_params_pcb_edit{
1396 pcbIUScale.mmToIU( 1 ),
1397 true,
1398 true,
1399 true,
1400 Edge_Cuts, // Outsets often for slots?
1402 std::nullopt,
1403 false,
1404 };
1405
1406 OUTSET_ROUTINE::PARAMETERS& outset_params =
1407 IsFootprintEditor() ? outset_params_fp_edit : outset_params_pcb_edit;
1408
1409 {
1410 DIALOG_OUTSET_ITEMS dlg( frame, outset_params );
1411 if( dlg.ShowModal() == wxID_CANCEL )
1412 {
1413 return 0;
1414 }
1415 }
1416
1417 OUTSET_ROUTINE outset_routine( frame.GetModel(), change_handler, outset_params );
1418
1419 for( EDA_ITEM* item : selection )
1420 {
1421 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
1422 outset_routine.ProcessItem( *board_item );
1423 }
1424
1425 // Deselect all the original items
1426 m_selectionTool->ClearSelection();
1427
1428 // Select added and modified items
1429 for( BOARD_ITEM* item : items_to_select_on_success )
1430 m_selectionTool->AddItemToSel( item, true );
1431
1432 if( any_items_created )
1433 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1434
1435 // Notify other tools of the changes
1436 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1437
1438 commit.Push( outset_routine.GetCommitDescription() );
1439
1440 if( const std::optional<wxString> msg = outset_routine.GetStatusMessage() )
1441 frame.ShowInfoBarMsg( *msg );
1442
1443 return 0;
1444}
1445
1446
1448{
1449 // clang-format off
1457 // clang-format on
1458}
@ ERROR_OUTSIDE
Definition: approximation.h:33
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
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:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:239
virtual STROKE_PARAMS GetStroke() const
Definition: board_item.cpp:86
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:290
virtual bool HasLineStroke() const
Check if this item has line stoke properties.
Definition: board_item.h:229
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:456
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:934
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition: collector.h:109
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:88
void SetupStandardButtons(std::map< int, wxString > aLabels={})
std::string m_hash_key
Definition: dialog_shim.h:230
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:89
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:244
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:127
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition: eda_item.h:176
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:130
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:792
FILL_T GetFillMode() const
Definition: eda_shape.h:114
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:291
SHAPE_T GetShape() const
Definition: eda_shape.h:132
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:299
virtual void SetFilled(bool aFlag)
Definition: eda_shape.h:108
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:174
bool IsClosed() const
Definition: eda_shape.cpp:505
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:141
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:137
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:131
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition: eda_shape.h:274
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:178
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:873
void SetWidth(int aWidth)
Definition: eda_shape.h:121
void SetFillMode(FILL_T aFill)
Definition: eda_shape.h:113
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:810
static const TOOL_EVENT SelectedEvent
Definition: actions.h:292
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:299
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:573
Definition: pad.h:54
static TOOL_ACTION convertToKeepout
Definition: pcb_actions.h:583
static TOOL_ACTION convertToTracks
Definition: pcb_actions.h:586
static TOOL_ACTION convertToLines
Definition: pcb_actions.h:584
static TOOL_ACTION convertToZone
Definition: pcb_actions.h:582
static TOOL_ACTION convertToPoly
Definition: pcb_actions.h:581
static TOOL_ACTION outsetItems
Create outset items from selection.
Definition: pcb_actions.h:170
static TOOL_ACTION convertToArc
Definition: pcb_actions.h:585
static TOOL_ACTION createArray
Tool for creating an array of objects.
Definition: pcb_actions.h:493
void SetMid(const VECTOR2I &aMid)
Definition: pcb_track.h:304
const VECTOR2I & GetMid() const
Definition: pcb_track.h:305
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:1974
Common, abstract interface for edit frames.
PCB_LAYER_ID SelectOneLayer(PCB_LAYER_ID aDefaultLayer, LSET aNotAllowedLayersMask=LSET(), wxPoint aDlgPosition=wxDefaultPosition)
Show the dialog box for a layer selection.
Definition: sel_layer.cpp:302
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
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)
int GetWidth() const override
Definition: pcb_shape.cpp:301
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:170
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:771
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:89
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:90
T * frame() const
BOARD * board() const
const PCB_SELECTION & selection() const
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:118
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:121
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:1990
const VECTOR2I & GetStart() const
Definition: pcb_track.h:122
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:119
virtual void SetWidth(int aWidth)
Definition: pcb_track.h:115
virtual int GetWidth() const
Definition: pcb_track.h:116
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:1006
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 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:93
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:78
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:73
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:1143
SHAPE_POLY_SET * Outline()
Definition: zone.h:340
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:195
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:614
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:581
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:1070
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:398
CONVERT_STRATEGY m_Strategy
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
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