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>
38#include <footprint.h>
41#include <pcb_edit_frame.h>
42#include <pcb_shape.h>
43#include <pcb_track.h>
44#include <pad.h>
45#include <tool/tool_manager.h>
46#include <tools/edit_tool.h>
47#include <tools/pcb_actions.h>
49#include <trigo.h>
50#include <macros.h>
51#include <zone.h>
52
53#include "convert_tool.h"
54
55
57{
58public:
60 bool aShowCopyLineWidthOption, bool aShowCenterlineOption,
61 bool aShowBoundingHullOption ) :
62 DIALOG_SHIM( aParent, wxID_ANY, _( "Conversion Settings" ), wxDefaultPosition,
63 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
64 m_settings( aSettings )
65 {
66 // Allow each distinct version of the dialog to have a different size
67 m_hash_key = TO_UTF8( wxString::Format( wxS( "%s%c%c%c" ),
68 GetTitle(),
69 aShowCopyLineWidthOption,
70 aShowCenterlineOption,
71 aShowBoundingHullOption ) );
72
73 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
74 wxBoxSizer* topSizer = new wxBoxSizer( wxVERTICAL );
75 SetSizer( mainSizer );
76
77 m_rbMimicLineWidth = new wxRadioButton( this, wxID_ANY, _( "Copy line width of first object" ) );
78
79 if( aShowCopyLineWidthOption )
80 topSizer->Add( m_rbMimicLineWidth, 0, wxLEFT|wxRIGHT, 5 );
81 else
82 m_rbMimicLineWidth->Hide();
83
84 m_rbCenterline = new wxRadioButton( this, wxID_ANY, _( "Use centerlines" ) );
85
86 if( aShowCenterlineOption )
87 {
88 topSizer->AddSpacer( 6 );
89 topSizer->Add( m_rbCenterline, 0, wxLEFT|wxRIGHT, 5 );
90 }
91 else
92 {
93 m_rbCenterline->Hide();
94 }
95
96 m_rbBoundingHull = new wxRadioButton( this, wxID_ANY, _( "Create bounding hull" ) );
97
98 m_gapLabel = new wxStaticText( this, wxID_ANY, _( "Gap:" ) );
99 m_gapCtrl = new wxTextCtrl( this, wxID_ANY );
100 m_gapUnits = new wxStaticText( this, wxID_ANY, _( "mm" ) );
101 m_gap = new UNIT_BINDER( aParent, m_gapLabel, m_gapCtrl, m_gapUnits );
102
103 m_widthLabel = new wxStaticText( this, wxID_ANY, _( "Line width:" ) );
104 m_widthCtrl = new wxTextCtrl( this, wxID_ANY );
105 m_widthUnits = new wxStaticText( this, wxID_ANY, _( "mm" ) );
107
108 if( aShowBoundingHullOption )
109 {
110 topSizer->AddSpacer( 6 );
111 topSizer->Add( m_rbBoundingHull, 0, wxLEFT|wxRIGHT, 5 );
112
113 wxBoxSizer* hullParamsSizer = new wxBoxSizer( wxHORIZONTAL );
114 hullParamsSizer->Add( m_gapLabel, 0, wxALIGN_CENTRE_VERTICAL, 5 );
115 hullParamsSizer->Add( m_gapCtrl, 1, wxALIGN_CENTRE_VERTICAL|wxLEFT|wxRIGHT, 3 );
116 hullParamsSizer->Add( m_gapUnits, 0, wxALIGN_CENTRE_VERTICAL, 5 );
117 hullParamsSizer->AddSpacer( 18 );
118 hullParamsSizer->Add( m_widthLabel, 0, wxALIGN_CENTRE_VERTICAL, 5 );
119 hullParamsSizer->Add( m_widthCtrl, 1, wxALIGN_CENTRE_VERTICAL|wxLEFT|wxRIGHT, 3 );
120 hullParamsSizer->Add( m_widthUnits, 0, wxALIGN_CENTRE_VERTICAL, 5 );
121
122 topSizer->AddSpacer( 2 );
123 topSizer->Add( hullParamsSizer, 0, wxLEFT, 26 );
124
125 topSizer->AddSpacer( 15 );
126 }
127 else
128 {
129 m_rbBoundingHull->Hide();
130 m_gap->Show( false, true );
131 m_width->Show( false, true );
132 }
133
134 m_cbDeleteOriginals = new wxCheckBox( this, wxID_ANY, _( "Delete source objects after conversion" ) );
135 topSizer->Add( m_cbDeleteOriginals, 0, wxALL, 5 );
136
137 mainSizer->Add( topSizer, 1, wxALL|wxEXPAND, 10 );
138
139 wxBoxSizer* buttonsSizer = new wxBoxSizer( wxHORIZONTAL );
140 buttonsSizer->AddStretchSpacer();
141
142 wxStdDialogButtonSizer* sdbSizer = new wxStdDialogButtonSizer();
143 wxButton* sdbSizerOK = new wxButton( this, wxID_OK );
144 sdbSizer->AddButton( sdbSizerOK );
145 wxButton* sdbSizerCancel = new wxButton( this, wxID_CANCEL );
146 sdbSizer->AddButton( sdbSizerCancel );
147 sdbSizer->Realize();
148
149 buttonsSizer->Add( sdbSizer, 1, 0, 5 );
150 mainSizer->Add( buttonsSizer, 0, wxALL|wxEXPAND, 5 );
151
153
154 m_rbMimicLineWidth->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED,
155 wxCommandEventHandler( CONVERT_SETTINGS_DIALOG::onRadioButton ),
156 nullptr, this );
157 m_rbCenterline->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED,
158 wxCommandEventHandler( CONVERT_SETTINGS_DIALOG::onRadioButton ),
159 nullptr, this );
160 m_rbBoundingHull->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED,
161 wxCommandEventHandler( CONVERT_SETTINGS_DIALOG::onRadioButton ),
162 nullptr, this );
163
165 }
166
168 {
169 delete m_gap;
170 delete m_width;
171 };
172
173protected:
174 bool TransferDataToWindow() override
175 {
176 switch( m_settings->m_Strategy )
177 {
178 case COPY_LINEWIDTH: m_rbMimicLineWidth->SetValue( true ); break;
179 case CENTERLINE: m_rbCenterline->SetValue( true ); break;
180 case BOUNDING_HULL: m_rbBoundingHull->SetValue( true ); break;
181 }
182
183 m_gap->Enable( m_rbBoundingHull->GetValue() );
184 m_width->Enable( m_rbBoundingHull->GetValue() );
187
189 return true;
190 }
191
193 {
194 if( m_rbBoundingHull->GetValue() )
196 else if( m_rbCenterline->GetValue() )
198 else
200
203
205 return true;
206 }
207
208 void onRadioButton( wxCommandEvent& aEvent )
209 {
210 m_gap->Enable( m_rbBoundingHull->GetValue() );
211 m_width->Enable( m_rbBoundingHull->GetValue() );
212 }
213
214private:
216
217 wxRadioButton* m_rbMimicLineWidth;
218 wxRadioButton* m_rbCenterline;
219 wxRadioButton* m_rbBoundingHull;
220 wxStaticText* m_gapLabel;
221 wxTextCtrl* m_gapCtrl;
222 wxStaticText* m_gapUnits;
224 wxStaticText* m_widthLabel;
225 wxTextCtrl* m_widthCtrl;
226 wxStaticText* m_widthUnits;
229};
230
231
233 TOOL_INTERACTIVE( "pcbnew.Convert" ),
234 m_selectionTool( nullptr ),
235 m_menu( nullptr ),
236 m_frame( nullptr )
237{
239}
240
241
243{
244 delete m_menu;
245}
246
247
250
251
253{
255 m_frame = getEditFrame<PCB_BASE_FRAME>();
256
257 // Create a context menu and make it available through selection tool
258 m_menu = new CONDITIONAL_MENU( this );
259 m_menu->SetIcon( BITMAPS::convert );
260 m_menu->SetTitle( _( "Create from Selection" ) );
261
266 && P_S_C::SameLayer();
267
269
270 auto anyTracks = S_C::MoreThan( 0 ) && S_C::OnlyTypes( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } )
271 && P_S_C::SameLayer();
272
274
275 auto anyPads = S_C::OnlyTypes( { PCB_PAD_T } );
276
277 auto canCreateArcs = S_C::Count( 1 )
279 auto canCreateArray = S_C::MoreThan( 0 );
280 auto canCreatePolyType = shapes || anyPolys || anyTracks;
281
283 canCreatePolyType = shapes || anyPolys || anyTracks || anyPads;
284
285 auto canCreateLines = anyPolys;
286 auto canCreateTracks = anyPolys || graphicToTrack;
287 auto canCreate = canCreatePolyType
288 || canCreateLines
289 || canCreateTracks
290 || canCreateArcs
291 || canCreateArray;
292
293 m_menu->AddItem( PCB_ACTIONS::convertToPoly, canCreatePolyType );
294
296 m_menu->AddItem( PCB_ACTIONS::convertToZone, canCreatePolyType );
297
298 m_menu->AddItem( PCB_ACTIONS::convertToKeepout, canCreatePolyType );
299 m_menu->AddItem( PCB_ACTIONS::convertToLines, canCreateLines );
300 m_menu->AppendSeparator();
301
302 // Currently the code exists, but tracks are not really existing in footprints
303 // only segments on copper layers
305 m_menu->AddItem( PCB_ACTIONS::convertToTracks, canCreateTracks );
306
307 m_menu->AddItem( PCB_ACTIONS::convertToArc, canCreateArcs );
308
309 m_menu->AppendSeparator();
310 m_menu->AddItem( PCB_ACTIONS::createArray, canCreateArray );
311
313 selToolMenu.AddMenu( m_menu, canCreate, 100 );
314
315 return true;
316}
317
318
320{
325}
326
327
329{
331 std::vector<SHAPE_POLY_SET> polys;
332 PCB_LAYER_ID destLayer = m_frame->GetActiveLayer();
333 FOOTPRINT* parentFootprint = nullptr;
334
336 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
337 {
338 } );
339
340 if( selection.Empty() )
341 return 0;
342
343 auto getPolys =
344 [&]( CONVERT_SETTINGS cfg )
345 {
346 polys.clear();
347
348 for( EDA_ITEM* item : selection )
349 item->ClearTempFlags();
350
351 SHAPE_POLY_SET polySet;
352
353 polySet.Append( makePolysFromClosedGraphics( selection.GetItems(), cfg.m_Strategy ) );
354
355 if( cfg.m_Strategy == BOUNDING_HULL )
356 {
357 polySet.Append( makePolysFromOpenGraphics( selection.GetItems(), cfg.m_Gap ) );
358
359 polySet.ClearArcs();
361 polySet.Inflate( cfg.m_Gap, CORNER_STRATEGY::ROUND_ALL_CORNERS, bds.m_MaxError,
363 }
364 else
365 {
366 polySet.Append( makePolysFromChainedSegs( selection.GetItems(), cfg.m_Strategy ) );
367 }
368
369 if( polySet.IsEmpty() )
370 return false;
371
372 for( int ii = 0; ii < polySet.OutlineCount(); ++ii )
373 {
374 polys.emplace_back( SHAPE_POLY_SET( polySet.COutline( ii ) ) );
375
376 for( int jj = 0; jj < polySet.HoleCount( ii ); ++jj )
377 polys.back().AddHole( polySet.Hole( ii, jj ) );
378 }
379
380 return true;
381 };
382
383 // Pre-flight getPolys() to see if there's anything to convert.
384 CONVERT_SETTINGS preflightSettings = m_userSettings;
385 preflightSettings.m_Strategy = BOUNDING_HULL;
386
387 if( !getPolys( preflightSettings ) )
388 return 0;
389
390 if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( selection.Front() ) )
391 parentFootprint = item->GetParentFootprint();
392
394 BOARD_COMMIT commit( m_frame );
395
396 if( aEvent.IsAction( &PCB_ACTIONS::convertToPoly ) )
397 {
398 bool showCopyLineWidth = true;
399
400 // No copy-line-width option for pads
401 if( dynamic_cast<PAD*>( selection.Front() ) )
402 {
405
406 showCopyLineWidth = false;
407 }
408
409 CONVERT_SETTINGS previousSettings = m_userSettings;
410
411 CONVERT_SETTINGS_DIALOG dlg( m_frame, &m_userSettings, showCopyLineWidth, true, true );
412
413 if( dlg.ShowModal() != wxID_OK )
414 return 0;
415
416 CONVERT_SETTINGS resolvedSettings = m_userSettings;
417
418 if( resolvedSettings.m_Strategy != CENTERLINE )
419 {
420 if( resolvedSettings.m_LineWidth == 0 )
421 resolvedSettings.m_LineWidth = bds.m_LineThickness[ bds.GetLayerClass( layer ) ];
422 }
423
424 if( resolvedSettings.m_Strategy == BOUNDING_HULL )
425 {
426 if( resolvedSettings.m_Gap > 0 )
427 resolvedSettings.m_Gap += KiROUND( (double) resolvedSettings.m_LineWidth / 2 );
428 }
429
430 if( !getPolys( resolvedSettings ) )
431 {
432 wxString msg;
433
434 if( resolvedSettings.m_Strategy == BOUNDING_HULL )
435 msg = _( "Resulting polygon would be empty" );
436 else
437 msg = _( "Objects must form a closed shape" );
438
439 DisplayErrorMessage( m_frame, _( "Could not convert selection" ), msg );
440
441 m_userSettings = previousSettings;
442 return 0;
443 }
444
445 for( const SHAPE_POLY_SET& poly : polys )
446 {
447 PCB_SHAPE* graphic = new PCB_SHAPE( parentFootprint );
448
449 if( resolvedSettings.m_Strategy == COPY_LINEWIDTH )
450 {
451 BOARD_ITEM* topLeftItem = nullptr;
452 VECTOR2I pos;
453
454 for( EDA_ITEM* item : selection )
455 {
456 if( BOARD_ITEM* candidate = dynamic_cast<BOARD_ITEM*>( item ) )
457 {
458 if( candidate->HasLineStroke() )
459 {
460 pos = candidate->GetPosition();
461
462 if( !topLeftItem
463 || ( pos.x < topLeftItem->GetPosition().x )
464 || ( topLeftItem->GetPosition().x == pos.x
465 && pos.y < topLeftItem->GetPosition().y ) )
466 {
467 topLeftItem = candidate;
468 resolvedSettings.m_LineWidth = topLeftItem->GetStroke().GetWidth();
469 }
470 }
471 }
472 }
473 }
474
475 graphic->SetShape( SHAPE_T::POLY );
476 graphic->SetStroke( STROKE_PARAMS( resolvedSettings.m_LineWidth, LINE_STYLE::SOLID,
477 COLOR4D::UNSPECIFIED ) );
478 graphic->SetFilled( resolvedSettings.m_Strategy == CENTERLINE );
479 graphic->SetLayer( destLayer );
480 graphic->SetPolyShape( poly );
481
482 commit.Add( graphic );
483 }
484 }
485 else
486 {
487 // Creating zone or keepout
488 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
489 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
490 ZONE_SETTINGS zoneInfo = bds.GetDefaultZoneSettings();
491
492 bool nonCopper = IsNonCopperLayer( destLayer );
493 zoneInfo.m_Layers.reset().set( destLayer );
494 zoneInfo.m_Name.Empty();
495
496 int ret;
497
498 // No copy-line-width option for zones/keepouts
501
503 {
504 zoneInfo.SetIsRuleArea( true );
505 ret = InvokeRuleAreaEditor( frame, &zoneInfo, &m_userSettings );
506 }
507 else if( nonCopper )
508 {
509 zoneInfo.SetIsRuleArea( false );
510 ret = InvokeNonCopperZonesEditor( frame, &zoneInfo, &m_userSettings );
511 }
512 else
513 {
514 zoneInfo.SetIsRuleArea( false );
515 ret = InvokeCopperZonesEditor( frame, &zoneInfo, &m_userSettings );
516 }
517
518 if( ret == wxID_CANCEL )
519 return 0;
520
521 if( !getPolys( m_userSettings ) )
522 return 0;
523
524 for( const SHAPE_POLY_SET& poly : polys )
525 {
526 ZONE* zone = new ZONE( parent );
527
528 *zone->Outline() = poly;
529 zone->HatchBorder();
530
531 zoneInfo.ExportSetting( *zone );
532
533 commit.Add( zone );
534 }
535 }
536
538 {
539 PCB_SELECTION selectionCopy = selection;
541
542 for( EDA_ITEM* item : selectionCopy )
543 {
544 if( item->GetFlags() & SKIP_STRUCT )
545 commit.Remove( item );
546 }
547 }
548
549 if( aEvent.IsAction( &PCB_ACTIONS::convertToPoly ) )
550 {
552 commit.Push( _( "Convert to Polygon" ) );
553 else
554 commit.Push( _( "Create Polygon" ) );
555 }
556 else
557 {
559 commit.Push( _( "Convert to Zone" ) );
560 else
561 commit.Push( _( "Create Zone" ) );
562 }
563
564 return 0;
565}
566
567
568SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM*>& aItems,
569 CONVERT_STRATEGY aStrategy )
570{
571 // TODO: This code has a somewhat-similar purpose to ConvertOutlineToPolygon but is slightly
572 // different, so this remains a separate algorithm. It might be nice to analyze the dfiferences
573 // in requirements and refactor this.
574
575 // Using a large epsilon here to allow for sloppy drawing can cause the algorithm to miss very
576 // short segments in a converted bezier. So use an epsilon only large enough to cover for
577 // rouding errors in the conversion.
578 int chainingEpsilon = 100; // max dist from one endPt to next startPt in IU
579
581 SHAPE_POLY_SET poly;
582
583 // Stores pairs of (anchor, item) where anchor == 0 -> SEG.A, anchor == 1 -> SEG.B
584 std::map<VECTOR2I, std::vector<std::pair<int, EDA_ITEM*>>> connections;
585 std::deque<EDA_ITEM*> toCheck;
586
587 auto closeEnough =
588 []( const VECTOR2I& aLeft, const VECTOR2I& aRight, int aLimit )
589 {
590 return ( aLeft - aRight ).SquaredEuclideanNorm() <= SEG::Square( aLimit );
591 };
592
593 auto findInsertionPoint =
594 [&]( const VECTOR2I& aPoint ) -> VECTOR2I
595 {
596 if( connections.count( aPoint ) )
597 return aPoint;
598
599 for( const auto& candidatePair : connections )
600 {
601 if( closeEnough( aPoint, candidatePair.first, chainingEpsilon ) )
602 return candidatePair.first;
603 }
604
605 return aPoint;
606 };
607
608 for( EDA_ITEM* item : aItems )
609 {
610 if( std::optional<SEG> seg = getStartEndPoints( item ) )
611 {
612 toCheck.push_back( item );
613 connections[findInsertionPoint( seg->A )].emplace_back( std::make_pair( 0, item ) );
614 connections[findInsertionPoint( seg->B )].emplace_back( std::make_pair( 1, item ) );
615 }
616 }
617
618 while( !toCheck.empty() )
619 {
620 std::vector<BOARD_ITEM*> insertedItems;
621
622 EDA_ITEM* candidate = toCheck.front();
623 toCheck.pop_front();
624
625 if( candidate->GetFlags() & SKIP_STRUCT )
626 continue;
627
628 SHAPE_LINE_CHAIN outline;
629
630 auto insert =
631 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
632 {
633 if( aItem->Type() == PCB_ARC_T
634 || ( aItem->Type() == PCB_SHAPE_T
635 && static_cast<PCB_SHAPE*>( aItem )->GetShape() == SHAPE_T::ARC ) )
636 {
637 SHAPE_ARC arc;
638
639 if( aItem->Type() == PCB_ARC_T )
640 {
641 PCB_ARC* pcb_arc = static_cast<PCB_ARC*>( aItem );
642 arc = *static_cast<SHAPE_ARC*>( pcb_arc->GetEffectiveShape().get() );
643 }
644 else
645 {
646 PCB_SHAPE* pcb_shape = static_cast<PCB_SHAPE*>( aItem );
647 arc = SHAPE_ARC( pcb_shape->GetStart(), pcb_shape->GetArcMid(),
648 pcb_shape->GetEnd(), pcb_shape->GetWidth() );
649 }
650
651 if( aDirection )
652 outline.Append( aAnchor == arc.GetP0() ? arc : arc.Reversed() );
653 else
654 outline.Insert( 0, aAnchor == arc.GetP0() ? arc : arc.Reversed() );
655
656 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
657 }
658 else if( aItem->IsType( { PCB_SHAPE_LOCATE_BEZIER_T } ) )
659 {
660 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
661
662 if( aAnchor == graphic->GetStart() )
663 {
664 for( auto it = graphic->GetBezierPoints().begin();
665 it != graphic->GetBezierPoints().end();
666 ++it )
667 {
668 if( aDirection )
669 outline.Append( *it );
670 else
671 outline.Insert( 0, *it );
672 }
673
674 }
675 else
676 {
677 for( auto it = graphic->GetBezierPoints().rbegin();
678 it != graphic->GetBezierPoints().rend();
679 ++it )
680 {
681 if( aDirection )
682 outline.Append( *it );
683 else
684 outline.Insert( 0, *it );
685 }
686 }
687
688 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
689 }
690 else if( std::optional<SEG> nextSeg = getStartEndPoints( aItem ) )
691 {
692 VECTOR2I& point = ( aAnchor == nextSeg->A ) ? nextSeg->B : nextSeg->A;
693
694 if( aDirection )
695 outline.Append( point );
696 else
697 outline.Insert( 0, point );
698
699 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
700 }
701 };
702
703 // aDirection == true for walking "right" and appending to the end of points
704 // false for walking "left" and prepending to the beginning
705 std::function<void( EDA_ITEM*, const VECTOR2I&, bool )> process =
706 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
707 {
708 if( aItem->GetFlags() & SKIP_STRUCT )
709 return;
710
711 aItem->SetFlags( SKIP_STRUCT );
712
713 insert( aItem, aAnchor, aDirection );
714
715 std::optional<SEG> anchors = getStartEndPoints( aItem );
716 wxASSERT( anchors );
717
718 VECTOR2I nextAnchor = ( aAnchor == anchors->A ) ? anchors->B : anchors->A;
719
720 for( std::pair<int, EDA_ITEM*> pair : connections[nextAnchor] )
721 {
722 if( pair.second == aItem )
723 continue;
724
725 process( pair.second, nextAnchor, aDirection );
726 }
727 };
728
729 std::optional<SEG> anchors = getStartEndPoints( candidate );
730 wxASSERT( anchors );
731
732 // Start with the first object and walk "right"
733 // Note if the first object is an arc, we don't need to insert its first point here, the
734 // whole arc will be inserted at anchor B inside process()
735 if( !( candidate->Type() == PCB_ARC_T
736 || ( candidate->Type() == PCB_SHAPE_T
737 && static_cast<PCB_SHAPE*>( candidate )->GetShape() == SHAPE_T::ARC ) ) )
738 {
739 insert( candidate, anchors->A, true );
740 }
741
742 process( candidate, anchors->B, true );
743
744 // check for any candidates on the "left"
745 EDA_ITEM* left = nullptr;
746
747 for( std::pair<int, EDA_ITEM*> possibleLeft : connections[anchors->A] )
748 {
749 if( possibleLeft.second != candidate )
750 {
751 left = possibleLeft.second;
752 break;
753 }
754 }
755
756 if( left )
757 process( left, anchors->A, false );
758
759 if( outline.PointCount() < 3
760 || !closeEnough( outline.GetPoint( 0 ), outline.GetPoint( -1 ), chainingEpsilon ) )
761 {
762 for( EDA_ITEM* item : insertedItems )
763 item->ClearFlags( SKIP_STRUCT );
764
765 continue;
766 }
767
768 outline.SetClosed( true );
769
770 poly.AddOutline( outline );
771
772 if( aStrategy == BOUNDING_HULL )
773 {
774 for( BOARD_ITEM* item : insertedItems )
775 {
776 item->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError,
777 ERROR_INSIDE, false );
778 }
779 }
780
781 insertedItems.clear();
782 }
783
784 return poly;
785}
786
787
788SHAPE_POLY_SET CONVERT_TOOL::makePolysFromOpenGraphics( const std::deque<EDA_ITEM*>& aItems,
789 int aGap )
790{
792 SHAPE_POLY_SET poly;
793
794 for( EDA_ITEM* item : aItems )
795 {
796 if( item->GetFlags() & SKIP_STRUCT )
797 continue;
798
799 switch( item->Type() )
800 {
801 case PCB_SHAPE_T:
802 {
803 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
804
805 if( shape->IsClosed() )
806 continue;
807
808 shape->TransformShapeToPolygon( poly, UNDEFINED_LAYER, aGap, bds.m_MaxError,
809 ERROR_INSIDE, false );
810 shape->SetFlags( SKIP_STRUCT );
811
812 break;
813 }
814
815 case PCB_TRACE_T:
816 case PCB_ARC_T:
817 case PCB_VIA_T:
818 {
819 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
820
821 track->TransformShapeToPolygon( poly, UNDEFINED_LAYER, aGap, bds.m_MaxError,
822 ERROR_INSIDE, false );
823 track->SetFlags( SKIP_STRUCT );
824
825 break;
826 }
827
828 default:
829 continue;
830 }
831 }
832
833 return poly;
834}
835
836
838 CONVERT_STRATEGY aStrategy )
839{
841 SHAPE_POLY_SET poly;
842
843 for( EDA_ITEM* item : aItems )
844 {
845 if( item->GetFlags() & SKIP_STRUCT )
846 continue;
847
848 switch( item->Type() )
849 {
850 case PCB_SHAPE_T:
851 {
852 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
853 FILL_T wasFilled = shape->GetFillMode();
854
855 if( !shape->IsClosed() )
856 continue;
857
858 if( aStrategy != BOUNDING_HULL )
859 shape->SetFilled( true );
860
862 aStrategy == COPY_LINEWIDTH || aStrategy == CENTERLINE );
863
864 if( aStrategy != BOUNDING_HULL )
865 shape->SetFillMode( wasFilled );
866
867 shape->SetFlags( SKIP_STRUCT );
868 break;
869 }
870
871 case PCB_ZONE_T:
872 poly.Append( *static_cast<ZONE*>( item )->Outline() );
873 item->SetFlags( SKIP_STRUCT );
874 break;
875
876 case PCB_FIELD_T:
877 case PCB_TEXT_T:
878 {
879 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
880 text->TransformTextToPolySet( poly, 0, bds.m_MaxError, ERROR_INSIDE );
881 text->SetFlags( SKIP_STRUCT );
882 break;
883 }
884
885 case PCB_PAD_T:
886 {
887 PAD* pad = static_cast<PAD*>( item );
888 pad->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError, ERROR_INSIDE,
889 false );
890 pad->SetFlags( SKIP_STRUCT );
891 break;
892 }
893
894
895 default:
896 continue;
897 }
898 }
899
900 return poly;
901}
902
903
905{
906 auto& selection = m_selectionTool->RequestSelection(
907 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
908 {
909 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
910 {
911 BOARD_ITEM* item = aCollector[i];
912
913 switch( item->Type() )
914 {
915 case PCB_SHAPE_T:
916 switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
917 {
918 case SHAPE_T::SEGMENT:
919 case SHAPE_T::ARC:
920 case SHAPE_T::POLY:
921 case SHAPE_T::RECTANGLE:
922 break;
923
924 default:
925 aCollector.Remove( item );
926 }
927
928 break;
929
930 case PCB_ZONE_T:
931 break;
932
933 default:
934 aCollector.Remove( item );
935 }
936 }
937 } );
938
939 if( selection.Empty() )
940 return 0;
941
942 auto getPolySet =
943 []( EDA_ITEM* aItem )
944 {
945 SHAPE_POLY_SET set;
946
947 switch( aItem->Type() )
948 {
949 case PCB_ZONE_T:
950 set = *static_cast<ZONE*>( aItem )->Outline();
951 break;
952
953 case PCB_SHAPE_T:
954 {
955 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
956
957 if( graphic->GetShape() == SHAPE_T::POLY )
958 {
959 set = graphic->GetPolyShape();
960 }
961 else if( graphic->GetShape() == SHAPE_T::RECTANGLE )
962 {
963 SHAPE_LINE_CHAIN outline;
964 VECTOR2I start( graphic->GetStart() );
965 VECTOR2I end( graphic->GetEnd() );
966
967 outline.Append( start );
968 outline.Append( VECTOR2I( end.x, start.y ) );
969 outline.Append( end );
970 outline.Append( VECTOR2I( start.x, end.y ) );
971 outline.SetClosed( true );
972
973 set.AddOutline( outline );
974 }
975 else
976 {
977 wxFAIL_MSG( wxT( "Unhandled graphic shape type in PolyToLines - getPolySet" ) );
978 }
979 break;
980 }
981
982 default:
983 wxFAIL_MSG( wxT( "Unhandled type in PolyToLines - getPolySet" ) );
984 break;
985 }
986
987 return set;
988 };
989
990 auto getSegList =
991 []( SHAPE_POLY_SET& aPoly )
992 {
993 std::vector<SEG> segs;
994
995 // Our input should be valid polys, so OK to assert here
996 wxASSERT( aPoly.VertexCount() >= 2 );
997
998 for( int i = 1; i < aPoly.VertexCount(); i++ )
999 segs.emplace_back( SEG( aPoly.CVertex( i - 1 ), aPoly.CVertex( i ) ) );
1000
1001 segs.emplace_back( SEG( aPoly.CVertex( aPoly.VertexCount() - 1 ),
1002 aPoly.CVertex( 0 ) ) );
1003
1004 return segs;
1005 };
1006
1007 BOARD_COMMIT commit( m_frame );
1008 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1009 FOOTPRINT_EDIT_FRAME* fpEditor = dynamic_cast<FOOTPRINT_EDIT_FRAME*>( m_frame );
1010 FOOTPRINT* footprint = nullptr;
1011 PCB_LAYER_ID targetLayer = m_frame->GetActiveLayer();
1012 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1013
1014 if( fpEditor )
1015 footprint = fpEditor->GetBoard()->GetFirstFootprint();
1016
1017 auto handleGraphicSeg =
1018 [&]( EDA_ITEM* aItem )
1019 {
1020 if( aItem->Type() != PCB_SHAPE_T )
1021 return false;
1022
1023 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
1024
1025 if( graphic->GetShape() == SHAPE_T::SEGMENT )
1026 {
1027 PCB_TRACK* track = new PCB_TRACK( parent );
1028
1029 track->SetLayer( targetLayer );
1030 track->SetStart( graphic->GetStart() );
1031 track->SetEnd( graphic->GetEnd() );
1032 track->SetWidth( graphic->GetWidth() );
1033 commit.Add( track );
1034
1035 return true;
1036 }
1037 else if( graphic->GetShape() == SHAPE_T::ARC )
1038 {
1039 PCB_ARC* arc = new PCB_ARC( parent );
1040
1041 arc->SetLayer( targetLayer );
1042 arc->SetStart( graphic->GetStart() );
1043 arc->SetEnd( graphic->GetEnd() );
1044 arc->SetMid( graphic->GetArcMid() );
1045 arc->SetWidth( graphic->GetWidth() );
1046 commit.Add( arc );
1047
1048 return true;
1049 }
1050
1051 return false;
1052 };
1053
1054 if( aEvent.IsAction( &PCB_ACTIONS::convertToTracks ) )
1055 {
1056 if( !IsCopperLayer( targetLayer ) )
1057 {
1058 targetLayer = frame->SelectOneLayer( F_Cu, LSET::AllNonCuMask() );
1059
1060 if( targetLayer == UNDEFINED_LAYER ) // User canceled
1061 return true;
1062 }
1063 }
1064 else
1065 {
1066 CONVERT_SETTINGS_DIALOG dlg( m_frame, &m_userSettings, false, false, false );
1067
1068 if( dlg.ShowModal() != wxID_OK )
1069 return true;
1070 }
1071
1072 for( EDA_ITEM* item : selection )
1073 {
1074 if( handleGraphicSeg( item ) )
1075 continue;
1076
1077 SHAPE_POLY_SET polySet = getPolySet( item );
1078 std::vector<SEG> segs = getSegList( polySet );
1079
1080 if( aEvent.IsAction( &PCB_ACTIONS::convertToLines ) )
1081 {
1082 for( SEG& seg : segs )
1083 {
1084 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1085
1086 graphic->SetLayer( targetLayer );
1087 graphic->SetStart( VECTOR2I( seg.A ) );
1088 graphic->SetEnd( VECTOR2I( seg.B ) );
1089 commit.Add( graphic );
1090 }
1091 }
1092 else
1093 {
1094 // I am really unsure converting a polygon to "tracks" (i.e. segments on
1095 // copper layers) make sense for footprints, but anyway this code exists
1096 if( fpEditor )
1097 {
1098 // Creating segments on copper layer
1099 for( SEG& seg : segs )
1100 {
1101 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1102 graphic->SetLayer( targetLayer );
1103 graphic->SetStart( VECTOR2I( seg.A ) );
1104 graphic->SetEnd( VECTOR2I( seg.B ) );
1105 commit.Add( graphic );
1106 }
1107 }
1108 else
1109 {
1110 // Creating tracks
1111 for( SEG& seg : segs )
1112 {
1113 PCB_TRACK* track = new PCB_TRACK( parent );
1114
1115 track->SetLayer( targetLayer );
1116 track->SetStart( VECTOR2I( seg.A ) );
1117 track->SetEnd( VECTOR2I( seg.B ) );
1118 commit.Add( track );
1119 }
1120 }
1121 }
1122 }
1123
1124 if( m_userSettings.m_DeleteOriginals )
1125 {
1126 PCB_SELECTION selectionCopy = selection;
1127 m_selectionTool->ClearSelection();
1128
1129 for( EDA_ITEM* item : selectionCopy )
1130 commit.Remove( item );
1131 }
1132
1133 commit.Push( _( "Create Lines" ) );
1134
1135 return 0;
1136}
1137
1138
1140{
1141 auto& selection = m_selectionTool->RequestSelection(
1142 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1143 {
1144 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1145 {
1146 BOARD_ITEM* item = aCollector[i];
1147
1148 if( !( item->Type() == PCB_SHAPE_T ||
1149 item->Type() == PCB_TRACE_T ) )
1150 {
1151 aCollector.Remove( item );
1152 }
1153 }
1154 } );
1155
1156 EDA_ITEM* source = selection.Front();
1157 VECTOR2I start, end, mid;
1158
1159 // Offset the midpoint along the normal a little bit so that it's more obviously an arc
1160 const double offsetRatio = 0.1;
1161
1162 if( std::optional<SEG> seg = getStartEndPoints( source ) )
1163 {
1164 start = seg->A;
1165 end = seg->B;
1166
1167 VECTOR2I normal = ( seg->B - seg->A ).Perpendicular().Resize( offsetRatio * seg->Length() );
1168 mid = seg->Center() + normal;
1169 }
1170 else
1171 {
1172 return -1;
1173 }
1174
1175 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1176 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1177
1178 BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( source );
1179
1180 // Don't continue processing if we don't actually have a board item
1181 if( !boardItem )
1182 return 0;
1183
1184 PCB_LAYER_ID layer = boardItem->GetLayer();
1185
1186 BOARD_COMMIT commit( m_frame );
1187
1188 if( source->Type() == PCB_SHAPE_T )
1189 {
1190 PCB_SHAPE* line = static_cast<PCB_SHAPE*>( source );
1191 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1192
1193 VECTOR2I center = CalcArcCenter( start, mid, end );
1194
1195 arc->SetFilled( false );
1196 arc->SetLayer( layer );
1197 arc->SetStroke( line->GetStroke() );
1198
1199 arc->SetCenter( VECTOR2I( center ) );
1200 arc->SetStart( VECTOR2I( start ) );
1201 arc->SetEnd( VECTOR2I( end ) );
1202
1203 commit.Add( arc );
1204 }
1205 else
1206 {
1207 wxASSERT( source->Type() == PCB_TRACE_T );
1208 PCB_TRACK* line = static_cast<PCB_TRACK*>( source );
1209 PCB_ARC* arc = new PCB_ARC( parent );
1210
1211 arc->SetLayer( layer );
1212 arc->SetWidth( line->GetWidth() );
1213 arc->SetStart( VECTOR2I( start ) );
1214 arc->SetMid( VECTOR2I( mid ) );
1215 arc->SetEnd( VECTOR2I( end ) );
1216
1217 commit.Add( arc );
1218 }
1219
1220 commit.Push( _( "Create Arc" ) );
1221
1222 return 0;
1223}
1224
1225
1226std::optional<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem )
1227{
1228 switch( aItem->Type() )
1229 {
1230 case PCB_SHAPE_T:
1231 {
1232 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
1233
1234 switch( shape->GetShape() )
1235 {
1236 case SHAPE_T::SEGMENT:
1237 case SHAPE_T::ARC:
1238 case SHAPE_T::POLY:
1239 case SHAPE_T::BEZIER:
1240 if( shape->GetStart() == shape->GetEnd() )
1241 return std::nullopt;
1242
1243 return std::make_optional<SEG>( VECTOR2I( shape->GetStart() ),
1244 VECTOR2I( shape->GetEnd() ) );
1245
1246 default:
1247 return std::nullopt;
1248 }
1249 }
1250
1251 case PCB_TRACE_T:
1252 {
1253 PCB_TRACK* line = static_cast<PCB_TRACK*>( aItem );
1254 return std::make_optional<SEG>( VECTOR2I( line->GetStart() ), VECTOR2I( line->GetEnd() ) );
1255 }
1256
1257 case PCB_ARC_T:
1258 {
1259 PCB_ARC* arc = static_cast<PCB_ARC*>( aItem );
1260 return std::make_optional<SEG>( VECTOR2I( arc->GetStart() ), VECTOR2I( arc->GetEnd() ) );
1261 }
1262
1263 default:
1264 return std::nullopt;
1265 }
1266}
1267
1268
1270{
1277}
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
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]
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:77
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:226
virtual STROKE_PARAMS GetStroke() const
Definition: board_item.cpp:85
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:260
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:248
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:433
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:794
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
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 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:105
virtual ~CONVERT_TOOL()
CONVERT_SETTINGS m_userSettings
Definition: convert_tool.h:108
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:106
PCB_BASE_FRAME * m_frame
Definition: convert_tool.h:107
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:210
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
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:88
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:242
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:126
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:100
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition: eda_item.h:175
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:129
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:545
FILL_T GetFillMode() const
Definition: eda_shape.h:102
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:274
void SetFilled(bool aFlag)
Definition: eda_shape.h:96
SHAPE_T GetShape() const
Definition: eda_shape.h:120
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:282
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:162
bool IsClosed() const
Definition: eda_shape.cpp:212
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:129
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:125
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:119
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition: eda_shape.h:257
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:166
void SetFillMode(FILL_T aFill)
Definition: eda_shape.h:101
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:563
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:206
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition: lset.cpp:884
Definition: pad.h:53
static TOOL_ACTION convertToKeepout
Definition: pcb_actions.h:576
static TOOL_ACTION convertToTracks
Definition: pcb_actions.h:579
static TOOL_ACTION convertToLines
Definition: pcb_actions.h:577
static TOOL_ACTION convertToZone
Definition: pcb_actions.h:575
static TOOL_ACTION convertToPoly
Definition: pcb_actions.h:574
static TOOL_ACTION convertToArc
Definition: pcb_actions.h:578
static TOOL_ACTION createArray
Tool for creating an array of objects.
Definition: pcb_actions.h:486
void SetMid(const VECTOR2I &aMid)
Definition: pcb_track.h:315
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:1623
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:285
virtual PCB_LAYER_ID GetActiveLayer() const
BOARD * GetBoard() const
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:367
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:315
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:786
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:85
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:86
void SetWidth(int aWidth)
Definition: pcb_track.h:107
int GetWidth() const
Definition: pcb_track.h:108
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:110
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:113
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:1629
const VECTOR2I & GetStart() const
Definition: pcb_track.h:114
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:111
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.
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:633
const VECTOR2I & GetP0() const
Definition: shape_arc.h:113
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:81
int GetWidth() const
Definition: stroke_params.h:91
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:217
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:127
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:71
void SetIsRuleArea(bool aEnable)
void ExportSetting(ZONE &aTarget, bool aFullExport=true) const
Function ExportSetting copy settings to a given zone.
wxString m_Name
Definition: zone_settings.h:98
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:910
SHAPE_POLY_SET * Outline()
Definition: zone.h:336
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:186
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, CONVERT_SETTINGS *aConvertSettings)
Function InvokeRuleAreaEditor invokes up a modal dialog window for copper zone editing.
#define _(s)
#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
@ FRAME_PCB_EDITOR
Definition: frame_type.h:42
@ FRAME_FOOTPRINT_EDITOR
Definition: frame_type.h:43
@ ERROR_OUTSIDE
@ ERROR_INSIDE
bool IsNonCopperLayer(int aLayerId)
Test whether a layer is a non copper layer.
Definition: layer_ids.h:892
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:881
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ F_Cu
Definition: layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
CONVERT_STRATEGY
@ COPY_LINEWIDTH
@ CENTERLINE
@ BOUNDING_HULL
static PGM_BASE * process
Definition: pgm_base.cpp:1057
#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
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:520
@ 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
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:118
VECTOR2< int > VECTOR2I
Definition: vector2d.h:602