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 (C) 1992-2023 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> segmentTypes = { PCB_TRACE_T,
267 static const std::vector<KICAD_T> shapeTypes = { PCB_SHAPE_LOCATE_SEGMENT_T,
273 PCB_TEXT_T };
274 static const std::vector<KICAD_T> trackTypes = { PCB_TRACE_T,
275 PCB_ARC_T,
276 PCB_VIA_T };
277 static const std::vector<KICAD_T> toTrackTypes = { PCB_SHAPE_LOCATE_SEGMENT_T,
279 static const std::vector<KICAD_T> polyTypes = { PCB_ZONE_T,
282 static const std::vector<KICAD_T> outsetTypes = { PCB_PAD_T, PCB_SHAPE_T };
283
284 auto shapes = S_C::OnlyTypes( shapeTypes ) && P_S_C::SameLayer();
285 auto graphicToTrack = S_C::OnlyTypes( toTrackTypes );
286 auto anyTracks = S_C::MoreThan( 0 ) && S_C::OnlyTypes( trackTypes ) && P_S_C::SameLayer();
287 auto anyPolys = S_C::OnlyTypes( polyTypes );
288 auto anyPads = S_C::OnlyTypes( padTypes );
289
290 auto canCreateArcs = S_C::Count( 1 ) && S_C::OnlyTypes( segmentTypes );
291 auto canCreateArray = S_C::MoreThan( 0 );
292 auto canCreatePoly = shapes || anyPolys || anyTracks;
293
294 auto canCreateOutset = S_C::OnlyTypes( outsetTypes );
295
297 canCreatePoly = shapes || anyPolys || anyTracks || anyPads;
298
299 auto canCreateLines = anyPolys;
300 auto canCreateTracks = anyPolys || graphicToTrack;
301 auto canCreate = canCreatePoly
302 || canCreateLines
303 || canCreateTracks
304 || canCreateArcs
305 || canCreateArray
306 || canCreateOutset;
307
308 m_menu->AddItem( PCB_ACTIONS::convertToPoly, canCreatePoly );
309
311 m_menu->AddItem( PCB_ACTIONS::convertToZone, canCreatePoly );
312
314 m_menu->AddItem( PCB_ACTIONS::convertToLines, canCreateLines );
315 m_menu->AddItem( PCB_ACTIONS::outsetItems, canCreateOutset );
317
318 // Currently the code exists, but tracks are not really existing in footprints
319 // only segments on copper layers
321 m_menu->AddItem( PCB_ACTIONS::convertToTracks, canCreateTracks );
322
323 m_menu->AddItem( PCB_ACTIONS::convertToArc, canCreateArcs );
324
326 m_menu->AddItem( PCB_ACTIONS::createArray, canCreateArray );
327
329 selToolMenu.AddMenu( m_menu, canCreate, 100 );
330
331 return true;
332}
333
334
336{
341}
342
343
345{
347 std::vector<SHAPE_POLY_SET> polys;
348 PCB_LAYER_ID destLayer = m_frame->GetActiveLayer();
349 FOOTPRINT* parentFootprint = nullptr;
350
352 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
353 {
354 } );
355
356 if( selection.Empty() )
357 return 0;
358
359 auto getPolys =
360 [&]( CONVERT_SETTINGS cfg )
361 {
362 polys.clear();
363
364 for( EDA_ITEM* item : selection )
365 item->ClearTempFlags();
366
367 SHAPE_POLY_SET polySet;
368
369 polySet.Append( makePolysFromClosedGraphics( selection.GetItems(), cfg.m_Strategy ) );
370
371 if( cfg.m_Strategy == BOUNDING_HULL )
372 {
374
375 polySet.ClearArcs();
377
378 // Now inflate the bounding hull by cfg.m_Gap
379 polySet.Inflate( cfg.m_Gap, CORNER_STRATEGY::ROUND_ALL_CORNERS, bds.m_MaxError,
381 }
382 else
383 {
384 polySet.Append( makePolysFromChainedSegs( selection.GetItems(), cfg.m_Strategy ) );
385 }
386
387 if( polySet.IsEmpty() )
388 return false;
389
390 for( int ii = 0; ii < polySet.OutlineCount(); ++ii )
391 {
392 polys.emplace_back( SHAPE_POLY_SET( polySet.COutline( ii ) ) );
393
394 for( int jj = 0; jj < polySet.HoleCount( ii ); ++jj )
395 polys.back().AddHole( polySet.Hole( ii, jj ) );
396 }
397
398 return true;
399 };
400
401 // Pre-flight getPolys() to see if there's anything to convert.
402 CONVERT_SETTINGS preflightSettings = m_userSettings;
403 preflightSettings.m_Strategy = BOUNDING_HULL;
404
405 if( !getPolys( preflightSettings ) )
406 return 0;
407
409 parentFootprint = static_cast<BOARD_ITEM*>( selection.Front() )->GetParentFootprint();
410
412 BOARD_COMMIT commit( m_frame );
413
414 if( aEvent.IsAction( &PCB_ACTIONS::convertToPoly ) )
415 {
416 bool showCopyLineWidth = true;
417
418 // No copy-line-width option for pads
419 if( selection.Front()->Type() == PCB_PAD_T )
420 {
423
424 showCopyLineWidth = false;
425 }
426
427 CONVERT_SETTINGS previousSettings = m_userSettings;
428
429 CONVERT_SETTINGS_DIALOG dlg( m_frame, &m_userSettings, showCopyLineWidth, true, true );
430
431 if( dlg.ShowModal() != wxID_OK )
432 return 0;
433
434 CONVERT_SETTINGS resolvedSettings = m_userSettings;
435
436 if( resolvedSettings.m_Strategy != CENTERLINE )
437 {
438 if( resolvedSettings.m_LineWidth == 0 )
439 resolvedSettings.m_LineWidth = bds.m_LineThickness[ bds.GetLayerClass( layer ) ];
440 }
441
442 if( resolvedSettings.m_Strategy == BOUNDING_HULL )
443 {
444 if( resolvedSettings.m_Gap > 0 )
445 resolvedSettings.m_Gap += KiROUND( (double) resolvedSettings.m_LineWidth / 2 );
446 }
447
448 if( !getPolys( resolvedSettings ) )
449 {
450 wxString msg;
451
452 if( resolvedSettings.m_Strategy == BOUNDING_HULL )
453 msg = _( "Resulting polygon would be empty" );
454 else
455 msg = _( "Objects must form a closed shape" );
456
457 DisplayErrorMessage( m_frame, _( "Could not convert selection" ), msg );
458
459 m_userSettings = previousSettings;
460 return 0;
461 }
462
463 for( const SHAPE_POLY_SET& poly : polys )
464 {
465 PCB_SHAPE* graphic = new PCB_SHAPE( parentFootprint );
466
467 if( resolvedSettings.m_Strategy == COPY_LINEWIDTH )
468 {
469 BOARD_ITEM* topLeftItem = nullptr;
470 VECTOR2I pos;
471
472 for( EDA_ITEM* item : selection )
473 {
474 if( !item->IsBOARD_ITEM() )
475 continue;
476
477 BOARD_ITEM* candidate = static_cast<BOARD_ITEM*>( item );
478
479 if( candidate->HasLineStroke() )
480 {
481 pos = candidate->GetPosition();
482
483 if( !topLeftItem
484 || ( pos.x < topLeftItem->GetPosition().x )
485 || ( topLeftItem->GetPosition().x == pos.x
486 && pos.y < topLeftItem->GetPosition().y ) )
487 {
488 topLeftItem = candidate;
489 resolvedSettings.m_LineWidth = topLeftItem->GetStroke().GetWidth();
490 }
491 }
492 }
493 }
494
495 graphic->SetShape( SHAPE_T::POLY );
496 graphic->SetStroke( STROKE_PARAMS( resolvedSettings.m_LineWidth, LINE_STYLE::SOLID,
497 COLOR4D::UNSPECIFIED ) );
498 graphic->SetFilled( resolvedSettings.m_Strategy == CENTERLINE );
499 graphic->SetLayer( destLayer );
500 graphic->SetPolyShape( poly );
501
502 commit.Add( graphic );
503 }
504 }
505 else
506 {
507 // Creating zone or keepout
508 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
509 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
510 ZONE_SETTINGS zoneInfo = bds.GetDefaultZoneSettings();
511
512 bool nonCopper = IsNonCopperLayer( destLayer );
513 zoneInfo.m_Layers.reset().set( destLayer );
514 zoneInfo.m_Name.Empty();
515
516 int ret;
517
518 // No copy-line-width option for zones/keepouts
521
523 {
524 zoneInfo.SetIsRuleArea( true );
525 ret = InvokeRuleAreaEditor( frame, &zoneInfo, board(), &m_userSettings );
526 }
527 else if( nonCopper )
528 {
529 zoneInfo.SetIsRuleArea( false );
530 ret = InvokeNonCopperZonesEditor( frame, &zoneInfo, &m_userSettings );
531 }
532 else
533 {
534 zoneInfo.SetIsRuleArea( false );
535 ret = InvokeCopperZonesEditor( frame, &zoneInfo, &m_userSettings );
536 }
537
538 if( ret == wxID_CANCEL )
539 return 0;
540
541 if( !getPolys( m_userSettings ) )
542 return 0;
543
544 for( const SHAPE_POLY_SET& poly : polys )
545 {
546 ZONE* zone = new ZONE( parent );
547
548 *zone->Outline() = poly;
549 zone->HatchBorder();
550
551 zoneInfo.ExportSetting( *zone );
552
553 commit.Add( zone );
554 }
555 }
556
558 {
559 PCB_SELECTION selectionCopy = selection;
561
562 for( EDA_ITEM* item : selectionCopy )
563 {
564 if( item->GetFlags() & SKIP_STRUCT )
565 commit.Remove( item );
566 }
567 }
568
569 if( aEvent.IsAction( &PCB_ACTIONS::convertToPoly ) )
570 {
572 commit.Push( _( "Convert to Polygon" ) );
573 else
574 commit.Push( _( "Create Polygon" ) );
575 }
576 else
577 {
579 commit.Push( _( "Convert to Zone" ) );
580 else
581 commit.Push( _( "Create Zone" ) );
582 }
583
584 return 0;
585}
586
587
588SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM*>& aItems,
589 CONVERT_STRATEGY aStrategy )
590{
591 // TODO: This code has a somewhat-similar purpose to ConvertOutlineToPolygon but is slightly
592 // different, so this remains a separate algorithm. It might be nice to analyze the dfiferences
593 // in requirements and refactor this.
594
595 // Using a large epsilon here to allow for sloppy drawing can cause the algorithm to miss very
596 // short segments in a converted bezier. So use an epsilon only large enough to cover for
597 // rouding errors in the conversion.
598 int chainingEpsilon = 100; // max dist from one endPt to next startPt in IU
599
601 SHAPE_POLY_SET poly;
602
603 // Stores pairs of (anchor, item) where anchor == 0 -> SEG.A, anchor == 1 -> SEG.B
604 std::map<VECTOR2I, std::vector<std::pair<int, EDA_ITEM*>>> connections;
605 std::deque<EDA_ITEM*> toCheck;
606
607 auto closeEnough =
608 []( const VECTOR2I& aLeft, const VECTOR2I& aRight, int aLimit )
609 {
610 return ( aLeft - aRight ).SquaredEuclideanNorm() <= SEG::Square( aLimit );
611 };
612
613 auto findInsertionPoint =
614 [&]( const VECTOR2I& aPoint ) -> VECTOR2I
615 {
616 if( connections.count( aPoint ) )
617 return aPoint;
618
619 for( const auto& candidatePair : connections )
620 {
621 if( closeEnough( aPoint, candidatePair.first, chainingEpsilon ) )
622 return candidatePair.first;
623 }
624
625 return aPoint;
626 };
627
628 for( EDA_ITEM* item : aItems )
629 {
630 if( std::optional<SEG> seg = getStartEndPoints( item ) )
631 {
632 toCheck.push_back( item );
633 connections[findInsertionPoint( seg->A )].emplace_back( std::make_pair( 0, item ) );
634 connections[findInsertionPoint( seg->B )].emplace_back( std::make_pair( 1, item ) );
635 }
636 }
637
638 while( !toCheck.empty() )
639 {
640 std::vector<BOARD_ITEM*> insertedItems;
641
642 EDA_ITEM* candidate = toCheck.front();
643 toCheck.pop_front();
644
645 if( candidate->GetFlags() & SKIP_STRUCT )
646 continue;
647
648 SHAPE_LINE_CHAIN outline;
649
650 auto insert =
651 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
652 {
653 if( aItem->Type() == PCB_ARC_T
654 || ( aItem->Type() == PCB_SHAPE_T
655 && static_cast<PCB_SHAPE*>( aItem )->GetShape() == SHAPE_T::ARC ) )
656 {
657 SHAPE_ARC arc;
658
659 if( aItem->Type() == PCB_ARC_T )
660 {
661 PCB_ARC* pcb_arc = static_cast<PCB_ARC*>( aItem );
662 arc = *static_cast<SHAPE_ARC*>( pcb_arc->GetEffectiveShape().get() );
663 }
664 else
665 {
666 PCB_SHAPE* pcb_shape = static_cast<PCB_SHAPE*>( aItem );
667 arc = SHAPE_ARC( pcb_shape->GetStart(), pcb_shape->GetArcMid(),
668 pcb_shape->GetEnd(), pcb_shape->GetWidth() );
669 }
670
671 if( aDirection )
672 outline.Append( aAnchor == arc.GetP0() ? arc : arc.Reversed() );
673 else
674 outline.Insert( 0, aAnchor == arc.GetP0() ? arc : arc.Reversed() );
675
676 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
677 }
678 else if( aItem->IsType( { PCB_SHAPE_LOCATE_BEZIER_T } ) )
679 {
680 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
681
682 if( aAnchor == graphic->GetStart() )
683 {
684 for( auto it = graphic->GetBezierPoints().begin();
685 it != graphic->GetBezierPoints().end();
686 ++it )
687 {
688 if( aDirection )
689 outline.Append( *it );
690 else
691 outline.Insert( 0, *it );
692 }
693
694 }
695 else
696 {
697 for( auto it = graphic->GetBezierPoints().rbegin();
698 it != graphic->GetBezierPoints().rend();
699 ++it )
700 {
701 if( aDirection )
702 outline.Append( *it );
703 else
704 outline.Insert( 0, *it );
705 }
706 }
707
708 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
709 }
710 else if( std::optional<SEG> nextSeg = getStartEndPoints( aItem ) )
711 {
712 VECTOR2I& point = ( aAnchor == nextSeg->A ) ? nextSeg->B : nextSeg->A;
713
714 if( aDirection )
715 outline.Append( point );
716 else
717 outline.Insert( 0, point );
718
719 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
720 }
721 };
722
723 // aDirection == true for walking "right" and appending to the end of points
724 // false for walking "left" and prepending to the beginning
725 std::function<void( EDA_ITEM*, const VECTOR2I&, bool )> process =
726 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
727 {
728 if( aItem->GetFlags() & SKIP_STRUCT )
729 return;
730
731 aItem->SetFlags( SKIP_STRUCT );
732
733 insert( aItem, aAnchor, aDirection );
734
735 std::optional<SEG> anchors = getStartEndPoints( aItem );
736 wxASSERT( anchors );
737
738 VECTOR2I nextAnchor = ( aAnchor == anchors->A ) ? anchors->B : anchors->A;
739
740 for( std::pair<int, EDA_ITEM*> pair : connections[nextAnchor] )
741 {
742 if( pair.second == aItem )
743 continue;
744
745 process( pair.second, nextAnchor, aDirection );
746 }
747 };
748
749 std::optional<SEG> anchors = getStartEndPoints( candidate );
750 wxASSERT( anchors );
751
752 // Start with the first object and walk "right"
753 // Note if the first object is an arc, we don't need to insert its first point here, the
754 // whole arc will be inserted at anchor B inside process()
755 if( !( candidate->Type() == PCB_ARC_T
756 || ( candidate->Type() == PCB_SHAPE_T
757 && static_cast<PCB_SHAPE*>( candidate )->GetShape() == SHAPE_T::ARC ) ) )
758 {
759 insert( candidate, anchors->A, true );
760 }
761
762 process( candidate, anchors->B, true );
763
764 // check for any candidates on the "left"
765 EDA_ITEM* left = nullptr;
766
767 for( std::pair<int, EDA_ITEM*> possibleLeft : connections[anchors->A] )
768 {
769 if( possibleLeft.second != candidate )
770 {
771 left = possibleLeft.second;
772 break;
773 }
774 }
775
776 if( left )
777 process( left, anchors->A, false );
778
779 if( outline.PointCount() < 3
780 || !closeEnough( outline.GetPoint( 0 ), outline.GetPoint( -1 ), chainingEpsilon ) )
781 {
782 for( EDA_ITEM* item : insertedItems )
783 item->ClearFlags( SKIP_STRUCT );
784
785 continue;
786 }
787
788 outline.SetClosed( true );
789
790 poly.AddOutline( outline );
791
792 if( aStrategy == BOUNDING_HULL )
793 {
794 for( BOARD_ITEM* item : insertedItems )
795 {
796 item->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError,
797 ERROR_INSIDE, false );
798 }
799 }
800
801 insertedItems.clear();
802 }
803
804 return poly;
805}
806
807
808SHAPE_POLY_SET CONVERT_TOOL::makePolysFromOpenGraphics( const std::deque<EDA_ITEM*>& aItems,
809 int aGap )
810{
812 SHAPE_POLY_SET poly;
813
814 for( EDA_ITEM* item : aItems )
815 {
816 if( item->GetFlags() & SKIP_STRUCT )
817 continue;
818
819 switch( item->Type() )
820 {
821 case PCB_SHAPE_T:
822 {
823 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
824
825 if( shape->IsClosed() )
826 continue;
827
828 shape->TransformShapeToPolygon( poly, UNDEFINED_LAYER, aGap, bds.m_MaxError,
829 ERROR_INSIDE, false );
830 shape->SetFlags( SKIP_STRUCT );
831
832 break;
833 }
834
835 case PCB_TRACE_T:
836 case PCB_ARC_T:
837 case PCB_VIA_T:
838 {
839 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
840
841 track->TransformShapeToPolygon( poly, UNDEFINED_LAYER, aGap, bds.m_MaxError,
842 ERROR_INSIDE, false );
843 track->SetFlags( SKIP_STRUCT );
844
845 break;
846 }
847
848 default:
849 continue;
850 }
851 }
852
853 return poly;
854}
855
856
858 CONVERT_STRATEGY aStrategy )
859{
861 SHAPE_POLY_SET poly;
862
863 for( EDA_ITEM* item : aItems )
864 {
865 if( item->GetFlags() & SKIP_STRUCT )
866 continue;
867
868 switch( item->Type() )
869 {
870 case PCB_SHAPE_T:
871 {
872 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
873 FILL_T wasFilled = shape->GetFillMode();
874
875 if( !shape->IsClosed() )
876 continue;
877
878 if( aStrategy != BOUNDING_HULL )
879 shape->SetFilled( true );
880
882 aStrategy == COPY_LINEWIDTH
883 || aStrategy == CENTERLINE );
884
885 if( aStrategy != BOUNDING_HULL )
886 shape->SetFillMode( wasFilled );
887
888 shape->SetFlags( SKIP_STRUCT );
889 break;
890 }
891
892 case PCB_ZONE_T:
893 poly.Append( *static_cast<ZONE*>( item )->Outline() );
894 item->SetFlags( SKIP_STRUCT );
895 break;
896
897 case PCB_FIELD_T:
898 case PCB_TEXT_T:
899 {
900 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
901 text->TransformTextToPolySet( poly, 0, bds.m_MaxError, ERROR_INSIDE );
902 text->SetFlags( SKIP_STRUCT );
903 break;
904 }
905
906 case PCB_PAD_T:
907 {
908 PAD* pad = static_cast<PAD*>( item );
909 pad->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError, ERROR_INSIDE,
910 false );
911 pad->SetFlags( SKIP_STRUCT );
912 break;
913 }
914
915
916 default:
917 continue;
918 }
919 }
920
921 return poly;
922}
923
924
926{
928 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
929 {
930 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
931 {
932 BOARD_ITEM* item = aCollector[i];
933
934 switch( item->Type() )
935 {
936 case PCB_SHAPE_T:
937 switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
938 {
939 case SHAPE_T::SEGMENT:
940 case SHAPE_T::ARC:
941 case SHAPE_T::POLY:
942 case SHAPE_T::RECTANGLE:
943 break;
944
945 default:
946 aCollector.Remove( item );
947 }
948
949 break;
950
951 case PCB_ZONE_T:
952 break;
953
954 default:
955 aCollector.Remove( item );
956 }
957 }
958 } );
959
960 if( selection.Empty() )
961 return 0;
962
963 auto getPolySet =
964 []( EDA_ITEM* aItem )
965 {
966 SHAPE_POLY_SET set;
967
968 switch( aItem->Type() )
969 {
970 case PCB_ZONE_T:
971 set = *static_cast<ZONE*>( aItem )->Outline();
972 break;
973
974 case PCB_SHAPE_T:
975 {
976 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
977
978 if( graphic->GetShape() == SHAPE_T::POLY )
979 {
980 set = graphic->GetPolyShape();
981 }
982 else if( graphic->GetShape() == SHAPE_T::RECTANGLE )
983 {
984 SHAPE_LINE_CHAIN outline;
985 VECTOR2I start( graphic->GetStart() );
986 VECTOR2I end( graphic->GetEnd() );
987
988 outline.Append( start );
989 outline.Append( VECTOR2I( end.x, start.y ) );
990 outline.Append( end );
991 outline.Append( VECTOR2I( start.x, end.y ) );
992 outline.SetClosed( true );
993
994 set.AddOutline( outline );
995 }
996 else
997 {
998 wxFAIL_MSG( wxT( "Unhandled graphic shape type in PolyToLines - getPolySet" ) );
999 }
1000 break;
1001 }
1002
1003 default:
1004 wxFAIL_MSG( wxT( "Unhandled type in PolyToLines - getPolySet" ) );
1005 break;
1006 }
1007
1008 return set;
1009 };
1010
1011 auto getSegList =
1012 []( SHAPE_POLY_SET& aPoly )
1013 {
1014 std::vector<SEG> segs;
1015
1016 // Our input should be valid polys, so OK to assert here
1017 wxASSERT( aPoly.VertexCount() >= 2 );
1018
1019 for( int i = 1; i < aPoly.VertexCount(); i++ )
1020 segs.emplace_back( SEG( aPoly.CVertex( i - 1 ), aPoly.CVertex( i ) ) );
1021
1022 segs.emplace_back( SEG( aPoly.CVertex( aPoly.VertexCount() - 1 ),
1023 aPoly.CVertex( 0 ) ) );
1024
1025 return segs;
1026 };
1027
1028 BOARD_COMMIT commit( m_frame );
1029 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1030 FOOTPRINT_EDIT_FRAME* fpEditor = dynamic_cast<FOOTPRINT_EDIT_FRAME*>( m_frame );
1031 FOOTPRINT* footprint = nullptr;
1032 PCB_LAYER_ID targetLayer = m_frame->GetActiveLayer();
1033 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1034
1035 if( fpEditor )
1036 footprint = fpEditor->GetBoard()->GetFirstFootprint();
1037
1038 auto handleGraphicSeg =
1039 [&]( EDA_ITEM* aItem )
1040 {
1041 if( aItem->Type() != PCB_SHAPE_T )
1042 return false;
1043
1044 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
1045
1046 if( graphic->GetShape() == SHAPE_T::SEGMENT )
1047 {
1048 PCB_TRACK* track = new PCB_TRACK( parent );
1049
1050 track->SetLayer( targetLayer );
1051 track->SetStart( graphic->GetStart() );
1052 track->SetEnd( graphic->GetEnd() );
1053 track->SetWidth( graphic->GetWidth() );
1054 commit.Add( track );
1055
1056 return true;
1057 }
1058 else if( graphic->GetShape() == SHAPE_T::ARC )
1059 {
1060 PCB_ARC* arc = new PCB_ARC( parent );
1061
1062 arc->SetLayer( targetLayer );
1063 arc->SetStart( graphic->GetStart() );
1064 arc->SetEnd( graphic->GetEnd() );
1065 arc->SetMid( graphic->GetArcMid() );
1066 arc->SetWidth( graphic->GetWidth() );
1067 commit.Add( arc );
1068
1069 return true;
1070 }
1071
1072 return false;
1073 };
1074
1075 if( aEvent.IsAction( &PCB_ACTIONS::convertToTracks ) )
1076 {
1077 if( !IsCopperLayer( targetLayer ) )
1078 {
1079 targetLayer = frame->SelectOneLayer( F_Cu, LSET::AllNonCuMask() );
1080
1081 if( targetLayer == UNDEFINED_LAYER ) // User canceled
1082 return true;
1083 }
1084 }
1085 else
1086 {
1087 CONVERT_SETTINGS_DIALOG dlg( m_frame, &m_userSettings, false, false, false );
1088
1089 if( dlg.ShowModal() != wxID_OK )
1090 return true;
1091 }
1092
1093 for( EDA_ITEM* item : selection )
1094 {
1095 if( handleGraphicSeg( item ) )
1096 continue;
1097
1098 BOARD_ITEM& boardItem = static_cast<BOARD_ITEM&>( *item );
1099 SHAPE_POLY_SET polySet = getPolySet( item );
1100 std::vector<SEG> segs = getSegList( polySet );
1101
1102 std::optional<int> itemWidth = GetBoardItemWidth( boardItem );
1103
1104 if( aEvent.IsAction( &PCB_ACTIONS::convertToLines ) )
1105 {
1106 for( SEG& seg : segs )
1107 {
1108 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1109
1110 graphic->SetLayer( targetLayer );
1111 graphic->SetStart( VECTOR2I( seg.A ) );
1112 graphic->SetEnd( VECTOR2I( seg.B ) );
1113
1114 // The width can exist but be 0 for filled, unstroked shapes
1115 if( itemWidth && *itemWidth > 0 )
1116 graphic->SetWidth( *itemWidth );
1117
1118 commit.Add( graphic );
1119 }
1120 }
1121 else
1122 {
1123 // I am really unsure converting a polygon to "tracks" (i.e. segments on
1124 // copper layers) make sense for footprints, but anyway this code exists
1125 if( fpEditor )
1126 {
1127 // Creating segments on copper layer
1128 for( SEG& seg : segs )
1129 {
1130 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1131 graphic->SetLayer( targetLayer );
1132 graphic->SetStart( VECTOR2I( seg.A ) );
1133 graphic->SetEnd( VECTOR2I( seg.B ) );
1134
1135 if( itemWidth )
1136 graphic->SetWidth( *itemWidth );
1137
1138 commit.Add( graphic );
1139 }
1140 }
1141 else
1142 {
1143 // Creating tracks
1144 for( SEG& seg : segs )
1145 {
1146 PCB_TRACK* track = new PCB_TRACK( parent );
1147
1148 track->SetLayer( targetLayer );
1149 track->SetStart( VECTOR2I( seg.A ) );
1150 track->SetEnd( VECTOR2I( seg.B ) );
1151 commit.Add( track );
1152 }
1153 }
1154 }
1155 }
1156
1157 if( m_userSettings.m_DeleteOriginals )
1158 {
1159 PCB_SELECTION selectionCopy = selection;
1160 m_selectionTool->ClearSelection();
1161
1162 for( EDA_ITEM* item : selectionCopy )
1163 commit.Remove( item );
1164 }
1165
1166 commit.Push( _( "Create Lines" ) );
1167
1168 return 0;
1169}
1170
1171
1173{
1175 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1176 {
1177 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1178 {
1179 BOARD_ITEM* item = aCollector[i];
1180
1181 if( !( item->Type() == PCB_SHAPE_T ||
1182 item->Type() == PCB_TRACE_T ) )
1183 {
1184 aCollector.Remove( item );
1185 }
1186 }
1187 } );
1188
1189 if( !selection.Front()->IsBOARD_ITEM() )
1190 return -1;
1191
1192 BOARD_ITEM* source = static_cast<BOARD_ITEM*>( selection.Front() );
1193 VECTOR2I start, end, mid;
1194
1195 // Offset the midpoint along the normal a little bit so that it's more obviously an arc
1196 const double offsetRatio = 0.1;
1197
1198 if( std::optional<SEG> seg = getStartEndPoints( source ) )
1199 {
1200 start = seg->A;
1201 end = seg->B;
1202
1203 VECTOR2I normal = ( seg->B - seg->A ).Perpendicular().Resize( offsetRatio * seg->Length() );
1204 mid = seg->Center() + normal;
1205 }
1206 else
1207 {
1208 return -1;
1209 }
1210
1211 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1212 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1213 PCB_LAYER_ID layer = source->GetLayer();
1214 BOARD_COMMIT commit( m_frame );
1215
1216 if( source->Type() == PCB_SHAPE_T )
1217 {
1218 PCB_SHAPE* line = static_cast<PCB_SHAPE*>( source );
1219 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1220
1221 VECTOR2I center = CalcArcCenter( start, mid, end );
1222
1223 arc->SetFilled( false );
1224 arc->SetLayer( layer );
1225 arc->SetStroke( line->GetStroke() );
1226
1227 arc->SetCenter( VECTOR2I( center ) );
1228 arc->SetStart( VECTOR2I( start ) );
1229 arc->SetEnd( VECTOR2I( end ) );
1230
1231 commit.Add( arc );
1232 }
1233 else
1234 {
1235 wxASSERT( source->Type() == PCB_TRACE_T );
1236 PCB_TRACK* line = static_cast<PCB_TRACK*>( source );
1237 PCB_ARC* arc = new PCB_ARC( parent );
1238
1239 arc->SetLayer( layer );
1240 arc->SetWidth( line->GetWidth() );
1241 arc->SetStart( VECTOR2I( start ) );
1242 arc->SetMid( VECTOR2I( mid ) );
1243 arc->SetEnd( VECTOR2I( end ) );
1244
1245 commit.Add( arc );
1246 }
1247
1248 commit.Push( _( "Create Arc" ) );
1249
1250 return 0;
1251}
1252
1253
1254std::optional<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem )
1255{
1256 switch( aItem->Type() )
1257 {
1258 case PCB_SHAPE_T:
1259 {
1260 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
1261
1262 switch( shape->GetShape() )
1263 {
1264 case SHAPE_T::SEGMENT:
1265 case SHAPE_T::ARC:
1266 case SHAPE_T::POLY:
1267 case SHAPE_T::BEZIER:
1268 if( shape->GetStart() == shape->GetEnd() )
1269 return std::nullopt;
1270
1271 return std::make_optional<SEG>( VECTOR2I( shape->GetStart() ),
1272 VECTOR2I( shape->GetEnd() ) );
1273
1274 default:
1275 return std::nullopt;
1276 }
1277 }
1278
1279 case PCB_TRACE_T:
1280 {
1281 PCB_TRACK* line = static_cast<PCB_TRACK*>( aItem );
1282 return std::make_optional<SEG>( VECTOR2I( line->GetStart() ), VECTOR2I( line->GetEnd() ) );
1283 }
1284
1285 case PCB_ARC_T:
1286 {
1287 PCB_ARC* arc = static_cast<PCB_ARC*>( aItem );
1288 return std::make_optional<SEG>( VECTOR2I( arc->GetStart() ), VECTOR2I( arc->GetEnd() ) );
1289 }
1290
1291 default:
1292 return std::nullopt;
1293 }
1294}
1295
1296
1298{
1299 PCB_BASE_EDIT_FRAME& frame = *getEditFrame<PCB_BASE_EDIT_FRAME>();
1301 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1302 {
1303 // Iterate from the back so we don't have to worry about removals.
1304 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1305 {
1306 BOARD_ITEM* item = aCollector[i];
1307
1308 // We've converted the polygon and rectangle to segments, so drop everything
1309 // that isn't a segment at this point
1310 if( !item->IsType( { PCB_PAD_T, PCB_SHAPE_T } ) )
1311 {
1312 aCollector.Remove( item );
1313 }
1314 }
1315 },
1316 true /* prompt user regarding locked items */ );
1317
1318 BOARD_COMMIT commit( this );
1319
1320 for( EDA_ITEM* item : selection )
1321 item->ClearFlags( STRUCT_DELETED );
1322
1323 // List of thing to select at the end of the operation
1324 // (doing it as we go will invalidate the iterator)
1325 std::vector<BOARD_ITEM*> items_to_select_on_success;
1326
1327 // Handle modifications to existing items by the routine
1328 // How to deal with this depends on whether we're in the footprint editor or not
1329 // and whether the item was conjured up by decomposing a polygon or rectangle
1330 auto item_modification_handler = [&]( BOARD_ITEM& aItem )
1331 {
1332 };
1333
1334 bool any_items_created = false;
1335 auto item_creation_handler = [&]( std::unique_ptr<BOARD_ITEM> aItem )
1336 {
1337 any_items_created = true;
1338 items_to_select_on_success.push_back( aItem.get() );
1339 commit.Add( aItem.release() );
1340 };
1341
1342 auto item_removal_handler = [&]( BOARD_ITEM& aItem )
1343 {
1344 // If you do an outset on a FP pad, do you really want to delete
1345 // the parent?
1346 if( !aItem.GetParentFootprint() )
1347 {
1348 commit.Remove( &aItem );
1349 }
1350 };
1351
1352 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1354 item_creation_handler, item_modification_handler, item_removal_handler );
1355
1356 // Persistent settings between dialog invocations
1357 // Init with some sensible defaults
1358 static OUTSET_ROUTINE::PARAMETERS outset_params_fp_edit{
1359 pcbIUScale.mmToIU( 0.25 ), // A common outset value
1360 false,
1361 false,
1362 true,
1363 F_CrtYd,
1365 pcbIUScale.mmToIU( 0.01 ),
1366 false,
1367 };
1368
1369 static OUTSET_ROUTINE::PARAMETERS outset_params_pcb_edit{
1370 pcbIUScale.mmToIU( 1 ),
1371 true,
1372 true,
1373 true,
1374 Edge_Cuts, // Outsets often for slots?
1376 std::nullopt,
1377 false,
1378 };
1379
1380 OUTSET_ROUTINE::PARAMETERS& outset_params =
1381 IsFootprintEditor() ? outset_params_fp_edit : outset_params_pcb_edit;
1382
1383 {
1384 DIALOG_OUTSET_ITEMS dlg( frame, outset_params );
1385 if( dlg.ShowModal() == wxID_CANCEL )
1386 {
1387 return 0;
1388 }
1389 }
1390
1391 OUTSET_ROUTINE outset_routine( frame.GetModel(), change_handler, outset_params );
1392
1393 for( EDA_ITEM* item : selection )
1394 {
1395 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
1396 outset_routine.ProcessItem( *board_item );
1397 }
1398
1399 // Deselect all the original items
1400 m_selectionTool->ClearSelection();
1401
1402 // Select added and modified items
1403 for( BOARD_ITEM* item : items_to_select_on_success )
1404 m_selectionTool->AddItemToSel( item, true );
1405
1406 if( any_items_created )
1407 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1408
1409 // Notify other tools of the changes
1410 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1411
1412 commit.Push( outset_routine.GetCommitDescription() );
1413
1414 if( const std::optional<wxString> msg = outset_routine.GetStatusMessage() )
1415 frame.ShowInfoBarMsg( *msg );
1416
1417 return 0;
1418}
1419
1420
1422{
1423 // clang-format off
1431 // clang-format on
1432}
@ 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:142
BASE_SET & set(size_t pos)
Definition: base_set.h:115
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
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:80
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:238
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:289
virtual bool HasLineStroke() const
Check if this item has line stoke properties.
Definition: board_item.h:228
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:448
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:890
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)
Notify observers that aItem has been removed.
Definition: commit.h:92
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
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:211
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
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:243
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:527
FILL_T GetFillMode() const
Definition: eda_shape.h:107
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:279
void SetFilled(bool aFlag)
Definition: eda_shape.h:101
SHAPE_T GetShape() const
Definition: eda_shape.h:125
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:287
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:167
bool IsClosed() const
Definition: eda_shape.cpp:247
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:134
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:130
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:124
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition: eda_shape.h:262
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:171
void SetWidth(int aWidth)
Definition: eda_shape.h:114
void SetFillMode(FILL_T aFill)
Definition: eda_shape.h:106
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:545
static const TOOL_EVENT SelectedEvent
Definition: actions.h:273
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:280
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:100
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition: lset.cpp:687
Definition: pad.h:54
static TOOL_ACTION convertToKeepout
Definition: pcb_actions.h:580
static TOOL_ACTION convertToTracks
Definition: pcb_actions.h:583
static TOOL_ACTION convertToLines
Definition: pcb_actions.h:581
static TOOL_ACTION convertToZone
Definition: pcb_actions.h:579
static TOOL_ACTION convertToPoly
Definition: pcb_actions.h:578
static TOOL_ACTION outsetItems
Create outset items from selection.
Definition: pcb_actions.h:170
static TOOL_ACTION convertToArc
Definition: pcb_actions.h:582
static TOOL_ACTION createArray
Tool for creating an array of objects.
Definition: pcb_actions.h:490
void SetMid(const VECTOR2I &aMid)
Definition: pcb_track.h:298
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:1938
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:301
virtual PCB_LAYER_ID GetActiveLayer() const
BOARD * GetBoard() const
virtual BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Returns 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:457
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:326
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:930
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:1949
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:680
const VECTOR2I & GetP0() const
Definition: shape_arc.h:114
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(POLYGON_MODE aFastMode)
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFastMo...
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:79
int GetWidth() const
Definition: stroke_params.h:89
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:218
Generic, UI-independent tool event.
Definition: tool_event.h:167
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:954
SHAPE_POLY_SET * Outline()
Definition: zone.h:337
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
@ ARC
use RECTANGLE instead of RECT to avoid collision in a Windows header
FILL_T
Definition: eda_shape.h:55
static const std::vector< KICAD_T > padTypes
Definition: edit_tool.cpp:78
static const std::vector< KICAD_T > trackTypes
Definition: edit_tool.cpp:84
@ 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:554
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:532
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:1058
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:391
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:691