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