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 std::deque<EDA_ITEM*> toCheck;
606
607 auto closeEnough =
608 []( const VECTOR2I& aLeft, const VECTOR2I& aRight, int aLimit )
609 {
610 return ( aLeft - aRight ).SquaredEuclideanNorm() <= SEG::Square( aLimit );
611 };
612
613 auto findInsertionPoint =
614 [&]( const VECTOR2I& aPoint ) -> VECTOR2I
615 {
616 if( connections.count( aPoint ) )
617 return aPoint;
618
619 for( const auto& candidatePair : connections )
620 {
621 if( closeEnough( aPoint, candidatePair.first, chainingEpsilon ) )
622 return candidatePair.first;
623 }
624
625 return aPoint;
626 };
627
628 for( EDA_ITEM* item : aItems )
629 {
630 if( std::optional<SEG> seg = getStartEndPoints( item ) )
631 {
632 toCheck.push_back( item );
633 connections[findInsertionPoint( seg->A )].emplace_back( std::make_pair( 0, item ) );
634 connections[findInsertionPoint( seg->B )].emplace_back( std::make_pair( 1, item ) );
635 }
636 }
637
638 while( !toCheck.empty() )
639 {
640 std::vector<BOARD_ITEM*> insertedItems;
641
642 EDA_ITEM* candidate = toCheck.front();
643 toCheck.pop_front();
644
645 if( candidate->GetFlags() & SKIP_STRUCT )
646 continue;
647
648 SHAPE_LINE_CHAIN outline;
649
650 auto insert =
651 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
652 {
653 if( aItem->IsType( { PCB_ARC_T, PCB_SHAPE_LOCATE_ARC_T } ) )
654 {
655 SHAPE_ARC arc;
656
657 if( aItem->Type() == PCB_ARC_T )
658 {
659 PCB_ARC* pcb_arc = static_cast<PCB_ARC*>( aItem );
660 arc = *static_cast<SHAPE_ARC*>( pcb_arc->GetEffectiveShape().get() );
661 }
662 else
663 {
664 PCB_SHAPE* pcb_shape = static_cast<PCB_SHAPE*>( aItem );
665 arc = SHAPE_ARC( pcb_shape->GetStart(), pcb_shape->GetArcMid(),
666 pcb_shape->GetEnd(), pcb_shape->GetWidth() );
667 }
668
669 if( aDirection )
670 outline.Append( aAnchor == arc.GetP0() ? arc : arc.Reversed() );
671 else
672 outline.Insert( 0, aAnchor == arc.GetP0() ? arc : arc.Reversed() );
673
674 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
675 }
676 else if( aItem->IsType( { PCB_SHAPE_LOCATE_BEZIER_T } ) )
677 {
678 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
679
680 if( aAnchor == graphic->GetStart() )
681 {
682 for( const VECTOR2I& pt : graphic->GetBezierPoints() )
683 {
684 if( aDirection )
685 outline.Append( pt );
686 else
687 outline.Insert( 0, pt );
688 }
689
690 }
691 else
692 {
693 for( const VECTOR2I& pt : std::ranges::reverse_view( graphic->GetBezierPoints() ) )
694 {
695 if( aDirection )
696 outline.Append( pt );
697 else
698 outline.Insert( 0, pt );
699 }
700 }
701
702 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
703 }
704 else if( std::optional<SEG> nextSeg = getStartEndPoints( aItem ) )
705 {
706 VECTOR2I& point = ( aAnchor == nextSeg->A ) ? nextSeg->B : nextSeg->A;
707
708 if( aDirection )
709 outline.Append( point );
710 else
711 outline.Insert( 0, point );
712
713 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
714 }
715 };
716
717 // aDirection == true for walking "right" and appending to the end of points
718 // false for walking "left" and prepending to the beginning
719 std::function<void( EDA_ITEM*, const VECTOR2I&, bool )> process =
720 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
721 {
722 if( aItem->GetFlags() & SKIP_STRUCT )
723 return;
724
725 aItem->SetFlags( SKIP_STRUCT );
726
727 insert( aItem, aAnchor, aDirection );
728
729 std::optional<SEG> anchors = getStartEndPoints( aItem );
730 wxASSERT( anchors );
731
732 VECTOR2I nextAnchor = ( aAnchor == anchors->A ) ? anchors->B : anchors->A;
733
734 for( std::pair<int, EDA_ITEM*> pair : connections[nextAnchor] )
735 {
736 if( pair.second == aItem )
737 continue;
738
739 process( pair.second, nextAnchor, aDirection );
740 }
741 };
742
743 std::optional<SEG> anchors = getStartEndPoints( candidate );
744 wxASSERT( anchors );
745
746 // Start with the first object and walk "right"
747 // Note if the first object is an arc, we don't need to insert its first point here, the
748 // whole arc will be inserted at anchor B inside process()
749 if( !candidate->IsType( { PCB_ARC_T, PCB_SHAPE_LOCATE_ARC_T } ) )
750 insert( candidate, anchors->A, true );
751
752 process( candidate, anchors->B, true );
753
754 // check for any candidates on the "left"
755 EDA_ITEM* left = nullptr;
756
757 for( std::pair<int, EDA_ITEM*> possibleLeft : connections[anchors->A] )
758 {
759 if( possibleLeft.second != candidate )
760 {
761 left = possibleLeft.second;
762 break;
763 }
764 }
765
766 if( left )
767 process( left, anchors->A, false );
768
769 if( outline.PointCount() < 3
770 || !closeEnough( outline.GetPoint( 0 ), outline.GetPoint( -1 ), chainingEpsilon ) )
771 {
772 for( EDA_ITEM* item : insertedItems )
773 item->ClearFlags( SKIP_STRUCT );
774
775 continue;
776 }
777
778 outline.SetClosed( true );
779
780 poly.AddOutline( outline );
781
782 if( aStrategy == BOUNDING_HULL )
783 {
784 for( BOARD_ITEM* item : insertedItems )
785 item->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, item->GetMaxError(), ERROR_INSIDE );
786 }
787
788 insertedItems.clear();
789 }
790
791 return poly;
792}
793
794
795SHAPE_POLY_SET CONVERT_TOOL::makePolysFromOpenGraphics( const std::deque<EDA_ITEM*>& aItems, int aGap )
796{
797 SHAPE_POLY_SET poly;
798
799 for( EDA_ITEM* item : aItems )
800 {
801 if( item->GetFlags() & SKIP_STRUCT )
802 continue;
803
804 switch( item->Type() )
805 {
806 case PCB_SHAPE_T:
807 {
808 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
809
810 if( shape->IsClosed() )
811 continue;
812
813 shape->TransformShapeToPolygon( poly, UNDEFINED_LAYER, aGap, shape->GetMaxError(), ERROR_INSIDE );
814 shape->SetFlags( SKIP_STRUCT );
815
816 break;
817 }
818
819 case PCB_TRACE_T:
820 case PCB_ARC_T:
821 case PCB_VIA_T:
822 {
823 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
824
825 track->TransformShapeToPolygon( poly, UNDEFINED_LAYER, aGap, track->GetMaxError(), ERROR_INSIDE );
826 track->SetFlags( SKIP_STRUCT );
827
828 break;
829 }
830
831 default:
832 continue;
833 }
834 }
835
836 return poly;
837}
838
839
841 CONVERT_STRATEGY aStrategy )
842{
843 SHAPE_POLY_SET poly;
844
845 for( EDA_ITEM* item : aItems )
846 {
847 if( item->GetFlags() & SKIP_STRUCT )
848 continue;
849
850 switch( item->Type() )
851 {
852 case PCB_SHAPE_T:
853 {
854 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
855 FILL_T wasFilled = shape->GetFillMode();
856
857 if( !shape->IsClosed() )
858 continue;
859
860 if( shape->GetShape() == SHAPE_T::CIRCLE && aStrategy != BOUNDING_HULL )
861 {
862 VECTOR2I c = shape->GetCenter();
863 int R = shape->GetRadius();
864 SHAPE_ARC arc( c - VECTOR2I( R, 0 ), c + VECTOR2I( R, 0 ), c - VECTOR2I( R, 0 ), 0 );
865
866 poly.NewOutline();
867 poly.Append( arc );
868 }
869 else
870 {
871 if( aStrategy != BOUNDING_HULL )
872 shape->SetFilled( true );
873
875 aStrategy != BOUNDING_HULL );
876
877 if( aStrategy != BOUNDING_HULL )
878 shape->SetFillMode( wasFilled );
879 }
880
881 shape->SetFlags( SKIP_STRUCT );
882 break;
883 }
884
885 case PCB_ZONE_T:
886 poly.Append( *static_cast<ZONE*>( item )->Outline() );
887 item->SetFlags( SKIP_STRUCT );
888 break;
889
890 case PCB_FIELD_T:
891 case PCB_TEXT_T:
892 {
893 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
894 text->TransformTextToPolySet( poly, 0, text->GetMaxError(), ERROR_INSIDE );
895 text->SetFlags( SKIP_STRUCT );
896 break;
897 }
898
899 case PCB_BARCODE_T:
900 {
901 PCB_BARCODE* barcode = static_cast<PCB_BARCODE*>( item );
902
903 if( aStrategy == BOUNDING_HULL )
904 barcode->GetBoundingHull( poly, UNDEFINED_LAYER, 0, barcode->GetMaxError(), ERROR_INSIDE );
905 else
906 barcode->TransformShapeToPolySet( poly, UNDEFINED_LAYER, 0, barcode->GetMaxError(), ERROR_INSIDE );
907
908 barcode->SetFlags( SKIP_STRUCT );
909 break;
910 }
911
912 case PCB_PAD_T:
913 {
914 PAD* pad = static_cast<PAD*>( item );
915
916 pad->Padstack().ForEachUniqueLayer(
917 [&]( PCB_LAYER_ID aLayer )
918 {
919 SHAPE_POLY_SET layerPoly;
920 pad->TransformShapeToPolygon( layerPoly, aLayer, 0, pad->GetMaxError(), ERROR_INSIDE );
921 poly.BooleanAdd( layerPoly );
922 } );
923
924 pad->SetFlags( SKIP_STRUCT );
925 break;
926 }
927
928
929 default:
930 continue;
931 }
932 }
933
934 return poly;
935}
936
937
939{
940 auto& selection = m_selectionTool->RequestSelection(
941 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
942 {
943 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
944 {
945 BOARD_ITEM* item = aCollector[i];
946
947 switch( item->Type() )
948 {
949 case PCB_SHAPE_T:
950 switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
951 {
952 case SHAPE_T::SEGMENT:
953 case SHAPE_T::ARC:
954 case SHAPE_T::POLY:
956 break;
957
958 default:
959 aCollector.Remove( item );
960 }
961
962 break;
963
964 case PCB_ZONE_T:
965 break;
966
967 default:
968 aCollector.Remove( item );
969 }
970 }
971 } );
972
973 if( selection.Empty() )
974 return 0;
975
976 BOARD_COMMIT commit( m_frame );
978 FOOTPRINT_EDIT_FRAME* fpEditor = dynamic_cast<FOOTPRINT_EDIT_FRAME*>( m_frame );
979 FOOTPRINT* footprint = nullptr;
980 PCB_LAYER_ID targetLayer = m_frame->GetActiveLayer();
981 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
982
983 if( fpEditor )
984 footprint = fpEditor->GetBoard()->GetFirstFootprint();
985
986 auto handleGraphicSeg = [&]( EDA_ITEM* aItem, NETINFO_ITEM* aNet )
987 {
988 if( aItem->Type() != PCB_SHAPE_T )
989 return false;
990
991 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
992
993 if( graphic->GetShape() == SHAPE_T::SEGMENT )
994 {
995 PCB_TRACK* track = new PCB_TRACK( parent );
996
997 track->SetLayer( targetLayer );
998 track->SetStart( graphic->GetStart() );
999 track->SetEnd( graphic->GetEnd() );
1000 track->SetWidth( graphic->GetWidth() );
1001
1002 if( aNet )
1003 track->SetNet( aNet );
1004
1005 commit.Add( track );
1006
1007 return true;
1008 }
1009 else if( graphic->GetShape() == SHAPE_T::ARC )
1010 {
1011 PCB_ARC* arc = new PCB_ARC( parent );
1012
1013 arc->SetLayer( targetLayer );
1014 arc->SetStart( graphic->GetStart() );
1015 arc->SetEnd( graphic->GetEnd() );
1016 arc->SetMid( graphic->GetArcMid() );
1017 arc->SetWidth( graphic->GetWidth() );
1018
1019 if( aNet )
1020 arc->SetNet( aNet );
1021
1022 commit.Add( arc );
1023
1024 return true;
1025 }
1026
1027 return false;
1028 };
1029
1030 auto addGraphicChain =
1031 [&]( const SHAPE_LINE_CHAIN& aChain, std::optional<int> aWidth )
1032 {
1033 for( size_t si = 0; si < aChain.GetSegmentCount(); ++si )
1034 {
1035 const SEG seg = aChain.GetSegment( si );
1036
1037 if( seg.Length() == 0 )
1038 continue;
1039
1040 if( aChain.IsArcSegment( si ) )
1041 continue;
1042
1043 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1044
1045 graphic->SetLayer( targetLayer );
1046 graphic->SetStart( seg.A );
1047 graphic->SetEnd( seg.B );
1048
1049 if( aWidth && *aWidth > 0 )
1050 graphic->SetWidth( *aWidth );
1051
1052 commit.Add( graphic );
1053 }
1054
1055 for( size_t ai = 0; ai < aChain.ArcCount(); ++ai )
1056 {
1057 const SHAPE_ARC& arc = aChain.Arc( ai );
1058
1059 if( arc.GetP0() == arc.GetP1() )
1060 continue;
1061
1062 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::ARC );
1063
1064 graphic->SetLayer( targetLayer );
1065 graphic->SetFilled( false );
1066 graphic->SetArcGeometry( arc.GetP0(), arc.GetArcMid(), arc.GetP1() );
1067
1068 if( aWidth && *aWidth > 0 )
1069 graphic->SetWidth( *aWidth );
1070
1071 commit.Add( graphic );
1072 }
1073 };
1074
1075 auto addTrackChain = [&]( const SHAPE_LINE_CHAIN& aChain, std::optional<int> aWidth, NETINFO_ITEM* aNet )
1076 {
1077 for( size_t si = 0; si < aChain.GetSegmentCount(); ++si )
1078 {
1079 const SEG seg = aChain.GetSegment( si );
1080
1081 if( seg.Length() == 0 )
1082 continue;
1083
1084 if( aChain.IsArcSegment( si ) )
1085 continue;
1086
1087 PCB_TRACK* track = new PCB_TRACK( parent );
1088
1089 track->SetLayer( targetLayer );
1090 track->SetStart( seg.A );
1091 track->SetEnd( seg.B );
1092
1093 if( aWidth && *aWidth > 0 )
1094 track->SetWidth( *aWidth );
1095
1096 if( aNet )
1097 track->SetNet( aNet );
1098
1099 commit.Add( track );
1100 }
1101
1102 for( size_t ai = 0; ai < aChain.ArcCount(); ++ai )
1103 {
1104 const SHAPE_ARC& arc = aChain.Arc( ai );
1105
1106 if( arc.GetP0() == arc.GetP1() )
1107 continue;
1108
1109 PCB_ARC* trackArc = new PCB_ARC( parent );
1110
1111 trackArc->SetLayer( targetLayer );
1112 trackArc->SetStart( arc.GetP0() );
1113 trackArc->SetEnd( arc.GetP1() );
1114 trackArc->SetMid( arc.GetArcMid() );
1115
1116 if( aWidth && *aWidth > 0 )
1117 trackArc->SetWidth( *aWidth );
1118
1119 if( aNet )
1120 trackArc->SetNet( aNet );
1121
1122 commit.Add( trackArc );
1123 }
1124 };
1125
1126 auto processChain = [&]( const SHAPE_LINE_CHAIN& aChain, std::optional<int> aWidth, NETINFO_ITEM* aNet )
1127 {
1128 if( aChain.GetSegmentCount() == 0 && aChain.ArcCount() == 0 )
1129 return;
1130
1131 if( aEvent.IsAction( &PCB_ACTIONS::convertToLines ) )
1132 {
1133 addGraphicChain( aChain, aWidth );
1134 }
1135 else if( fpEditor )
1136 {
1137 addGraphicChain( aChain, aWidth );
1138 }
1139 else
1140 {
1141 addTrackChain( aChain, aWidth, aNet );
1142 }
1143 };
1144
1145 auto processPolySet = [&]( const SHAPE_POLY_SET& aPoly, std::optional<int> aWidth, NETINFO_ITEM* aNet )
1146 {
1147 for( int oi = 0; oi < aPoly.OutlineCount(); ++oi )
1148 {
1149 processChain( aPoly.COutline( oi ), aWidth, aNet );
1150
1151 for( int hi = 0; hi < aPoly.HoleCount( oi ); ++hi )
1152 processChain( aPoly.CHole( oi, hi ), aWidth, aNet );
1153 }
1154 };
1155
1156 if( aEvent.IsAction( &PCB_ACTIONS::convertToTracks ) )
1157 {
1158 if( !IsCopperLayer( targetLayer ) )
1159 {
1160 targetLayer = frame->SelectOneLayer( F_Cu, LSET::AllNonCuMask() );
1161
1162 if( targetLayer == UNDEFINED_LAYER ) // User canceled
1163 return true;
1164 }
1165 }
1166 else
1167 {
1168 CONVERT_SETTINGS_DIALOG dlg( m_frame, &m_userSettings, false, false, false );
1169
1170 if( dlg.ShowModal() != wxID_OK )
1171 return true;
1172 }
1173
1174 for( EDA_ITEM* item : selection )
1175 {
1176 if( !item->IsBOARD_ITEM() )
1177 continue;
1178
1179 BOARD_CONNECTED_ITEM* connected = dynamic_cast<BOARD_CONNECTED_ITEM*>( item );
1180 NETINFO_ITEM* sourceNet = connected ? connected->GetNet() : nullptr;
1181
1182 if( handleGraphicSeg( item, sourceNet ) )
1183 continue;
1184
1185 BOARD_ITEM& boardItem = static_cast<BOARD_ITEM&>( *item );
1186 std::optional<int> itemWidth = GetBoardItemWidth( boardItem );
1187
1188 if( boardItem.Type() == PCB_SHAPE_T )
1189 {
1190 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( item );
1191
1192 switch( graphic->GetShape() )
1193 {
1194 case SHAPE_T::RECTANGLE:
1195 {
1196 SHAPE_RECT rect( graphic->GetStart(), graphic->GetEnd() );
1197 ROUNDRECT rrect( rect, graphic->GetCornerRadius(), true );
1198 SHAPE_POLY_SET poly;
1199
1200 rrect.TransformToPolygon( poly, graphic->GetMaxError() );
1201 processPolySet( poly, itemWidth, sourceNet );
1202 break;
1203 }
1204
1205 case SHAPE_T::POLY: processPolySet( graphic->GetPolyShape(), itemWidth, sourceNet ); break;
1206
1207 case SHAPE_T::ELLIPSE:
1209 {
1210 SHAPE_ELLIPSE e =
1211 graphic->GetShape() == SHAPE_T::ELLIPSE_ARC
1212 ? SHAPE_ELLIPSE( graphic->GetEllipseCenter(), graphic->GetEllipseMajorRadius(),
1213 graphic->GetEllipseMinorRadius(), graphic->GetEllipseRotation(),
1214 graphic->GetEllipseStartAngle(), graphic->GetEllipseEndAngle() )
1215 : SHAPE_ELLIPSE( graphic->GetEllipseCenter(), graphic->GetEllipseMajorRadius(),
1216 graphic->GetEllipseMinorRadius(), graphic->GetEllipseRotation() );
1217
1219 SHAPE_POLY_SET poly;
1220 poly.AddOutline( chain );
1221 processPolySet( poly, itemWidth, sourceNet );
1222 break;
1223 }
1224
1225 default:
1226 wxFAIL_MSG( wxT( "Unhandled graphic shape type in PolyToLines" ) );
1227 break;
1228 }
1229 }
1230 else if( boardItem.Type() == PCB_ZONE_T )
1231 {
1232 ZONE* zone = static_cast<ZONE*>( item );
1233 processPolySet( *zone->Outline(), itemWidth, sourceNet );
1234 }
1235 else
1236 {
1237 wxFAIL_MSG( wxT( "Unhandled type in PolyToLines" ) );
1238 }
1239 }
1240
1241 if( m_userSettings.m_DeleteOriginals )
1242 {
1243 PCB_SELECTION selectionCopy = selection;
1244 m_selectionTool->ClearSelection();
1245
1246 for( EDA_ITEM* item : selectionCopy )
1247 commit.Remove( item );
1248 }
1249
1250 commit.Push( _( "Create Lines" ) );
1251
1252 return 0;
1253}
1254
1255
1257{
1258 auto& selection = m_selectionTool->RequestSelection(
1259 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1260 {
1261 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1262 {
1263 BOARD_ITEM* item = aCollector[i];
1264
1265 if( !item->IsType( { PCB_SHAPE_T, PCB_TRACE_T, PCB_ARC_T } ) )
1266 aCollector.Remove( item );
1267 }
1268 } );
1269
1270 if( selection.Empty() || !selection.Front()->IsBOARD_ITEM() )
1271 return -1;
1272
1273 BOARD_ITEM* source = static_cast<BOARD_ITEM*>( selection.Front() );
1274 VECTOR2I start, end, mid;
1275
1276 // Offset the midpoint along the normal a little bit so that it's more obviously an arc
1277 const double offsetRatio = 0.1;
1278
1279 if( std::optional<SEG> seg = getStartEndPoints( source ) )
1280 {
1281 start = seg->A;
1282 end = seg->B;
1283
1284 VECTOR2I normal = ( seg->B - seg->A ).Perpendicular().Resize( offsetRatio * seg->Length() );
1285 mid = seg->Center() + normal;
1286 }
1287 else
1288 {
1289 return -1;
1290 }
1291
1293 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1294 PCB_LAYER_ID layer = source->GetLayer();
1295 BOARD_COMMIT commit( m_frame );
1296
1297 if( source->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( source )->GetShape() == SHAPE_T::SEGMENT )
1298 {
1299 PCB_SHAPE* line = static_cast<PCB_SHAPE*>( source );
1300 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1301
1302 VECTOR2I center = CalcArcCenter( start, mid, end );
1303
1304 arc->SetFilled( false );
1305 arc->SetLayer( layer );
1306 arc->SetStroke( line->GetStroke() );
1307
1308 arc->SetCenter( center );
1309 arc->SetStart( start );
1310 arc->SetEnd( end );
1311
1312 commit.Add( arc );
1313 }
1314 else if( source->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( source )->GetShape() == SHAPE_T::ARC )
1315 {
1316 PCB_SHAPE* source_arc = static_cast<PCB_SHAPE*>( source );
1317 PCB_ARC* arc = new PCB_ARC( parent );
1318
1319 arc->SetLayer( layer );
1320 arc->SetWidth( source_arc->GetWidth() );
1321 arc->SetStart( start );
1322 arc->SetMid( source_arc->GetArcMid() );
1323 arc->SetEnd( end );
1324
1325 commit.Add( arc );
1326 }
1327 else if( source->Type() == PCB_TRACE_T )
1328 {
1329 PCB_TRACK* line = static_cast<PCB_TRACK*>( source );
1330 PCB_ARC* arc = new PCB_ARC( parent );
1331
1332 arc->SetLayer( layer );
1333 arc->SetWidth( line->GetWidth() );
1334 arc->SetNet( line->GetNet() );
1335 arc->SetStart( start );
1336 arc->SetMid( mid );
1337 arc->SetEnd( end );
1338
1339 commit.Add( arc );
1340 }
1341 else if( source->Type() == PCB_ARC_T )
1342 {
1343 PCB_ARC* source_arc = static_cast<PCB_ARC*>( source );
1344 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1345
1346 arc->SetFilled( false );
1347 arc->SetLayer( layer );
1348 arc->SetWidth( source_arc->GetWidth() );
1349
1350 arc->SetArcGeometry( source_arc->GetStart(), source_arc->GetMid(), source_arc->GetEnd() );
1351 commit.Add( arc );
1352 }
1353
1354 commit.Push( _( "Create Arc" ) );
1355
1356 return 0;
1357}
1358
1359
1360std::optional<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem )
1361{
1362 switch( aItem->Type() )
1363 {
1364 case PCB_SHAPE_T:
1365 {
1366 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
1367
1368 switch( shape->GetShape() )
1369 {
1370 case SHAPE_T::SEGMENT:
1371 case SHAPE_T::ARC:
1372 case SHAPE_T::POLY:
1373 case SHAPE_T::BEZIER:
1374 if( shape->GetStart() == shape->GetEnd() )
1375 return std::nullopt;
1376
1377 return std::make_optional<SEG>( VECTOR2I( shape->GetStart() ), VECTOR2I( shape->GetEnd() ) );
1378
1379 default:
1380 return std::nullopt;
1381 }
1382 }
1383
1384 case PCB_TRACE_T:
1385 {
1386 PCB_TRACK* line = static_cast<PCB_TRACK*>( aItem );
1387 return std::make_optional<SEG>( VECTOR2I( line->GetStart() ), VECTOR2I( line->GetEnd() ) );
1388 }
1389
1390 case PCB_ARC_T:
1391 {
1392 PCB_ARC* arc = static_cast<PCB_ARC*>( aItem );
1393 return std::make_optional<SEG>( VECTOR2I( arc->GetStart() ), VECTOR2I( arc->GetEnd() ) );
1394 }
1395
1396 default:
1397 return std::nullopt;
1398 }
1399}
1400
1401
1403{
1405 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
1406 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1407 {
1408 // Iterate from the back so we don't have to worry about removals.
1409 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1410 {
1411 BOARD_ITEM* item = aCollector[i];
1412
1413 // We've converted the polygon and rectangle to segments, so drop everything
1414 // that isn't a segment at this point
1415 if( !item->IsType( { PCB_PAD_T, PCB_SHAPE_T } ) )
1416 aCollector.Remove( item );
1417 }
1418
1419 sTool->FilterCollectorForLockedItems( aCollector );
1420 } );
1421
1422 BOARD_COMMIT commit( this );
1423
1424 for( EDA_ITEM* item : selection )
1425 item->ClearFlags( STRUCT_DELETED );
1426
1427 // List of thing to select at the end of the operation
1428 // (doing it as we go will invalidate the iterator)
1429 std::vector<BOARD_ITEM*> items_to_select_on_success;
1430
1431 // Handle modifications to existing items by the routine
1432 // How to deal with this depends on whether we're in the footprint editor or not
1433 // and whether the item was conjured up by decomposing a polygon or rectangle
1434 auto item_modification_handler =
1435 [&]( BOARD_ITEM& aItem )
1436 {
1437 };
1438
1439 bool any_items_created = false;
1440
1441 auto item_creation_handler =
1442 [&]( std::unique_ptr<BOARD_ITEM> aItem )
1443 {
1444 any_items_created = true;
1445 items_to_select_on_success.push_back( aItem.get() );
1446 commit.Add( aItem.release() );
1447 };
1448
1449 auto item_removal_handler =
1450 [&]( BOARD_ITEM& aItem )
1451 {
1452 // If you do an outset on a FP pad, do you really want to delete
1453 // the parent?
1454 if( !aItem.GetParentFootprint() )
1455 {
1456 commit.Remove( &aItem );
1457 }
1458 };
1459
1460 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1461 ITEM_MODIFICATION_ROUTINE::CALLABLE_BASED_HANDLER change_handler( item_creation_handler,
1462 item_modification_handler,
1463 item_removal_handler );
1464
1465 // Persistent settings between dialog invocations
1466 // Init with some sensible defaults
1467 static OUTSET_ROUTINE::PARAMETERS outset_params_fp_edit
1468 {
1469 pcbIUScale.mmToIU( 0.25 ), // A common outset value
1470 false,
1471 false,
1472 true,
1473 F_CrtYd,
1474 frame.GetDesignSettings().GetLineThickness( F_CrtYd ),
1475 pcbIUScale.mmToIU( 0.01 ),
1476 false,
1477 };
1478
1479 static OUTSET_ROUTINE::PARAMETERS outset_params_pcb_edit
1480 {
1481 pcbIUScale.mmToIU( 1 ),
1482 true,
1483 true,
1484 true,
1485 Edge_Cuts, // Outsets often for slots?
1486 frame.GetDesignSettings().GetLineThickness( Edge_Cuts ),
1487 std::nullopt,
1488 false,
1489 };
1490
1491 OUTSET_ROUTINE::PARAMETERS& outset_params = IsFootprintEditor() ? outset_params_fp_edit
1492 : outset_params_pcb_edit;
1493
1494 {
1495 DIALOG_OUTSET_ITEMS dlg( frame, outset_params );
1496
1497 if( dlg.ShowModal() == wxID_CANCEL )
1498 return 0;
1499 }
1500
1501 OUTSET_ROUTINE outset_routine( frame.GetModel(), change_handler, outset_params );
1502
1503 for( EDA_ITEM* item : selection )
1504 {
1505 if( !item->IsBOARD_ITEM() )
1506 continue;
1507
1508 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
1509 outset_routine.ProcessItem( *board_item );
1510 }
1511
1512 // Deselect all the original items
1513 m_selectionTool->ClearSelection();
1514
1515 // Select added and modified items
1516 for( BOARD_ITEM* item : items_to_select_on_success )
1517 m_selectionTool->AddItemToSel( item, true );
1518
1519 if( any_items_created )
1520 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1521
1522 // Notify other tools of the changes
1524
1525 commit.Push( outset_routine.GetCommitDescription() );
1526
1527 if( const std::optional<wxString> msg = outset_routine.GetStatusMessage() )
1528 frame.ShowInfoBarMsg( *msg );
1529
1530 return 0;
1531}
1532
1533
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
BASE_SET & reset(size_t pos)
Definition base_set.h:143
BASE_SET & set(size_t pos)
Definition base_set.h:116
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
Container for design settings for a BOARD object.
int GetLayerClass(PCB_LAYER_ID aLayer) const
int m_LineThickness[LAYER_CLASS_COUNT]
ZONE_SETTINGS & GetDefaultZoneSettings()
Abstract interface for BOARD_ITEMs capable of storing other items inside.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:265
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:479
virtual bool HasLineStroke() const
Check if this item has line stoke properties.
Definition board_item.h:255
int GetMaxError() const
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition board.h:595
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition collector.h:107
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:398
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:86
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
CONVERT_SETTINGS * m_settings
bool TransferDataToWindow() override
wxRadioButton * m_rbBoundingHull
wxRadioButton * m_rbMimicLineWidth
wxRadioButton * m_rbCenterline
void onRadioButton(wxCommandEvent &aEvent)
bool TransferDataFromWindow() override
wxCheckBox * m_cbDeleteOriginals
CONVERT_SETTINGS_DIALOG(EDA_DRAW_FRAME *aParent, CONVERT_SETTINGS *aSettings, bool aShowCopyLineWidthOption, bool aShowCenterlineOption, bool aShowBoundingHullOption)
wxStaticText * m_widthLabel
wxStaticText * m_widthUnits
int CreateLines(const TOOL_EVENT &aEvent)
Convert selected polygon-like object to graphic lines, if possible.
bool Init() override
Init() is called once upon a registration of the tool.
int SegmentToArc(const TOOL_EVENT &aEvent)
Convert selected segment (graphic or track) to an arc of the same type.
void initUserSettings()
Initialize the user settings for the tool.
SHAPE_POLY_SET makePolysFromChainedSegs(const std::deque< EDA_ITEM * > &aItems, CONVERT_STRATEGY aStrategy)
Try to make polygons from chained segments in the selected items.
SHAPE_POLY_SET makePolysFromOpenGraphics(const std::deque< EDA_ITEM * > &aItems, int aGap)
Make polygons from graphic shapes and zones.
static std::optional< SEG > getStartEndPoints(EDA_ITEM *aItem)
Retrieve the start and end points for a generic item.
SHAPE_POLY_SET makePolysFromClosedGraphics(const std::deque< EDA_ITEM * > &aItems, CONVERT_STRATEGY aStrategy)
int CreatePolys(const TOOL_EVENT &aEvent)
Convert selected lines to a polygon, if possible.
PCB_SELECTION_TOOL * m_selectionTool
virtual ~CONVERT_TOOL()
CONVERT_SETTINGS m_userSettings
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
CONDITIONAL_MENU * m_menu
PCB_BASE_FRAME * m_frame
int OutsetItems(const TOOL_EVENT &aEvent)
Convert selected items to outset versions of themselves.
DIALOG_OUTSET_ITEMS, derived from DIALOG_OUTSET_ITEMS_BASE, created by wxFormBuilder.
void SetupStandardButtons(std::map< int, wxString > aLabels={})
std::string m_hash_key
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
DIALOG_SHIM(wxWindow *aParent, wxWindowID id, const wxString &title, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER, const wxString &name=wxDialogNameStr)
int ShowModal() override
The base class for create windows for drawing purpose.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
virtual VECTOR2I GetPosition() const
Definition eda_item.h:282
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:202
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:155
int GetEllipseMinorRadius() const
Definition eda_shape.h:310
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:292
void SetCenter(const VECTOR2I &aCenter)
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:338
FILL_T GetFillMode() const
Definition eda_shape.h:158
int GetEllipseMajorRadius() const
Definition eda_shape.h:301
SHAPE_POLY_SET & GetPolyShape()
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:319
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:185
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:152
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:240
bool IsClosed() const
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:329
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition eda_shape.h:404
int GetCornerRadius() const
void SetFillMode(FILL_T aFill)
VECTOR2I GetArcMid() const
static const TOOL_EVENT SelectedEvent
Definition actions.h:341
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:348
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
A handler that is based on a set of callbacks provided by the user of the ITEM_MODIFICATION_ROUTINE.
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition lset.cpp:623
Handle the data for a net.
Definition netinfo.h:46
void ProcessItem(BOARD_ITEM &aItem)
wxString GetCommitDescription() const override
std::optional< wxString > GetStatusMessage() const
Definition pad.h:61
static TOOL_ACTION convertToKeepout
static TOOL_ACTION convertToTracks
static TOOL_ACTION convertToLines
static TOOL_ACTION convertToZone
static TOOL_ACTION convertToPoly
static TOOL_ACTION outsetItems
Create outset items from selection.
static TOOL_ACTION convertToArc
static TOOL_ACTION createArray
Tool for creating an array of objects.
void SetMid(const VECTOR2I &aMid)
Definition pcb_track.h:285
const VECTOR2I & GetMid() const
Definition pcb_track.h:286
std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
void GetBoundingHull(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Common, abstract interface for edit frames.
BOARD * GetBoard() const
static SELECTION_CONDITION SameLayer()
Creates a functor that tests if selection contains items that belong exclusively to the same layer.
The selection tool: currently supports:
void FilterCollectorForLockedItems(GENERAL_COLLECTOR &aCollector)
In the PCB editor strip out any locked items unless the OverrideLocks checkbox is set.
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:78
void SetWidth(int aWidth) override
int GetWidth() const override
void SetShape(SHAPE_T aShape) override
Definition pcb_shape.h:200
void SetEnd(const VECTOR2I &aEnd) override
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
void SetPolyShape(const SHAPE_POLY_SET &aShape) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the shape to a closed polygon.
STROKE_PARAMS GetStroke() const override
void SetStart(const VECTOR2I &aStart) override
void SetStroke(const STROKE_PARAMS &aStroke) override
T * frame() const
bool IsFootprintEditor() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
BOARD * board() const
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:89
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:92
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the track shape to a closed polygon.
const VECTOR2I & GetStart() const
Definition pcb_track.h:93
const VECTOR2I & GetEnd() const
Definition pcb_track.h:90
virtual void SetWidth(int aWidth)
Definition pcb_track.h:86
virtual int GetWidth() const
Definition pcb_track.h:87
A round rectangle shape, based on a rectangle and a radius.
Definition roundrect.h:32
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aMaxError) const
Get the polygonal representation of the roundrect.
Definition roundrect.cpp:79
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
int Length() const
Return the length (this).
Definition seg.h:339
static SEG::ecoord Square(int a)
Definition seg.h:119
Class that groups generic conditions for selected items.
static SELECTION_CONDITION MoreThan(int aNumber)
Create a functor that tests if the number of selected items is greater than the value given as parame...
static SELECTION_CONDITION Count(int aNumber)
Create a functor that tests if the number of selected items is equal to the value given as parameter.
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
const VECTOR2I & GetArcMid() const
Definition shape_arc.h:116
SHAPE_ARC Reversed() const
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError) const
Build a polyline approximation of the ellipse or arc.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
virtual const VECTOR2I GetPoint(int aIndex) const override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
virtual const SEG GetSegment(int aIndex) const override
virtual size_t GetSegmentCount() const override
size_t ArcCount() const
bool IsArcSegment(size_t aSegment) const
void Insert(size_t aVertex, const VECTOR2I &aP)
Represent a set of closed polygons.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
const SHAPE_LINE_CHAIN & CHole(int aOutline, int aHole) const
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Simple container to manage line stroke parameters.
int GetWidth() const
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
Generic, UI-independent tool event.
Definition tool_event.h:167
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
ZONE_SETTINGS handles zones parameters.
void SetIsRuleArea(bool aEnable)
void ExportSetting(ZONE &aTarget, bool aFullExport=true) const
Function ExportSetting copy settings to a given zone.
Handle a list of polygons defining a copper zone.
Definition zone.h:70
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition zone.cpp:1520
SHAPE_POLY_SET * Outline()
Definition zone.h:418
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
This file is part of the common library.
PCB_SELECTION_CONDITIONS P_S_C
@ ROUND_ALL_CORNERS
All angles are rounded.
int InvokeCopperZonesEditor(PCB_BASE_FRAME *aCaller, ZONE *aZone, ZONE_SETTINGS *aSettings, CONVERT_SETTINGS *aConvertSettings)
Function InvokeCopperZonesEditor invokes up a modal dialog window for copper zone editing.
int InvokeNonCopperZonesEditor(PCB_BASE_FRAME *aParent, ZONE_SETTINGS *aSettings, CONVERT_SETTINGS *aConvertSettings)
Function InvokeNonCopperZonesEditor invokes up a modal dialog window for non-copper zone editing.
int InvokeRuleAreaEditor(PCB_BASE_FRAME *aCaller, ZONE_SETTINGS *aZoneSettings, BOARD *aBoard, CONVERT_SETTINGS *aConvertSettings)
Function InvokeRuleAreaEditor invokes up a modal dialog window for copper zone editing.
#define _(s)
#define STRUCT_DELETED
flag indication structures to be erased
#define SKIP_STRUCT
flag indicating that the structure should be ignored
@ ELLIPSE
Definition eda_shape.h:52
@ SEGMENT
Definition eda_shape.h:46
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
@ ELLIPSE_ARC
Definition eda_shape.h:53
FILL_T
Definition eda_shape.h:59
static const std::vector< KICAD_T > padTypes
static const std::vector< KICAD_T > trackTypes
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:39
bool IsNonCopperLayer(int aLayerId)
Test whether a layer is a non copper layer.
Definition layer_ids.h:708
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:675
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ Edge_Cuts
Definition layer_ids.h:108
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ F_Cu
Definition layer_ids.h:60
This file contains miscellaneous commonly used macros and functions.
#define _HKI(x)
Definition page_info.cpp:40
BARCODE class definition.
std::optional< int > GetBoardItemWidth(const BOARD_ITEM &aItem)
Gets the width of a BOARD_ITEM, for items that have a meaningful width.
Utility functions that can be shared by PCB tools.
CONVERT_STRATEGY
@ COPY_LINEWIDTH
@ CENTERLINE
@ BOUNDING_HULL
static PGM_BASE * process
SCH_CONDITIONS S_C
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
CONVERT_STRATEGY m_Strategy
VECTOR2I center
const SHAPE_LINE_CHAIN chain
VECTOR2I end
const VECTOR2I CalcArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition trigo.cpp:530
@ PCB_SHAPE_LOCATE_ELLIPSE_ARC_T
Definition typeinfo.h:237
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:81
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:85
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:83
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:94
@ PCB_SHAPE_LOCATE_CIRCLE_T
Definition typeinfo.h:132
@ PCB_SHAPE_LOCATE_SEGMENT_T
Definition typeinfo.h:130
@ PCB_SHAPE_LOCATE_RECT_T
Definition typeinfo.h:131
@ PCB_SHAPE_LOCATE_ELLIPSE_T
Definition typeinfo.h:236
@ PCB_SHAPE_LOCATE_BEZIER_T
Definition typeinfo.h:135
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
@ PCB_SHAPE_LOCATE_POLY_T
Definition typeinfo.h:134
@ PCB_SHAPE_LOCATE_ARC_T
Definition typeinfo.h:133
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683