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