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 std::unique_ptr<PCB_TRACK> arc = PCB_ARC::CreateFromThreePoints( parent, graphic->GetStart(),
1032 graphic->GetArcMid(), graphic->GetEnd() );
1033
1034 arc->SetLayer( targetLayer );
1035 arc->SetWidth( graphic->GetWidth() );
1036
1037 if( aNet )
1038 arc->SetNet( aNet );
1039
1040 commit.Add( arc.release() );
1041
1042 return true;
1043 }
1044
1045 return false;
1046 };
1047
1048 auto addGraphicChain =
1049 [&]( const SHAPE_LINE_CHAIN& aChain, std::optional<int> aWidth )
1050 {
1051 for( size_t si = 0; si < aChain.GetSegmentCount(); ++si )
1052 {
1053 const SEG seg = aChain.GetSegment( si );
1054
1055 if( seg.Length() == 0 )
1056 continue;
1057
1058 if( aChain.IsArcSegment( si ) )
1059 continue;
1060
1061 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1062
1063 graphic->SetLayer( targetLayer );
1064 graphic->SetStart( seg.A );
1065 graphic->SetEnd( seg.B );
1066
1067 if( aWidth && *aWidth > 0 )
1068 graphic->SetWidth( *aWidth );
1069
1070 commit.Add( graphic );
1071 }
1072
1073 for( size_t ai = 0; ai < aChain.ArcCount(); ++ai )
1074 {
1075 const SHAPE_ARC& arc = aChain.Arc( ai );
1076
1077 if( arc.GetP0() == arc.GetP1() )
1078 continue;
1079
1080 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::ARC );
1081
1082 graphic->SetLayer( targetLayer );
1083 graphic->SetFilled( false );
1084 graphic->SetArcGeometry( arc.GetP0(), arc.GetArcMid(), arc.GetP1() );
1085
1086 if( aWidth && *aWidth > 0 )
1087 graphic->SetWidth( *aWidth );
1088
1089 commit.Add( graphic );
1090 }
1091 };
1092
1093 auto addTrackChain = [&]( const SHAPE_LINE_CHAIN& aChain, std::optional<int> aWidth, NETINFO_ITEM* aNet )
1094 {
1095 for( size_t si = 0; si < aChain.GetSegmentCount(); ++si )
1096 {
1097 const SEG seg = aChain.GetSegment( si );
1098
1099 if( seg.Length() == 0 )
1100 continue;
1101
1102 if( aChain.IsArcSegment( si ) )
1103 continue;
1104
1105 PCB_TRACK* track = new PCB_TRACK( parent );
1106
1107 track->SetLayer( targetLayer );
1108 track->SetStart( seg.A );
1109 track->SetEnd( seg.B );
1110
1111 if( aWidth && *aWidth > 0 )
1112 track->SetWidth( *aWidth );
1113
1114 if( aNet )
1115 track->SetNet( aNet );
1116
1117 commit.Add( track );
1118 }
1119
1120 for( size_t ai = 0; ai < aChain.ArcCount(); ++ai )
1121 {
1122 const SHAPE_ARC& arc = aChain.Arc( ai );
1123
1124 if( arc.GetP0() == arc.GetP1() )
1125 continue;
1126
1127 std::unique_ptr<PCB_TRACK> trackArc =
1128 PCB_ARC::CreateFromThreePoints( parent, arc.GetP0(), arc.GetArcMid(), arc.GetP1() );
1129
1130 trackArc->SetLayer( targetLayer );
1131
1132 if( aWidth && *aWidth > 0 )
1133 trackArc->SetWidth( *aWidth );
1134
1135 if( aNet )
1136 trackArc->SetNet( aNet );
1137
1138 commit.Add( trackArc.release() );
1139 }
1140 };
1141
1142 auto processChain = [&]( const SHAPE_LINE_CHAIN& aChain, std::optional<int> aWidth, NETINFO_ITEM* aNet )
1143 {
1144 if( aChain.GetSegmentCount() == 0 && aChain.ArcCount() == 0 )
1145 return;
1146
1147 if( aEvent.IsAction( &PCB_ACTIONS::convertToLines ) )
1148 {
1149 addGraphicChain( aChain, aWidth );
1150 }
1151 else if( fpEditor )
1152 {
1153 addGraphicChain( aChain, aWidth );
1154 }
1155 else
1156 {
1157 addTrackChain( aChain, aWidth, aNet );
1158 }
1159 };
1160
1161 auto processPolySet = [&]( const SHAPE_POLY_SET& aPoly, std::optional<int> aWidth, NETINFO_ITEM* aNet )
1162 {
1163 for( int oi = 0; oi < aPoly.OutlineCount(); ++oi )
1164 {
1165 processChain( aPoly.COutline( oi ), aWidth, aNet );
1166
1167 for( int hi = 0; hi < aPoly.HoleCount( oi ); ++hi )
1168 processChain( aPoly.CHole( oi, hi ), aWidth, aNet );
1169 }
1170 };
1171
1172 if( aEvent.IsAction( &PCB_ACTIONS::convertToTracks ) )
1173 {
1174 if( !IsCopperLayer( targetLayer ) )
1175 {
1176 targetLayer = frame->SelectOneLayer( F_Cu, LSET::AllNonCuMask() );
1177
1178 if( targetLayer == UNDEFINED_LAYER ) // User canceled
1179 return true;
1180 }
1181 }
1182 else
1183 {
1184 CONVERT_SETTINGS_DIALOG dlg( m_frame, &m_userSettings, false, false, false );
1185
1186 if( dlg.ShowModal() != wxID_OK )
1187 return true;
1188 }
1189
1190 for( EDA_ITEM* item : selection )
1191 {
1192 if( !item->IsBOARD_ITEM() )
1193 continue;
1194
1195 BOARD_CONNECTED_ITEM* connected = dynamic_cast<BOARD_CONNECTED_ITEM*>( item );
1196 NETINFO_ITEM* sourceNet = connected ? connected->GetNet() : nullptr;
1197
1198 if( handleGraphicSeg( item, sourceNet ) )
1199 continue;
1200
1201 BOARD_ITEM& boardItem = static_cast<BOARD_ITEM&>( *item );
1202 std::optional<int> itemWidth = GetBoardItemWidth( boardItem );
1203
1204 if( boardItem.Type() == PCB_SHAPE_T )
1205 {
1206 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( item );
1207
1208 switch( graphic->GetShape() )
1209 {
1210 case SHAPE_T::RECTANGLE:
1211 {
1212 SHAPE_RECT rect( graphic->GetStart(), graphic->GetEnd() );
1213 ROUNDRECT rrect( rect, graphic->GetCornerRadius(), true );
1214 SHAPE_POLY_SET poly;
1215
1216 rrect.TransformToPolygon( poly, graphic->GetMaxError() );
1217 processPolySet( poly, itemWidth, sourceNet );
1218 break;
1219 }
1220
1221 case SHAPE_T::POLY: processPolySet( graphic->GetPolyShape(), itemWidth, sourceNet ); break;
1222
1223 case SHAPE_T::ELLIPSE:
1225 {
1226 SHAPE_ELLIPSE e =
1227 graphic->GetShape() == SHAPE_T::ELLIPSE_ARC
1228 ? SHAPE_ELLIPSE( graphic->GetEllipseCenter(), graphic->GetEllipseMajorRadius(),
1229 graphic->GetEllipseMinorRadius(), graphic->GetEllipseRotation(),
1230 graphic->GetEllipseStartAngle(), graphic->GetEllipseEndAngle() )
1231 : SHAPE_ELLIPSE( graphic->GetEllipseCenter(), graphic->GetEllipseMajorRadius(),
1232 graphic->GetEllipseMinorRadius(), graphic->GetEllipseRotation() );
1233
1235 SHAPE_POLY_SET poly;
1236 poly.AddOutline( chain );
1237 processPolySet( poly, itemWidth, sourceNet );
1238 break;
1239 }
1240
1241 default:
1242 wxFAIL_MSG( wxT( "Unhandled graphic shape type in PolyToLines" ) );
1243 break;
1244 }
1245 }
1246 else if( boardItem.Type() == PCB_ZONE_T )
1247 {
1248 ZONE* zone = static_cast<ZONE*>( item );
1249 processPolySet( *zone->Outline(), itemWidth, sourceNet );
1250 }
1251 else
1252 {
1253 wxFAIL_MSG( wxT( "Unhandled type in PolyToLines" ) );
1254 }
1255 }
1256
1257 if( m_userSettings.m_DeleteOriginals )
1258 {
1259 PCB_SELECTION selectionCopy = selection;
1260 m_selectionTool->ClearSelection();
1261
1262 for( EDA_ITEM* item : selectionCopy )
1263 commit.Remove( item );
1264 }
1265
1266 commit.Push( _( "Create Lines" ) );
1267
1268 return 0;
1269}
1270
1271
1273{
1274 auto& selection = m_selectionTool->RequestSelection(
1275 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1276 {
1277 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1278 {
1279 BOARD_ITEM* item = aCollector[i];
1280
1281 if( !item->IsType( { PCB_SHAPE_T, PCB_TRACE_T, PCB_ARC_T } ) )
1282 aCollector.Remove( item );
1283 }
1284 } );
1285
1286 if( selection.Empty() || !selection.Front()->IsBOARD_ITEM() )
1287 return -1;
1288
1289 BOARD_ITEM* source = static_cast<BOARD_ITEM*>( selection.Front() );
1290 VECTOR2I start, end, mid;
1291
1292 // Offset the midpoint along the normal a little bit so that it's more obviously an arc
1293 const double offsetRatio = 0.1;
1294
1295 if( std::optional<SEG> seg = getStartEndPoints( source ) )
1296 {
1297 start = seg->A;
1298 end = seg->B;
1299
1300 VECTOR2I normal = ( seg->B - seg->A ).Perpendicular().Resize( offsetRatio * seg->Length() );
1301 mid = seg->Center() + normal;
1302 }
1303 else
1304 {
1305 return -1;
1306 }
1307
1309 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1310 PCB_LAYER_ID layer = source->GetLayer();
1311 BOARD_COMMIT commit( m_frame );
1312
1313 if( source->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( source )->GetShape() == SHAPE_T::SEGMENT )
1314 {
1315 PCB_SHAPE* line = static_cast<PCB_SHAPE*>( source );
1316 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1317
1318 VECTOR2I center = CalcArcCenter( start, mid, end );
1319
1320 arc->SetFilled( false );
1321 arc->SetLayer( layer );
1322 arc->SetStroke( line->GetStroke() );
1323
1324 arc->SetCenter( center );
1325 arc->SetStart( start );
1326 arc->SetEnd( end );
1327
1328 commit.Add( arc );
1329 }
1330 else if( source->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( source )->GetShape() == SHAPE_T::ARC )
1331 {
1332 PCB_SHAPE* source_arc = static_cast<PCB_SHAPE*>( source );
1333 std::unique_ptr<PCB_TRACK> arc = PCB_ARC::CreateFromThreePoints( parent, start, source_arc->GetArcMid(), end );
1334
1335 arc->SetLayer( layer );
1336 arc->SetWidth( source_arc->GetWidth() );
1337
1338 commit.Add( arc.release() );
1339 }
1340 else if( source->Type() == PCB_TRACE_T )
1341 {
1342 PCB_TRACK* line = static_cast<PCB_TRACK*>( source );
1343 std::unique_ptr<PCB_TRACK> arc = PCB_ARC::CreateFromThreePoints( parent, start, mid, end );
1344
1345 arc->SetLayer( layer );
1346 arc->SetWidth( line->GetWidth() );
1347 arc->SetNet( line->GetNet() );
1348
1349 commit.Add( arc.release() );
1350 }
1351 else if( source->Type() == PCB_ARC_T )
1352 {
1353 PCB_ARC* source_arc = static_cast<PCB_ARC*>( source );
1354 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1355
1356 arc->SetFilled( false );
1357 arc->SetLayer( layer );
1358 arc->SetWidth( source_arc->GetWidth() );
1359
1360 arc->SetArcGeometry( source_arc->GetStart(), source_arc->GetMid(), source_arc->GetEnd() );
1361 commit.Add( arc );
1362 }
1363
1364 commit.Push( _( "Create Arc" ) );
1365
1366 return 0;
1367}
1368
1369
1370std::optional<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem )
1371{
1372 switch( aItem->Type() )
1373 {
1374 case PCB_SHAPE_T:
1375 {
1376 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
1377
1378 switch( shape->GetShape() )
1379 {
1380 case SHAPE_T::SEGMENT:
1381 case SHAPE_T::ARC:
1382 case SHAPE_T::POLY:
1383 case SHAPE_T::BEZIER:
1384 if( shape->GetStart() == shape->GetEnd() )
1385 return std::nullopt;
1386
1387 return std::make_optional<SEG>( VECTOR2I( shape->GetStart() ), VECTOR2I( shape->GetEnd() ) );
1388
1389 default:
1390 return std::nullopt;
1391 }
1392 }
1393
1394 case PCB_TRACE_T:
1395 {
1396 PCB_TRACK* line = static_cast<PCB_TRACK*>( aItem );
1397 return std::make_optional<SEG>( VECTOR2I( line->GetStart() ), VECTOR2I( line->GetEnd() ) );
1398 }
1399
1400 case PCB_ARC_T:
1401 {
1402 PCB_ARC* arc = static_cast<PCB_ARC*>( aItem );
1403 return std::make_optional<SEG>( VECTOR2I( arc->GetStart() ), VECTOR2I( arc->GetEnd() ) );
1404 }
1405
1406 default:
1407 return std::nullopt;
1408 }
1409}
1410
1411
1413{
1415 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
1416 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1417 {
1418 // Iterate from the back so we don't have to worry about removals.
1419 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1420 {
1421 BOARD_ITEM* item = aCollector[i];
1422
1423 // We've converted the polygon and rectangle to segments, so drop everything
1424 // that isn't a segment at this point
1425 if( !item->IsType( { PCB_PAD_T, PCB_SHAPE_T } ) )
1426 aCollector.Remove( item );
1427 }
1428
1429 sTool->FilterCollectorForLockedItems( aCollector );
1430 } );
1431
1432 if( m_selectionTool->ReportFilteredLockedItems() )
1433 return 0;
1434
1435 BOARD_COMMIT commit( this );
1436
1437 for( EDA_ITEM* item : selection )
1438 item->ClearFlags( STRUCT_DELETED );
1439
1440 // List of thing to select at the end of the operation
1441 // (doing it as we go will invalidate the iterator)
1442 std::vector<BOARD_ITEM*> items_to_select_on_success;
1443
1444 // Handle modifications to existing items by the routine
1445 // How to deal with this depends on whether we're in the footprint editor or not
1446 // and whether the item was conjured up by decomposing a polygon or rectangle
1447 auto item_modification_handler =
1448 [&]( BOARD_ITEM& aItem )
1449 {
1450 };
1451
1452 bool any_items_created = false;
1453
1454 auto item_creation_handler =
1455 [&]( std::unique_ptr<BOARD_ITEM> aItem )
1456 {
1457 any_items_created = true;
1458 items_to_select_on_success.push_back( aItem.get() );
1459 commit.Add( aItem.release() );
1460 };
1461
1462 auto item_removal_handler =
1463 [&]( BOARD_ITEM& aItem )
1464 {
1465 // If you do an outset on a FP pad, do you really want to delete
1466 // the parent?
1467 if( !aItem.GetParentFootprint() )
1468 {
1469 commit.Remove( &aItem );
1470 }
1471 };
1472
1473 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1474 ITEM_MODIFICATION_ROUTINE::CALLABLE_BASED_HANDLER change_handler( item_creation_handler,
1475 item_modification_handler,
1476 item_removal_handler );
1477
1478 // Persistent settings between dialog invocations
1479 // Init with some sensible defaults
1480 static OUTSET_ROUTINE::PARAMETERS outset_params_fp_edit
1481 {
1482 pcbIUScale.mmToIU( 0.25 ), // A common outset value
1483 false,
1484 false,
1485 true,
1486 F_CrtYd,
1487 frame.GetDesignSettings().GetLineThickness( F_CrtYd ),
1488 pcbIUScale.mmToIU( 0.01 ),
1489 false,
1490 };
1491
1492 static OUTSET_ROUTINE::PARAMETERS outset_params_pcb_edit
1493 {
1494 pcbIUScale.mmToIU( 1 ),
1495 true,
1496 true,
1497 true,
1498 Edge_Cuts, // Outsets often for slots?
1499 frame.GetDesignSettings().GetLineThickness( Edge_Cuts ),
1500 std::nullopt,
1501 false,
1502 };
1503
1504 OUTSET_ROUTINE::PARAMETERS& outset_params = IsFootprintEditor() ? outset_params_fp_edit
1505 : outset_params_pcb_edit;
1506
1507 {
1508 DIALOG_OUTSET_ITEMS dlg( frame, outset_params );
1509
1510 if( dlg.ShowModal() == wxID_CANCEL )
1511 return 0;
1512 }
1513
1514 OUTSET_ROUTINE outset_routine( frame.GetModel(), change_handler, outset_params );
1515
1516 for( EDA_ITEM* item : selection )
1517 {
1518 if( !item->IsBOARD_ITEM() )
1519 continue;
1520
1521 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
1522 outset_routine.ProcessItem( *board_item );
1523 }
1524
1525 // Deselect all the original items
1526 m_selectionTool->ClearSelection();
1527
1528 // Select added and modified items
1529 for( BOARD_ITEM* item : items_to_select_on_success )
1530 m_selectionTool->AddItemToSel( item, true );
1531
1532 if( any_items_created )
1533 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1534
1535 // Notify other tools of the changes
1537
1538 commit.Push( outset_routine.GetCommitDescription() );
1539
1540 if( const std::optional<wxString> msg = outset_routine.GetStatusMessage() )
1541 frame.ShowInfoBarMsg( *msg );
1542
1543 return 0;
1544}
1545
1546
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
BASE_SET & reset(size_t pos)
Definition base_set.h:153
BASE_SET & set(size_t pos)
Definition base_set.h:126
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 NETINFO_ITEM 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:85
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:560
virtual bool HasLineStroke() const
Check if this item has line stoke properties.
Definition board_item.h:298
int GetMaxError() const
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition board.h:713
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:400
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:98
virtual VECTOR2I GetPosition() const
Definition eda_item.h:348
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:214
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:167
int GetEllipseMinorRadius() const
Definition eda_shape.h:403
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:385
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:431
FILL_T GetFillMode() const
Definition eda_shape.h:150
int GetEllipseMajorRadius() const
Definition eda_shape.h:394
SHAPE_POLY_SET & GetPolyShape()
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:412
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:177
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:144
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:333
bool IsClosed() const
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:283
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:422
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition eda_shape.h:505
int GetCornerRadius() const
void SetFillMode(FILL_T aFill)
VECTOR2I GetArcMid() const
static const TOOL_EVENT SelectedEvent
Definition actions.h:346
static const TOOL_EVENT SelectedItemsModified
Selected item had a property changed (except movement)
Definition actions.h:353
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:50
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.
static std::unique_ptr< PCB_TRACK > CreateFromThreePoints(BOARD_ITEM *aParent, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Create a PCB_ARC through the three points, or a PCB_TRACK segment when they are straight.
Definition pcb_track.cpp:95
std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
const VECTOR2I & GetMid() const
Definition pcb_track.h:300
void GetBoundingHull(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Add two rectangular polygons separately bounding the barcode's symbol and the barcode's text.
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.
void SetCenter(const VECTOR2I &aCenter) override
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:80
void SetWidth(int aWidth) override
int GetWidth() const override
void SetShape(SHAPE_T aShape) override
Definition pcb_shape.h:212
void SetEnd(const VECTOR2I &aEnd) override
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
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd) override
Set the three controlling points for an arc.
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:94
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:97
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:98
const VECTOR2I & GetEnd() const
Definition pcb_track.h:95
virtual void SetWidth(int aWidth)
Definition pcb_track.h:91
virtual int GetWidth() const
Definition pcb_track.h:92
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:340
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).
Handle zones parameters.
void SetIsRuleArea(bool aEnable)
void ExportSetting(ZONE &aTarget, bool aFullExport=true) const
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:1614
SHAPE_POLY_SET * Outline()
Definition zone.h:421
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)
Invoke a modal dialog window for copper zone editing.
int InvokeNonCopperZonesEditor(PCB_BASE_FRAME *aParent, ZONE_SETTINGS *aSettings, CONVERT_SETTINGS *aConvertSettings)
Invoke a modal dialog window for non-copper zone editing.
int InvokeRuleAreaEditor(PCB_BASE_FRAME *aCaller, ZONE_SETTINGS *aZoneSettings, BOARD *aBoard, CONVERT_SETTINGS *aConvertSettings)
Invoke a modal dialog window for copper zone editing.
#define _(s)
FILL_T
Definition eda_fill.h:29
#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:62
@ SEGMENT
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ ELLIPSE_ARC
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:38
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:39
std::string source
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
bool IsNonCopperLayer(int aLayerId)
Test whether a layer is a non copper layer.
Definition layer_ids.h:774
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:741
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
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:540
@ PCB_SHAPE_LOCATE_ELLIPSE_ARC_T
Definition typeinfo.h:236
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:93
@ PCB_SHAPE_LOCATE_CIRCLE_T
Definition typeinfo.h:131
@ PCB_SHAPE_LOCATE_SEGMENT_T
Definition typeinfo.h:129
@ PCB_SHAPE_LOCATE_RECT_T
Definition typeinfo.h:130
@ PCB_SHAPE_LOCATE_ELLIPSE_T
Definition typeinfo.h:235
@ PCB_SHAPE_LOCATE_BEZIER_T
Definition typeinfo.h:134
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_SHAPE_LOCATE_POLY_T
Definition typeinfo.h:133
@ PCB_SHAPE_LOCATE_ARC_T
Definition typeinfo.h:132
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708