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
262 static const std::vector<KICAD_T> padTypes = { PCB_PAD_T };
263 static const std::vector<KICAD_T> segmentTypes = { PCB_TRACE_T,
265 static const std::vector<KICAD_T> shapeTypes = { PCB_SHAPE_LOCATE_SEGMENT_T,
271 PCB_TEXT_T };
272 static const std::vector<KICAD_T> trackTypes = { PCB_TRACE_T,
273 PCB_ARC_T,
274 PCB_VIA_T };
275 static const std::vector<KICAD_T> toTrackTypes = { PCB_SHAPE_LOCATE_SEGMENT_T,
277 static const std::vector<KICAD_T> polyTypes = { PCB_ZONE_T,
280
281 auto shapes = S_C::OnlyTypes( shapeTypes ) && P_S_C::SameLayer();
282 auto graphicToTrack = S_C::OnlyTypes( toTrackTypes );
283 auto anyTracks = S_C::MoreThan( 0 ) && S_C::OnlyTypes( trackTypes ) && P_S_C::SameLayer();
284 auto anyPolys = S_C::OnlyTypes( polyTypes );
285 auto anyPads = S_C::OnlyTypes( padTypes );
286
287 auto canCreateArcs = S_C::Count( 1 ) && S_C::OnlyTypes( segmentTypes );
288 auto canCreateArray = S_C::MoreThan( 0 );
289 auto canCreatePoly = shapes || anyPolys || anyTracks;
290
292 canCreatePoly = shapes || anyPolys || anyTracks || anyPads;
293
294 auto canCreateLines = anyPolys;
295 auto canCreateTracks = anyPolys || graphicToTrack;
296 auto canCreate = canCreatePoly
297 || canCreateLines
298 || canCreateTracks
299 || canCreateArcs
300 || canCreateArray;
301
302 m_menu->AddItem( PCB_ACTIONS::convertToPoly, canCreatePoly );
303
305 m_menu->AddItem( PCB_ACTIONS::convertToZone, canCreatePoly );
306
308 m_menu->AddItem( PCB_ACTIONS::convertToLines, canCreateLines );
309 m_menu->AppendSeparator();
310
311 // Currently the code exists, but tracks are not really existing in footprints
312 // only segments on copper layers
314 m_menu->AddItem( PCB_ACTIONS::convertToTracks, canCreateTracks );
315
316 m_menu->AddItem( PCB_ACTIONS::convertToArc, canCreateArcs );
317
318 m_menu->AppendSeparator();
319 m_menu->AddItem( PCB_ACTIONS::createArray, canCreateArray );
320
322 selToolMenu.AddMenu( m_menu, canCreate, 100 );
323
324 return true;
325}
326
327
329{
334}
335
336
338{
340 std::vector<SHAPE_POLY_SET> polys;
341 PCB_LAYER_ID destLayer = m_frame->GetActiveLayer();
342 FOOTPRINT* parentFootprint = nullptr;
343
345 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
346 {
347 } );
348
349 if( selection.Empty() )
350 return 0;
351
352 auto getPolys =
353 [&]( CONVERT_SETTINGS cfg )
354 {
355 polys.clear();
356
357 for( EDA_ITEM* item : selection )
358 item->ClearTempFlags();
359
360 SHAPE_POLY_SET polySet;
361
362 polySet.Append( makePolysFromClosedGraphics( selection.GetItems(), cfg.m_Strategy ) );
363
364 if( cfg.m_Strategy == BOUNDING_HULL )
365 {
366 polySet.Append( makePolysFromOpenGraphics( selection.GetItems(), cfg.m_Gap ) );
367
368 polySet.ClearArcs();
370 polySet.Inflate( cfg.m_Gap, CORNER_STRATEGY::ROUND_ALL_CORNERS, bds.m_MaxError,
372 }
373 else
374 {
375 polySet.Append( makePolysFromChainedSegs( selection.GetItems(), cfg.m_Strategy ) );
376 }
377
378 if( polySet.IsEmpty() )
379 return false;
380
381 for( int ii = 0; ii < polySet.OutlineCount(); ++ii )
382 {
383 polys.emplace_back( SHAPE_POLY_SET( polySet.COutline( ii ) ) );
384
385 for( int jj = 0; jj < polySet.HoleCount( ii ); ++jj )
386 polys.back().AddHole( polySet.Hole( ii, jj ) );
387 }
388
389 return true;
390 };
391
392 // Pre-flight getPolys() to see if there's anything to convert.
393 CONVERT_SETTINGS preflightSettings = m_userSettings;
394 preflightSettings.m_Strategy = BOUNDING_HULL;
395
396 if( !getPolys( preflightSettings ) )
397 return 0;
398
399 if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( selection.Front() ) )
400 parentFootprint = item->GetParentFootprint();
401
403 BOARD_COMMIT commit( m_frame );
404
405 if( aEvent.IsAction( &PCB_ACTIONS::convertToPoly ) )
406 {
407 bool showCopyLineWidth = true;
408
409 // No copy-line-width option for pads
410 if( dynamic_cast<PAD*>( selection.Front() ) )
411 {
414
415 showCopyLineWidth = false;
416 }
417
418 CONVERT_SETTINGS previousSettings = m_userSettings;
419
420 CONVERT_SETTINGS_DIALOG dlg( m_frame, &m_userSettings, showCopyLineWidth, true, true );
421
422 if( dlg.ShowModal() != wxID_OK )
423 return 0;
424
425 CONVERT_SETTINGS resolvedSettings = m_userSettings;
426
427 if( resolvedSettings.m_Strategy != CENTERLINE )
428 {
429 if( resolvedSettings.m_LineWidth == 0 )
430 resolvedSettings.m_LineWidth = bds.m_LineThickness[ bds.GetLayerClass( layer ) ];
431 }
432
433 if( resolvedSettings.m_Strategy == BOUNDING_HULL )
434 {
435 if( resolvedSettings.m_Gap > 0 )
436 resolvedSettings.m_Gap += KiROUND( (double) resolvedSettings.m_LineWidth / 2 );
437 }
438
439 if( !getPolys( resolvedSettings ) )
440 {
441 wxString msg;
442
443 if( resolvedSettings.m_Strategy == BOUNDING_HULL )
444 msg = _( "Resulting polygon would be empty" );
445 else
446 msg = _( "Objects must form a closed shape" );
447
448 DisplayErrorMessage( m_frame, _( "Could not convert selection" ), msg );
449
450 m_userSettings = previousSettings;
451 return 0;
452 }
453
454 for( const SHAPE_POLY_SET& poly : polys )
455 {
456 PCB_SHAPE* graphic = new PCB_SHAPE( parentFootprint );
457
458 if( resolvedSettings.m_Strategy == COPY_LINEWIDTH )
459 {
460 BOARD_ITEM* topLeftItem = nullptr;
461 VECTOR2I pos;
462
463 for( EDA_ITEM* item : selection )
464 {
465 if( BOARD_ITEM* candidate = dynamic_cast<BOARD_ITEM*>( item ) )
466 {
467 if( candidate->HasLineStroke() )
468 {
469 pos = candidate->GetPosition();
470
471 if( !topLeftItem
472 || ( pos.x < topLeftItem->GetPosition().x )
473 || ( topLeftItem->GetPosition().x == pos.x
474 && pos.y < topLeftItem->GetPosition().y ) )
475 {
476 topLeftItem = candidate;
477 resolvedSettings.m_LineWidth = topLeftItem->GetStroke().GetWidth();
478 }
479 }
480 }
481 }
482 }
483
484 graphic->SetShape( SHAPE_T::POLY );
485 graphic->SetStroke( STROKE_PARAMS( resolvedSettings.m_LineWidth, LINE_STYLE::SOLID,
486 COLOR4D::UNSPECIFIED ) );
487 graphic->SetFilled( resolvedSettings.m_Strategy == CENTERLINE );
488 graphic->SetLayer( destLayer );
489 graphic->SetPolyShape( poly );
490
491 commit.Add( graphic );
492 }
493 }
494 else
495 {
496 // Creating zone or keepout
497 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
498 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
499 ZONE_SETTINGS zoneInfo = bds.GetDefaultZoneSettings();
500
501 bool nonCopper = IsNonCopperLayer( destLayer );
502 zoneInfo.m_Layers.reset().set( destLayer );
503 zoneInfo.m_Name.Empty();
504
505 int ret;
506
507 // No copy-line-width option for zones/keepouts
510
512 {
513 zoneInfo.SetIsRuleArea( true );
514 ret = InvokeRuleAreaEditor( frame, &zoneInfo, &m_userSettings );
515 }
516 else if( nonCopper )
517 {
518 zoneInfo.SetIsRuleArea( false );
519 ret = InvokeNonCopperZonesEditor( frame, &zoneInfo, &m_userSettings );
520 }
521 else
522 {
523 zoneInfo.SetIsRuleArea( false );
524 ret = InvokeCopperZonesEditor( frame, &zoneInfo, &m_userSettings );
525 }
526
527 if( ret == wxID_CANCEL )
528 return 0;
529
530 if( !getPolys( m_userSettings ) )
531 return 0;
532
533 for( const SHAPE_POLY_SET& poly : polys )
534 {
535 ZONE* zone = new ZONE( parent );
536
537 *zone->Outline() = poly;
538 zone->HatchBorder();
539
540 zoneInfo.ExportSetting( *zone );
541
542 commit.Add( zone );
543 }
544 }
545
547 {
548 PCB_SELECTION selectionCopy = selection;
550
551 for( EDA_ITEM* item : selectionCopy )
552 {
553 if( item->GetFlags() & SKIP_STRUCT )
554 commit.Remove( item );
555 }
556 }
557
558 if( aEvent.IsAction( &PCB_ACTIONS::convertToPoly ) )
559 {
561 commit.Push( _( "Convert to Polygon" ) );
562 else
563 commit.Push( _( "Create Polygon" ) );
564 }
565 else
566 {
568 commit.Push( _( "Convert to Zone" ) );
569 else
570 commit.Push( _( "Create Zone" ) );
571 }
572
573 return 0;
574}
575
576
577SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM*>& aItems,
578 CONVERT_STRATEGY aStrategy )
579{
580 // TODO: This code has a somewhat-similar purpose to ConvertOutlineToPolygon but is slightly
581 // different, so this remains a separate algorithm. It might be nice to analyze the dfiferences
582 // in requirements and refactor this.
583
584 // Using a large epsilon here to allow for sloppy drawing can cause the algorithm to miss very
585 // short segments in a converted bezier. So use an epsilon only large enough to cover for
586 // rouding errors in the conversion.
587 int chainingEpsilon = 100; // max dist from one endPt to next startPt in IU
588
590 SHAPE_POLY_SET poly;
591
592 // Stores pairs of (anchor, item) where anchor == 0 -> SEG.A, anchor == 1 -> SEG.B
593 std::map<VECTOR2I, std::vector<std::pair<int, EDA_ITEM*>>> connections;
594 std::deque<EDA_ITEM*> toCheck;
595
596 auto closeEnough =
597 []( const VECTOR2I& aLeft, const VECTOR2I& aRight, int aLimit )
598 {
599 return ( aLeft - aRight ).SquaredEuclideanNorm() <= SEG::Square( aLimit );
600 };
601
602 auto findInsertionPoint =
603 [&]( const VECTOR2I& aPoint ) -> VECTOR2I
604 {
605 if( connections.count( aPoint ) )
606 return aPoint;
607
608 for( const auto& candidatePair : connections )
609 {
610 if( closeEnough( aPoint, candidatePair.first, chainingEpsilon ) )
611 return candidatePair.first;
612 }
613
614 return aPoint;
615 };
616
617 for( EDA_ITEM* item : aItems )
618 {
619 if( std::optional<SEG> seg = getStartEndPoints( item ) )
620 {
621 toCheck.push_back( item );
622 connections[findInsertionPoint( seg->A )].emplace_back( std::make_pair( 0, item ) );
623 connections[findInsertionPoint( seg->B )].emplace_back( std::make_pair( 1, item ) );
624 }
625 }
626
627 while( !toCheck.empty() )
628 {
629 std::vector<BOARD_ITEM*> insertedItems;
630
631 EDA_ITEM* candidate = toCheck.front();
632 toCheck.pop_front();
633
634 if( candidate->GetFlags() & SKIP_STRUCT )
635 continue;
636
637 SHAPE_LINE_CHAIN outline;
638
639 auto insert =
640 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
641 {
642 if( aItem->Type() == PCB_ARC_T
643 || ( aItem->Type() == PCB_SHAPE_T
644 && static_cast<PCB_SHAPE*>( aItem )->GetShape() == SHAPE_T::ARC ) )
645 {
646 SHAPE_ARC arc;
647
648 if( aItem->Type() == PCB_ARC_T )
649 {
650 PCB_ARC* pcb_arc = static_cast<PCB_ARC*>( aItem );
651 arc = *static_cast<SHAPE_ARC*>( pcb_arc->GetEffectiveShape().get() );
652 }
653 else
654 {
655 PCB_SHAPE* pcb_shape = static_cast<PCB_SHAPE*>( aItem );
656 arc = SHAPE_ARC( pcb_shape->GetStart(), pcb_shape->GetArcMid(),
657 pcb_shape->GetEnd(), pcb_shape->GetWidth() );
658 }
659
660 if( aDirection )
661 outline.Append( aAnchor == arc.GetP0() ? arc : arc.Reversed() );
662 else
663 outline.Insert( 0, aAnchor == arc.GetP0() ? arc : arc.Reversed() );
664
665 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
666 }
667 else if( aItem->IsType( { PCB_SHAPE_LOCATE_BEZIER_T } ) )
668 {
669 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
670
671 if( aAnchor == graphic->GetStart() )
672 {
673 for( auto it = graphic->GetBezierPoints().begin();
674 it != graphic->GetBezierPoints().end();
675 ++it )
676 {
677 if( aDirection )
678 outline.Append( *it );
679 else
680 outline.Insert( 0, *it );
681 }
682
683 }
684 else
685 {
686 for( auto it = graphic->GetBezierPoints().rbegin();
687 it != graphic->GetBezierPoints().rend();
688 ++it )
689 {
690 if( aDirection )
691 outline.Append( *it );
692 else
693 outline.Insert( 0, *it );
694 }
695 }
696
697 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
698 }
699 else if( std::optional<SEG> nextSeg = getStartEndPoints( aItem ) )
700 {
701 VECTOR2I& point = ( aAnchor == nextSeg->A ) ? nextSeg->B : nextSeg->A;
702
703 if( aDirection )
704 outline.Append( point );
705 else
706 outline.Insert( 0, point );
707
708 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
709 }
710 };
711
712 // aDirection == true for walking "right" and appending to the end of points
713 // false for walking "left" and prepending to the beginning
714 std::function<void( EDA_ITEM*, const VECTOR2I&, bool )> process =
715 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
716 {
717 if( aItem->GetFlags() & SKIP_STRUCT )
718 return;
719
720 aItem->SetFlags( SKIP_STRUCT );
721
722 insert( aItem, aAnchor, aDirection );
723
724 std::optional<SEG> anchors = getStartEndPoints( aItem );
725 wxASSERT( anchors );
726
727 VECTOR2I nextAnchor = ( aAnchor == anchors->A ) ? anchors->B : anchors->A;
728
729 for( std::pair<int, EDA_ITEM*> pair : connections[nextAnchor] )
730 {
731 if( pair.second == aItem )
732 continue;
733
734 process( pair.second, nextAnchor, aDirection );
735 }
736 };
737
738 std::optional<SEG> anchors = getStartEndPoints( candidate );
739 wxASSERT( anchors );
740
741 // Start with the first object and walk "right"
742 // Note if the first object is an arc, we don't need to insert its first point here, the
743 // whole arc will be inserted at anchor B inside process()
744 if( !( candidate->Type() == PCB_ARC_T
745 || ( candidate->Type() == PCB_SHAPE_T
746 && static_cast<PCB_SHAPE*>( candidate )->GetShape() == SHAPE_T::ARC ) ) )
747 {
748 insert( candidate, anchors->A, true );
749 }
750
751 process( candidate, anchors->B, true );
752
753 // check for any candidates on the "left"
754 EDA_ITEM* left = nullptr;
755
756 for( std::pair<int, EDA_ITEM*> possibleLeft : connections[anchors->A] )
757 {
758 if( possibleLeft.second != candidate )
759 {
760 left = possibleLeft.second;
761 break;
762 }
763 }
764
765 if( left )
766 process( left, anchors->A, false );
767
768 if( outline.PointCount() < 3
769 || !closeEnough( outline.GetPoint( 0 ), outline.GetPoint( -1 ), chainingEpsilon ) )
770 {
771 for( EDA_ITEM* item : insertedItems )
772 item->ClearFlags( SKIP_STRUCT );
773
774 continue;
775 }
776
777 outline.SetClosed( true );
778
779 poly.AddOutline( outline );
780
781 if( aStrategy == BOUNDING_HULL )
782 {
783 for( BOARD_ITEM* item : insertedItems )
784 {
785 item->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError,
786 ERROR_INSIDE, false );
787 }
788 }
789
790 insertedItems.clear();
791 }
792
793 return poly;
794}
795
796
797SHAPE_POLY_SET CONVERT_TOOL::makePolysFromOpenGraphics( const std::deque<EDA_ITEM*>& aItems,
798 int aGap )
799{
801 SHAPE_POLY_SET poly;
802
803 for( EDA_ITEM* item : aItems )
804 {
805 if( item->GetFlags() & SKIP_STRUCT )
806 continue;
807
808 switch( item->Type() )
809 {
810 case PCB_SHAPE_T:
811 {
812 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
813
814 if( shape->IsClosed() )
815 continue;
816
817 shape->TransformShapeToPolygon( poly, UNDEFINED_LAYER, aGap, bds.m_MaxError,
818 ERROR_INSIDE, false );
819 shape->SetFlags( SKIP_STRUCT );
820
821 break;
822 }
823
824 case PCB_TRACE_T:
825 case PCB_ARC_T:
826 case PCB_VIA_T:
827 {
828 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
829
830 track->TransformShapeToPolygon( poly, UNDEFINED_LAYER, aGap, bds.m_MaxError,
831 ERROR_INSIDE, false );
832 track->SetFlags( SKIP_STRUCT );
833
834 break;
835 }
836
837 default:
838 continue;
839 }
840 }
841
842 return poly;
843}
844
845
847 CONVERT_STRATEGY aStrategy )
848{
850 SHAPE_POLY_SET poly;
851
852 for( EDA_ITEM* item : aItems )
853 {
854 if( item->GetFlags() & SKIP_STRUCT )
855 continue;
856
857 switch( item->Type() )
858 {
859 case PCB_SHAPE_T:
860 {
861 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
862 FILL_T wasFilled = shape->GetFillMode();
863
864 if( !shape->IsClosed() )
865 continue;
866
867 if( aStrategy != BOUNDING_HULL )
868 shape->SetFilled( true );
869
871 aStrategy == COPY_LINEWIDTH || aStrategy == CENTERLINE );
872
873 if( aStrategy != BOUNDING_HULL )
874 shape->SetFillMode( wasFilled );
875
876 shape->SetFlags( SKIP_STRUCT );
877 break;
878 }
879
880 case PCB_ZONE_T:
881 poly.Append( *static_cast<ZONE*>( item )->Outline() );
882 item->SetFlags( SKIP_STRUCT );
883 break;
884
885 case PCB_FIELD_T:
886 case PCB_TEXT_T:
887 {
888 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
889 text->TransformTextToPolySet( poly, 0, bds.m_MaxError, ERROR_INSIDE );
890 text->SetFlags( SKIP_STRUCT );
891 break;
892 }
893
894 case PCB_PAD_T:
895 {
896 PAD* pad = static_cast<PAD*>( item );
897 pad->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, bds.m_MaxError, ERROR_INSIDE,
898 false );
899 pad->SetFlags( SKIP_STRUCT );
900 break;
901 }
902
903
904 default:
905 continue;
906 }
907 }
908
909 return poly;
910}
911
912
914{
915 auto& selection = m_selectionTool->RequestSelection(
916 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
917 {
918 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
919 {
920 BOARD_ITEM* item = aCollector[i];
921
922 switch( item->Type() )
923 {
924 case PCB_SHAPE_T:
925 switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
926 {
927 case SHAPE_T::SEGMENT:
928 case SHAPE_T::ARC:
929 case SHAPE_T::POLY:
930 case SHAPE_T::RECTANGLE:
931 break;
932
933 default:
934 aCollector.Remove( item );
935 }
936
937 break;
938
939 case PCB_ZONE_T:
940 break;
941
942 default:
943 aCollector.Remove( item );
944 }
945 }
946 } );
947
948 if( selection.Empty() )
949 return 0;
950
951 auto getPolySet =
952 []( EDA_ITEM* aItem )
953 {
954 SHAPE_POLY_SET set;
955
956 switch( aItem->Type() )
957 {
958 case PCB_ZONE_T:
959 set = *static_cast<ZONE*>( aItem )->Outline();
960 break;
961
962 case PCB_SHAPE_T:
963 {
964 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
965
966 if( graphic->GetShape() == SHAPE_T::POLY )
967 {
968 set = graphic->GetPolyShape();
969 }
970 else if( graphic->GetShape() == SHAPE_T::RECTANGLE )
971 {
972 SHAPE_LINE_CHAIN outline;
973 VECTOR2I start( graphic->GetStart() );
974 VECTOR2I end( graphic->GetEnd() );
975
976 outline.Append( start );
977 outline.Append( VECTOR2I( end.x, start.y ) );
978 outline.Append( end );
979 outline.Append( VECTOR2I( start.x, end.y ) );
980 outline.SetClosed( true );
981
982 set.AddOutline( outline );
983 }
984 else
985 {
986 wxFAIL_MSG( wxT( "Unhandled graphic shape type in PolyToLines - getPolySet" ) );
987 }
988 break;
989 }
990
991 default:
992 wxFAIL_MSG( wxT( "Unhandled type in PolyToLines - getPolySet" ) );
993 break;
994 }
995
996 return set;
997 };
998
999 auto getSegList =
1000 []( SHAPE_POLY_SET& aPoly )
1001 {
1002 std::vector<SEG> segs;
1003
1004 // Our input should be valid polys, so OK to assert here
1005 wxASSERT( aPoly.VertexCount() >= 2 );
1006
1007 for( int i = 1; i < aPoly.VertexCount(); i++ )
1008 segs.emplace_back( SEG( aPoly.CVertex( i - 1 ), aPoly.CVertex( i ) ) );
1009
1010 segs.emplace_back( SEG( aPoly.CVertex( aPoly.VertexCount() - 1 ),
1011 aPoly.CVertex( 0 ) ) );
1012
1013 return segs;
1014 };
1015
1016 BOARD_COMMIT commit( m_frame );
1017 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1018 FOOTPRINT_EDIT_FRAME* fpEditor = dynamic_cast<FOOTPRINT_EDIT_FRAME*>( m_frame );
1019 FOOTPRINT* footprint = nullptr;
1020 PCB_LAYER_ID targetLayer = m_frame->GetActiveLayer();
1021 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1022
1023 if( fpEditor )
1024 footprint = fpEditor->GetBoard()->GetFirstFootprint();
1025
1026 auto handleGraphicSeg =
1027 [&]( EDA_ITEM* aItem )
1028 {
1029 if( aItem->Type() != PCB_SHAPE_T )
1030 return false;
1031
1032 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
1033
1034 if( graphic->GetShape() == SHAPE_T::SEGMENT )
1035 {
1036 PCB_TRACK* track = new PCB_TRACK( parent );
1037
1038 track->SetLayer( targetLayer );
1039 track->SetStart( graphic->GetStart() );
1040 track->SetEnd( graphic->GetEnd() );
1041 track->SetWidth( graphic->GetWidth() );
1042 commit.Add( track );
1043
1044 return true;
1045 }
1046 else if( graphic->GetShape() == SHAPE_T::ARC )
1047 {
1048 PCB_ARC* arc = new PCB_ARC( parent );
1049
1050 arc->SetLayer( targetLayer );
1051 arc->SetStart( graphic->GetStart() );
1052 arc->SetEnd( graphic->GetEnd() );
1053 arc->SetMid( graphic->GetArcMid() );
1054 arc->SetWidth( graphic->GetWidth() );
1055 commit.Add( arc );
1056
1057 return true;
1058 }
1059
1060 return false;
1061 };
1062
1063 if( aEvent.IsAction( &PCB_ACTIONS::convertToTracks ) )
1064 {
1065 if( !IsCopperLayer( targetLayer ) )
1066 {
1067 targetLayer = frame->SelectOneLayer( F_Cu, LSET::AllNonCuMask() );
1068
1069 if( targetLayer == UNDEFINED_LAYER ) // User canceled
1070 return true;
1071 }
1072 }
1073 else
1074 {
1075 CONVERT_SETTINGS_DIALOG dlg( m_frame, &m_userSettings, false, false, false );
1076
1077 if( dlg.ShowModal() != wxID_OK )
1078 return true;
1079 }
1080
1081 for( EDA_ITEM* item : selection )
1082 {
1083 if( handleGraphicSeg( item ) )
1084 continue;
1085
1086 SHAPE_POLY_SET polySet = getPolySet( item );
1087 std::vector<SEG> segs = getSegList( polySet );
1088
1089 if( aEvent.IsAction( &PCB_ACTIONS::convertToLines ) )
1090 {
1091 for( SEG& seg : segs )
1092 {
1093 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1094
1095 graphic->SetLayer( targetLayer );
1096 graphic->SetStart( VECTOR2I( seg.A ) );
1097 graphic->SetEnd( VECTOR2I( seg.B ) );
1098 commit.Add( graphic );
1099 }
1100 }
1101 else
1102 {
1103 // I am really unsure converting a polygon to "tracks" (i.e. segments on
1104 // copper layers) make sense for footprints, but anyway this code exists
1105 if( fpEditor )
1106 {
1107 // Creating segments on copper layer
1108 for( SEG& seg : segs )
1109 {
1110 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1111 graphic->SetLayer( targetLayer );
1112 graphic->SetStart( VECTOR2I( seg.A ) );
1113 graphic->SetEnd( VECTOR2I( seg.B ) );
1114 commit.Add( graphic );
1115 }
1116 }
1117 else
1118 {
1119 // Creating tracks
1120 for( SEG& seg : segs )
1121 {
1122 PCB_TRACK* track = new PCB_TRACK( parent );
1123
1124 track->SetLayer( targetLayer );
1125 track->SetStart( VECTOR2I( seg.A ) );
1126 track->SetEnd( VECTOR2I( seg.B ) );
1127 commit.Add( track );
1128 }
1129 }
1130 }
1131 }
1132
1133 if( m_userSettings.m_DeleteOriginals )
1134 {
1135 PCB_SELECTION selectionCopy = selection;
1136 m_selectionTool->ClearSelection();
1137
1138 for( EDA_ITEM* item : selectionCopy )
1139 commit.Remove( item );
1140 }
1141
1142 commit.Push( _( "Create Lines" ) );
1143
1144 return 0;
1145}
1146
1147
1149{
1150 auto& selection = m_selectionTool->RequestSelection(
1151 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1152 {
1153 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1154 {
1155 BOARD_ITEM* item = aCollector[i];
1156
1157 if( !( item->Type() == PCB_SHAPE_T ||
1158 item->Type() == PCB_TRACE_T ) )
1159 {
1160 aCollector.Remove( item );
1161 }
1162 }
1163 } );
1164
1165 EDA_ITEM* source = selection.Front();
1166 VECTOR2I start, end, mid;
1167
1168 // Offset the midpoint along the normal a little bit so that it's more obviously an arc
1169 const double offsetRatio = 0.1;
1170
1171 if( std::optional<SEG> seg = getStartEndPoints( source ) )
1172 {
1173 start = seg->A;
1174 end = seg->B;
1175
1176 VECTOR2I normal = ( seg->B - seg->A ).Perpendicular().Resize( offsetRatio * seg->Length() );
1177 mid = seg->Center() + normal;
1178 }
1179 else
1180 {
1181 return -1;
1182 }
1183
1184 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1185 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1186
1187 BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( source );
1188
1189 // Don't continue processing if we don't actually have a board item
1190 if( !boardItem )
1191 return 0;
1192
1193 PCB_LAYER_ID layer = boardItem->GetLayer();
1194
1195 BOARD_COMMIT commit( m_frame );
1196
1197 if( source->Type() == PCB_SHAPE_T )
1198 {
1199 PCB_SHAPE* line = static_cast<PCB_SHAPE*>( source );
1200 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1201
1202 VECTOR2I center = CalcArcCenter( start, mid, end );
1203
1204 arc->SetFilled( false );
1205 arc->SetLayer( layer );
1206 arc->SetStroke( line->GetStroke() );
1207
1208 arc->SetCenter( VECTOR2I( center ) );
1209 arc->SetStart( VECTOR2I( start ) );
1210 arc->SetEnd( VECTOR2I( end ) );
1211
1212 commit.Add( arc );
1213 }
1214 else
1215 {
1216 wxASSERT( source->Type() == PCB_TRACE_T );
1217 PCB_TRACK* line = static_cast<PCB_TRACK*>( source );
1218 PCB_ARC* arc = new PCB_ARC( parent );
1219
1220 arc->SetLayer( layer );
1221 arc->SetWidth( line->GetWidth() );
1222 arc->SetStart( VECTOR2I( start ) );
1223 arc->SetMid( VECTOR2I( mid ) );
1224 arc->SetEnd( VECTOR2I( end ) );
1225
1226 commit.Add( arc );
1227 }
1228
1229 commit.Push( _( "Create Arc" ) );
1230
1231 return 0;
1232}
1233
1234
1235std::optional<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem )
1236{
1237 switch( aItem->Type() )
1238 {
1239 case PCB_SHAPE_T:
1240 {
1241 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
1242
1243 switch( shape->GetShape() )
1244 {
1245 case SHAPE_T::SEGMENT:
1246 case SHAPE_T::ARC:
1247 case SHAPE_T::POLY:
1248 case SHAPE_T::BEZIER:
1249 if( shape->GetStart() == shape->GetEnd() )
1250 return std::nullopt;
1251
1252 return std::make_optional<SEG>( VECTOR2I( shape->GetStart() ),
1253 VECTOR2I( shape->GetEnd() ) );
1254
1255 default:
1256 return std::nullopt;
1257 }
1258 }
1259
1260 case PCB_TRACE_T:
1261 {
1262 PCB_TRACK* line = static_cast<PCB_TRACK*>( aItem );
1263 return std::make_optional<SEG>( VECTOR2I( line->GetStart() ), VECTOR2I( line->GetEnd() ) );
1264 }
1265
1266 case PCB_ARC_T:
1267 {
1268 PCB_ARC* arc = static_cast<PCB_ARC*>( aItem );
1269 return std::make_optional<SEG>( VECTOR2I( arc->GetStart() ), VECTOR2I( arc->GetEnd() ) );
1270 }
1271
1272 default:
1273 return std::nullopt;
1274 }
1275}
1276
1277
1279{
1286}
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 & set(size_t pos=std::numeric_limits< size_t >::max(), bool value=true)
Definition: base_set.h:61
BASE_SET & reset(size_t pos=std::numeric_limits< size_t >::max())
Definition: base_set.h:76
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:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:240
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:276
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:264
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:447
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:874
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: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:564
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 SetFillMode(FILL_T aFill)
Definition: eda_shape.h:106
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:582
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:207
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition: lset.cpp:753
Definition: pad.h:54
static TOOL_ACTION convertToKeepout
Definition: pcb_actions.h:571
static TOOL_ACTION convertToTracks
Definition: pcb_actions.h:574
static TOOL_ACTION convertToLines
Definition: pcb_actions.h:572
static TOOL_ACTION convertToZone
Definition: pcb_actions.h:570
static TOOL_ACTION convertToPoly
Definition: pcb_actions.h:569
static TOOL_ACTION convertToArc
Definition: pcb_actions.h:573
static TOOL_ACTION createArray
Tool for creating an array of objects.
Definition: pcb_actions.h:481
void SetMid(const VECTOR2I &aMid)
Definition: pcb_track.h:280
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:1672
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:284
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:369
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:317
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:821
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:115
int GetWidth() const
Definition: pcb_track.h:116
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:1678
const VECTOR2I & GetStart() const
Definition: pcb_track.h:122
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:119
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:663
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: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: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:72
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:99
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:913
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, 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
static const std::vector< KICAD_T > padTypes
Definition: edit_tool.cpp:76
static const std::vector< KICAD_T > trackTypes
Definition: edit_tool.cpp:82
@ 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:542
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:531
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:121
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:673