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 // TODO: This code has a somewhat-similar purpose to ConvertOutlineToPolygon but is slightly
591 // different, so this remains a separate algorithm. It might be nice to analyze the dfiferences
592 // in requirements and refactor this.
593
594 // Using a large epsilon here to allow for sloppy drawing can cause the algorithm to miss very
595 // short segments in a converted bezier. So use an epsilon only large enough to cover for
596 // rouding errors in the conversion.
597 int chainingEpsilon = 100; // max dist from one endPt to next startPt in IU
598
599 SHAPE_POLY_SET poly;
600
601 // Stores pairs of (anchor, item) where anchor == 0 -> SEG.A, anchor == 1 -> SEG.B
602 std::map<VECTOR2I, std::vector<std::pair<int, EDA_ITEM*>>> connections;
603 std::deque<EDA_ITEM*> toCheck;
604
605 auto closeEnough =
606 []( const VECTOR2I& aLeft, const VECTOR2I& aRight, int aLimit )
607 {
608 return ( aLeft - aRight ).SquaredEuclideanNorm() <= SEG::Square( aLimit );
609 };
610
611 auto findInsertionPoint =
612 [&]( const VECTOR2I& aPoint ) -> VECTOR2I
613 {
614 if( connections.count( aPoint ) )
615 return aPoint;
616
617 for( const auto& candidatePair : connections )
618 {
619 if( closeEnough( aPoint, candidatePair.first, chainingEpsilon ) )
620 return candidatePair.first;
621 }
622
623 return aPoint;
624 };
625
626 for( EDA_ITEM* item : aItems )
627 {
628 if( std::optional<SEG> seg = getStartEndPoints( item ) )
629 {
630 toCheck.push_back( item );
631 connections[findInsertionPoint( seg->A )].emplace_back( std::make_pair( 0, item ) );
632 connections[findInsertionPoint( seg->B )].emplace_back( std::make_pair( 1, item ) );
633 }
634 }
635
636 while( !toCheck.empty() )
637 {
638 std::vector<BOARD_ITEM*> insertedItems;
639
640 EDA_ITEM* candidate = toCheck.front();
641 toCheck.pop_front();
642
643 if( candidate->GetFlags() & SKIP_STRUCT )
644 continue;
645
646 SHAPE_LINE_CHAIN outline;
647
648 auto insert =
649 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
650 {
651 if( aItem->IsType( { PCB_ARC_T, PCB_SHAPE_LOCATE_ARC_T } ) )
652 {
653 SHAPE_ARC arc;
654
655 if( aItem->Type() == PCB_ARC_T )
656 {
657 PCB_ARC* pcb_arc = static_cast<PCB_ARC*>( aItem );
658 arc = *static_cast<SHAPE_ARC*>( pcb_arc->GetEffectiveShape().get() );
659 }
660 else
661 {
662 PCB_SHAPE* pcb_shape = static_cast<PCB_SHAPE*>( aItem );
663 arc = SHAPE_ARC( pcb_shape->GetStart(), pcb_shape->GetArcMid(),
664 pcb_shape->GetEnd(), pcb_shape->GetWidth() );
665 }
666
667 if( aDirection )
668 outline.Append( aAnchor == arc.GetP0() ? arc : arc.Reversed() );
669 else
670 outline.Insert( 0, aAnchor == arc.GetP0() ? arc : arc.Reversed() );
671
672 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
673 }
674 else if( aItem->IsType( { PCB_SHAPE_LOCATE_BEZIER_T } ) )
675 {
676 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
677
678 if( aAnchor == graphic->GetStart() )
679 {
680 for( const VECTOR2I& pt : graphic->GetBezierPoints() )
681 {
682 if( aDirection )
683 outline.Append( pt );
684 else
685 outline.Insert( 0, pt );
686 }
687
688 }
689 else
690 {
691 for( const VECTOR2I& pt : std::ranges::reverse_view( graphic->GetBezierPoints() ) )
692 {
693 if( aDirection )
694 outline.Append( pt );
695 else
696 outline.Insert( 0, pt );
697 }
698 }
699
700 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
701 }
702 else if( std::optional<SEG> nextSeg = getStartEndPoints( aItem ) )
703 {
704 VECTOR2I& point = ( aAnchor == nextSeg->A ) ? nextSeg->B : nextSeg->A;
705
706 if( aDirection )
707 outline.Append( point );
708 else
709 outline.Insert( 0, point );
710
711 insertedItems.push_back( static_cast<BOARD_ITEM*>( aItem ) );
712 }
713 };
714
715 // aDirection == true for walking "right" and appending to the end of points
716 // false for walking "left" and prepending to the beginning
717 std::function<void( EDA_ITEM*, const VECTOR2I&, bool )> process =
718 [&]( EDA_ITEM* aItem, const VECTOR2I& aAnchor, bool aDirection )
719 {
720 if( aItem->GetFlags() & SKIP_STRUCT )
721 return;
722
723 aItem->SetFlags( SKIP_STRUCT );
724
725 insert( aItem, aAnchor, aDirection );
726
727 std::optional<SEG> anchors = getStartEndPoints( aItem );
728 wxASSERT( anchors );
729
730 VECTOR2I nextAnchor = ( aAnchor == anchors->A ) ? anchors->B : anchors->A;
731
732 for( std::pair<int, EDA_ITEM*> pair : connections[nextAnchor] )
733 {
734 if( pair.second == aItem )
735 continue;
736
737 process( pair.second, nextAnchor, aDirection );
738 }
739 };
740
741 std::optional<SEG> anchors = getStartEndPoints( candidate );
742 wxASSERT( anchors );
743
744 // Start with the first object and walk "right"
745 // Note if the first object is an arc, we don't need to insert its first point here, the
746 // whole arc will be inserted at anchor B inside process()
747 if( !candidate->IsType( { PCB_ARC_T, PCB_SHAPE_LOCATE_ARC_T } ) )
748 insert( candidate, anchors->A, true );
749
750 process( candidate, anchors->B, true );
751
752 // check for any candidates on the "left"
753 EDA_ITEM* left = nullptr;
754
755 for( std::pair<int, EDA_ITEM*> possibleLeft : connections[anchors->A] )
756 {
757 if( possibleLeft.second != candidate )
758 {
759 left = possibleLeft.second;
760 break;
761 }
762 }
763
764 if( left )
765 process( left, anchors->A, false );
766
767 if( outline.PointCount() < 3
768 || !closeEnough( outline.GetPoint( 0 ), outline.GetPoint( -1 ), chainingEpsilon ) )
769 {
770 for( EDA_ITEM* item : insertedItems )
771 item->ClearFlags( SKIP_STRUCT );
772
773 continue;
774 }
775
776 outline.SetClosed( true );
777
778 poly.AddOutline( outline );
779
780 if( aStrategy == BOUNDING_HULL )
781 {
782 for( BOARD_ITEM* item : insertedItems )
783 item->TransformShapeToPolygon( poly, UNDEFINED_LAYER, 0, item->GetMaxError(), ERROR_INSIDE );
784 }
785
786 insertedItems.clear();
787 }
788
789 return poly;
790}
791
792
793SHAPE_POLY_SET CONVERT_TOOL::makePolysFromOpenGraphics( const std::deque<EDA_ITEM*>& aItems, int aGap )
794{
795 SHAPE_POLY_SET poly;
796
797 for( EDA_ITEM* item : aItems )
798 {
799 if( item->GetFlags() & SKIP_STRUCT )
800 continue;
801
802 switch( item->Type() )
803 {
804 case PCB_SHAPE_T:
805 {
806 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
807
808 if( shape->IsClosed() )
809 continue;
810
811 shape->TransformShapeToPolygon( poly, UNDEFINED_LAYER, aGap, shape->GetMaxError(), ERROR_INSIDE );
812 shape->SetFlags( SKIP_STRUCT );
813
814 break;
815 }
816
817 case PCB_TRACE_T:
818 case PCB_ARC_T:
819 case PCB_VIA_T:
820 {
821 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
822
823 track->TransformShapeToPolygon( poly, UNDEFINED_LAYER, aGap, track->GetMaxError(), ERROR_INSIDE );
824 track->SetFlags( SKIP_STRUCT );
825
826 break;
827 }
828
829 default:
830 continue;
831 }
832 }
833
834 return poly;
835}
836
837
839 CONVERT_STRATEGY aStrategy )
840{
841 SHAPE_POLY_SET poly;
842
843 for( EDA_ITEM* item : aItems )
844 {
845 if( item->GetFlags() & SKIP_STRUCT )
846 continue;
847
848 switch( item->Type() )
849 {
850 case PCB_SHAPE_T:
851 {
852 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
853 FILL_T wasFilled = shape->GetFillMode();
854
855 if( !shape->IsClosed() )
856 continue;
857
858 if( shape->GetShape() == SHAPE_T::CIRCLE && aStrategy != BOUNDING_HULL )
859 {
860 VECTOR2I c = shape->GetCenter();
861 int R = shape->GetRadius();
862 SHAPE_ARC arc( c - VECTOR2I( R, 0 ), c + VECTOR2I( R, 0 ), c - VECTOR2I( R, 0 ), 0 );
863
864 poly.NewOutline();
865 poly.Append( arc );
866 }
867 else
868 {
869 if( aStrategy != BOUNDING_HULL )
870 shape->SetFilled( true );
871
873 aStrategy != BOUNDING_HULL );
874
875 if( aStrategy != BOUNDING_HULL )
876 shape->SetFillMode( wasFilled );
877 }
878
879 shape->SetFlags( SKIP_STRUCT );
880 break;
881 }
882
883 case PCB_ZONE_T:
884 poly.Append( *static_cast<ZONE*>( item )->Outline() );
885 item->SetFlags( SKIP_STRUCT );
886 break;
887
888 case PCB_FIELD_T:
889 case PCB_TEXT_T:
890 {
891 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
892 text->TransformTextToPolySet( poly, 0, text->GetMaxError(), ERROR_INSIDE );
893 text->SetFlags( SKIP_STRUCT );
894 break;
895 }
896
897 case PCB_BARCODE_T:
898 {
899 PCB_BARCODE* barcode = static_cast<PCB_BARCODE*>( item );
900
901 if( aStrategy == BOUNDING_HULL )
902 barcode->GetBoundingHull( poly, UNDEFINED_LAYER, 0, barcode->GetMaxError(), ERROR_INSIDE );
903 else
904 barcode->TransformShapeToPolySet( poly, UNDEFINED_LAYER, 0, barcode->GetMaxError(), ERROR_INSIDE );
905
906 barcode->SetFlags( SKIP_STRUCT );
907 break;
908 }
909
910 case PCB_PAD_T:
911 {
912 PAD* pad = static_cast<PAD*>( item );
913
914 pad->Padstack().ForEachUniqueLayer(
915 [&]( PCB_LAYER_ID aLayer )
916 {
917 SHAPE_POLY_SET layerPoly;
918 pad->TransformShapeToPolygon( layerPoly, aLayer, 0, pad->GetMaxError(), ERROR_INSIDE );
919 poly.BooleanAdd( layerPoly );
920 } );
921
922 pad->SetFlags( SKIP_STRUCT );
923 break;
924 }
925
926
927 default:
928 continue;
929 }
930 }
931
932 return poly;
933}
934
935
937{
938 auto& selection = m_selectionTool->RequestSelection(
939 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
940 {
941 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
942 {
943 BOARD_ITEM* item = aCollector[i];
944
945 switch( item->Type() )
946 {
947 case PCB_SHAPE_T:
948 switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
949 {
950 case SHAPE_T::SEGMENT:
951 case SHAPE_T::ARC:
952 case SHAPE_T::POLY:
954 break;
955
956 default:
957 aCollector.Remove( item );
958 }
959
960 break;
961
962 case PCB_ZONE_T:
963 break;
964
965 default:
966 aCollector.Remove( item );
967 }
968 }
969 } );
970
971 if( selection.Empty() )
972 return 0;
973
974 BOARD_COMMIT commit( m_frame );
976 FOOTPRINT_EDIT_FRAME* fpEditor = dynamic_cast<FOOTPRINT_EDIT_FRAME*>( m_frame );
977 FOOTPRINT* footprint = nullptr;
978 PCB_LAYER_ID targetLayer = m_frame->GetActiveLayer();
979 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
980
981 if( fpEditor )
982 footprint = fpEditor->GetBoard()->GetFirstFootprint();
983
984 auto handleGraphicSeg = [&]( EDA_ITEM* aItem, NETINFO_ITEM* aNet )
985 {
986 if( aItem->Type() != PCB_SHAPE_T )
987 return false;
988
989 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
990
991 if( graphic->GetShape() == SHAPE_T::SEGMENT )
992 {
993 PCB_TRACK* track = new PCB_TRACK( parent );
994
995 track->SetLayer( targetLayer );
996 track->SetStart( graphic->GetStart() );
997 track->SetEnd( graphic->GetEnd() );
998 track->SetWidth( graphic->GetWidth() );
999
1000 if( aNet )
1001 track->SetNet( aNet );
1002
1003 commit.Add( track );
1004
1005 return true;
1006 }
1007 else if( graphic->GetShape() == SHAPE_T::ARC )
1008 {
1009 PCB_ARC* arc = new PCB_ARC( parent );
1010
1011 arc->SetLayer( targetLayer );
1012 arc->SetStart( graphic->GetStart() );
1013 arc->SetEnd( graphic->GetEnd() );
1014 arc->SetMid( graphic->GetArcMid() );
1015 arc->SetWidth( graphic->GetWidth() );
1016
1017 if( aNet )
1018 arc->SetNet( aNet );
1019
1020 commit.Add( arc );
1021
1022 return true;
1023 }
1024
1025 return false;
1026 };
1027
1028 auto addGraphicChain =
1029 [&]( const SHAPE_LINE_CHAIN& aChain, std::optional<int> aWidth )
1030 {
1031 for( size_t si = 0; si < aChain.GetSegmentCount(); ++si )
1032 {
1033 const SEG seg = aChain.GetSegment( si );
1034
1035 if( seg.Length() == 0 )
1036 continue;
1037
1038 if( aChain.IsArcSegment( si ) )
1039 continue;
1040
1041 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
1042
1043 graphic->SetLayer( targetLayer );
1044 graphic->SetStart( seg.A );
1045 graphic->SetEnd( seg.B );
1046
1047 if( aWidth && *aWidth > 0 )
1048 graphic->SetWidth( *aWidth );
1049
1050 commit.Add( graphic );
1051 }
1052
1053 for( size_t ai = 0; ai < aChain.ArcCount(); ++ai )
1054 {
1055 const SHAPE_ARC& arc = aChain.Arc( ai );
1056
1057 if( arc.GetP0() == arc.GetP1() )
1058 continue;
1059
1060 PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::ARC );
1061
1062 graphic->SetLayer( targetLayer );
1063 graphic->SetFilled( false );
1064 graphic->SetArcGeometry( arc.GetP0(), arc.GetArcMid(), arc.GetP1() );
1065
1066 if( aWidth && *aWidth > 0 )
1067 graphic->SetWidth( *aWidth );
1068
1069 commit.Add( graphic );
1070 }
1071 };
1072
1073 auto addTrackChain = [&]( const SHAPE_LINE_CHAIN& aChain, std::optional<int> aWidth, NETINFO_ITEM* aNet )
1074 {
1075 for( size_t si = 0; si < aChain.GetSegmentCount(); ++si )
1076 {
1077 const SEG seg = aChain.GetSegment( si );
1078
1079 if( seg.Length() == 0 )
1080 continue;
1081
1082 if( aChain.IsArcSegment( si ) )
1083 continue;
1084
1085 PCB_TRACK* track = new PCB_TRACK( parent );
1086
1087 track->SetLayer( targetLayer );
1088 track->SetStart( seg.A );
1089 track->SetEnd( seg.B );
1090
1091 if( aWidth && *aWidth > 0 )
1092 track->SetWidth( *aWidth );
1093
1094 if( aNet )
1095 track->SetNet( aNet );
1096
1097 commit.Add( track );
1098 }
1099
1100 for( size_t ai = 0; ai < aChain.ArcCount(); ++ai )
1101 {
1102 const SHAPE_ARC& arc = aChain.Arc( ai );
1103
1104 if( arc.GetP0() == arc.GetP1() )
1105 continue;
1106
1107 PCB_ARC* trackArc = new PCB_ARC( parent );
1108
1109 trackArc->SetLayer( targetLayer );
1110 trackArc->SetStart( arc.GetP0() );
1111 trackArc->SetEnd( arc.GetP1() );
1112 trackArc->SetMid( arc.GetArcMid() );
1113
1114 if( aWidth && *aWidth > 0 )
1115 trackArc->SetWidth( *aWidth );
1116
1117 if( aNet )
1118 trackArc->SetNet( aNet );
1119
1120 commit.Add( trackArc );
1121 }
1122 };
1123
1124 auto processChain = [&]( const SHAPE_LINE_CHAIN& aChain, std::optional<int> aWidth, NETINFO_ITEM* aNet )
1125 {
1126 if( aChain.GetSegmentCount() == 0 && aChain.ArcCount() == 0 )
1127 return;
1128
1129 if( aEvent.IsAction( &PCB_ACTIONS::convertToLines ) )
1130 {
1131 addGraphicChain( aChain, aWidth );
1132 }
1133 else if( fpEditor )
1134 {
1135 addGraphicChain( aChain, aWidth );
1136 }
1137 else
1138 {
1139 addTrackChain( aChain, aWidth, aNet );
1140 }
1141 };
1142
1143 auto processPolySet = [&]( const SHAPE_POLY_SET& aPoly, std::optional<int> aWidth, NETINFO_ITEM* aNet )
1144 {
1145 for( int oi = 0; oi < aPoly.OutlineCount(); ++oi )
1146 {
1147 processChain( aPoly.COutline( oi ), aWidth, aNet );
1148
1149 for( int hi = 0; hi < aPoly.HoleCount( oi ); ++hi )
1150 processChain( aPoly.CHole( oi, hi ), aWidth, aNet );
1151 }
1152 };
1153
1154 if( aEvent.IsAction( &PCB_ACTIONS::convertToTracks ) )
1155 {
1156 if( !IsCopperLayer( targetLayer ) )
1157 {
1158 targetLayer = frame->SelectOneLayer( F_Cu, LSET::AllNonCuMask() );
1159
1160 if( targetLayer == UNDEFINED_LAYER ) // User canceled
1161 return true;
1162 }
1163 }
1164 else
1165 {
1166 CONVERT_SETTINGS_DIALOG dlg( m_frame, &m_userSettings, false, false, false );
1167
1168 if( dlg.ShowModal() != wxID_OK )
1169 return true;
1170 }
1171
1172 for( EDA_ITEM* item : selection )
1173 {
1174 if( !item->IsBOARD_ITEM() )
1175 continue;
1176
1177 BOARD_CONNECTED_ITEM* connected = dynamic_cast<BOARD_CONNECTED_ITEM*>( item );
1178 NETINFO_ITEM* sourceNet = connected ? connected->GetNet() : nullptr;
1179
1180 if( handleGraphicSeg( item, sourceNet ) )
1181 continue;
1182
1183 BOARD_ITEM& boardItem = static_cast<BOARD_ITEM&>( *item );
1184 std::optional<int> itemWidth = GetBoardItemWidth( boardItem );
1185
1186 if( boardItem.Type() == PCB_SHAPE_T )
1187 {
1188 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( item );
1189
1190 switch( graphic->GetShape() )
1191 {
1192 case SHAPE_T::RECTANGLE:
1193 {
1194 SHAPE_RECT rect( graphic->GetStart(), graphic->GetEnd() );
1195 ROUNDRECT rrect( rect, graphic->GetCornerRadius(), true );
1196 SHAPE_POLY_SET poly;
1197
1198 rrect.TransformToPolygon( poly, graphic->GetMaxError() );
1199 processPolySet( poly, itemWidth, sourceNet );
1200 break;
1201 }
1202
1203 case SHAPE_T::POLY: processPolySet( graphic->GetPolyShape(), itemWidth, sourceNet ); break;
1204
1205 case SHAPE_T::ELLIPSE:
1207 {
1208 SHAPE_ELLIPSE e =
1209 graphic->GetShape() == SHAPE_T::ELLIPSE_ARC
1210 ? SHAPE_ELLIPSE( graphic->GetEllipseCenter(), graphic->GetEllipseMajorRadius(),
1211 graphic->GetEllipseMinorRadius(), graphic->GetEllipseRotation(),
1212 graphic->GetEllipseStartAngle(), graphic->GetEllipseEndAngle() )
1213 : SHAPE_ELLIPSE( graphic->GetEllipseCenter(), graphic->GetEllipseMajorRadius(),
1214 graphic->GetEllipseMinorRadius(), graphic->GetEllipseRotation() );
1215
1217 SHAPE_POLY_SET poly;
1218 poly.AddOutline( chain );
1219 processPolySet( poly, itemWidth, sourceNet );
1220 break;
1221 }
1222
1223 default:
1224 wxFAIL_MSG( wxT( "Unhandled graphic shape type in PolyToLines" ) );
1225 break;
1226 }
1227 }
1228 else if( boardItem.Type() == PCB_ZONE_T )
1229 {
1230 ZONE* zone = static_cast<ZONE*>( item );
1231 processPolySet( *zone->Outline(), itemWidth, sourceNet );
1232 }
1233 else
1234 {
1235 wxFAIL_MSG( wxT( "Unhandled type in PolyToLines" ) );
1236 }
1237 }
1238
1239 if( m_userSettings.m_DeleteOriginals )
1240 {
1241 PCB_SELECTION selectionCopy = selection;
1242 m_selectionTool->ClearSelection();
1243
1244 for( EDA_ITEM* item : selectionCopy )
1245 commit.Remove( item );
1246 }
1247
1248 commit.Push( _( "Create Lines" ) );
1249
1250 return 0;
1251}
1252
1253
1255{
1256 auto& selection = m_selectionTool->RequestSelection(
1257 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1258 {
1259 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1260 {
1261 BOARD_ITEM* item = aCollector[i];
1262
1263 if( !item->IsType( { PCB_SHAPE_T, PCB_TRACE_T, PCB_ARC_T } ) )
1264 aCollector.Remove( item );
1265 }
1266 } );
1267
1268 if( selection.Empty() || !selection.Front()->IsBOARD_ITEM() )
1269 return -1;
1270
1271 BOARD_ITEM* source = static_cast<BOARD_ITEM*>( selection.Front() );
1272 VECTOR2I start, end, mid;
1273
1274 // Offset the midpoint along the normal a little bit so that it's more obviously an arc
1275 const double offsetRatio = 0.1;
1276
1277 if( std::optional<SEG> seg = getStartEndPoints( source ) )
1278 {
1279 start = seg->A;
1280 end = seg->B;
1281
1282 VECTOR2I normal = ( seg->B - seg->A ).Perpendicular().Resize( offsetRatio * seg->Length() );
1283 mid = seg->Center() + normal;
1284 }
1285 else
1286 {
1287 return -1;
1288 }
1289
1291 BOARD_ITEM_CONTAINER* parent = frame->GetModel();
1292 PCB_LAYER_ID layer = source->GetLayer();
1293 BOARD_COMMIT commit( m_frame );
1294
1295 if( source->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( source )->GetShape() == SHAPE_T::SEGMENT )
1296 {
1297 PCB_SHAPE* line = static_cast<PCB_SHAPE*>( source );
1298 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1299
1300 VECTOR2I center = CalcArcCenter( start, mid, end );
1301
1302 arc->SetFilled( false );
1303 arc->SetLayer( layer );
1304 arc->SetStroke( line->GetStroke() );
1305
1306 arc->SetCenter( center );
1307 arc->SetStart( start );
1308 arc->SetEnd( end );
1309
1310 commit.Add( arc );
1311 }
1312 else if( source->Type() == PCB_SHAPE_T && static_cast<PCB_SHAPE*>( source )->GetShape() == SHAPE_T::ARC )
1313 {
1314 PCB_SHAPE* source_arc = static_cast<PCB_SHAPE*>( source );
1315 PCB_ARC* arc = new PCB_ARC( parent );
1316
1317 arc->SetLayer( layer );
1318 arc->SetWidth( source_arc->GetWidth() );
1319 arc->SetStart( start );
1320 arc->SetMid( source_arc->GetArcMid() );
1321 arc->SetEnd( end );
1322
1323 commit.Add( arc );
1324 }
1325 else if( source->Type() == PCB_TRACE_T )
1326 {
1327 PCB_TRACK* line = static_cast<PCB_TRACK*>( source );
1328 PCB_ARC* arc = new PCB_ARC( parent );
1329
1330 arc->SetLayer( layer );
1331 arc->SetWidth( line->GetWidth() );
1332 arc->SetNet( line->GetNet() );
1333 arc->SetStart( start );
1334 arc->SetMid( mid );
1335 arc->SetEnd( end );
1336
1337 commit.Add( arc );
1338 }
1339 else if( source->Type() == PCB_ARC_T )
1340 {
1341 PCB_ARC* source_arc = static_cast<PCB_ARC*>( source );
1342 PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
1343
1344 arc->SetFilled( false );
1345 arc->SetLayer( layer );
1346 arc->SetWidth( source_arc->GetWidth() );
1347
1348 arc->SetArcGeometry( source_arc->GetStart(), source_arc->GetMid(), source_arc->GetEnd() );
1349 commit.Add( arc );
1350 }
1351
1352 commit.Push( _( "Create Arc" ) );
1353
1354 return 0;
1355}
1356
1357
1358std::optional<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem )
1359{
1360 switch( aItem->Type() )
1361 {
1362 case PCB_SHAPE_T:
1363 {
1364 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
1365
1366 switch( shape->GetShape() )
1367 {
1368 case SHAPE_T::SEGMENT:
1369 case SHAPE_T::ARC:
1370 case SHAPE_T::POLY:
1371 case SHAPE_T::BEZIER:
1372 if( shape->GetStart() == shape->GetEnd() )
1373 return std::nullopt;
1374
1375 return std::make_optional<SEG>( VECTOR2I( shape->GetStart() ), VECTOR2I( shape->GetEnd() ) );
1376
1377 default:
1378 return std::nullopt;
1379 }
1380 }
1381
1382 case PCB_TRACE_T:
1383 {
1384 PCB_TRACK* line = static_cast<PCB_TRACK*>( aItem );
1385 return std::make_optional<SEG>( VECTOR2I( line->GetStart() ), VECTOR2I( line->GetEnd() ) );
1386 }
1387
1388 case PCB_ARC_T:
1389 {
1390 PCB_ARC* arc = static_cast<PCB_ARC*>( aItem );
1391 return std::make_optional<SEG>( VECTOR2I( arc->GetStart() ), VECTOR2I( arc->GetEnd() ) );
1392 }
1393
1394 default:
1395 return std::nullopt;
1396 }
1397}
1398
1399
1401{
1403 PCB_SELECTION& selection = m_selectionTool->RequestSelection(
1404 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1405 {
1406 // Iterate from the back so we don't have to worry about removals.
1407 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1408 {
1409 BOARD_ITEM* item = aCollector[i];
1410
1411 // We've converted the polygon and rectangle to segments, so drop everything
1412 // that isn't a segment at this point
1413 if( !item->IsType( { PCB_PAD_T, PCB_SHAPE_T } ) )
1414 aCollector.Remove( item );
1415 }
1416
1417 sTool->FilterCollectorForLockedItems( aCollector );
1418 } );
1419
1420 BOARD_COMMIT commit( this );
1421
1422 for( EDA_ITEM* item : selection )
1423 item->ClearFlags( STRUCT_DELETED );
1424
1425 // List of thing to select at the end of the operation
1426 // (doing it as we go will invalidate the iterator)
1427 std::vector<BOARD_ITEM*> items_to_select_on_success;
1428
1429 // Handle modifications to existing items by the routine
1430 // How to deal with this depends on whether we're in the footprint editor or not
1431 // and whether the item was conjured up by decomposing a polygon or rectangle
1432 auto item_modification_handler =
1433 [&]( BOARD_ITEM& aItem )
1434 {
1435 };
1436
1437 bool any_items_created = false;
1438
1439 auto item_creation_handler =
1440 [&]( std::unique_ptr<BOARD_ITEM> aItem )
1441 {
1442 any_items_created = true;
1443 items_to_select_on_success.push_back( aItem.get() );
1444 commit.Add( aItem.release() );
1445 };
1446
1447 auto item_removal_handler =
1448 [&]( BOARD_ITEM& aItem )
1449 {
1450 // If you do an outset on a FP pad, do you really want to delete
1451 // the parent?
1452 if( !aItem.GetParentFootprint() )
1453 {
1454 commit.Remove( &aItem );
1455 }
1456 };
1457
1458 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1459 ITEM_MODIFICATION_ROUTINE::CALLABLE_BASED_HANDLER change_handler( item_creation_handler,
1460 item_modification_handler,
1461 item_removal_handler );
1462
1463 // Persistent settings between dialog invocations
1464 // Init with some sensible defaults
1465 static OUTSET_ROUTINE::PARAMETERS outset_params_fp_edit
1466 {
1467 pcbIUScale.mmToIU( 0.25 ), // A common outset value
1468 false,
1469 false,
1470 true,
1471 F_CrtYd,
1472 frame.GetDesignSettings().GetLineThickness( F_CrtYd ),
1473 pcbIUScale.mmToIU( 0.01 ),
1474 false,
1475 };
1476
1477 static OUTSET_ROUTINE::PARAMETERS outset_params_pcb_edit
1478 {
1479 pcbIUScale.mmToIU( 1 ),
1480 true,
1481 true,
1482 true,
1483 Edge_Cuts, // Outsets often for slots?
1484 frame.GetDesignSettings().GetLineThickness( Edge_Cuts ),
1485 std::nullopt,
1486 false,
1487 };
1488
1489 OUTSET_ROUTINE::PARAMETERS& outset_params = IsFootprintEditor() ? outset_params_fp_edit
1490 : outset_params_pcb_edit;
1491
1492 {
1493 DIALOG_OUTSET_ITEMS dlg( frame, outset_params );
1494
1495 if( dlg.ShowModal() == wxID_CANCEL )
1496 return 0;
1497 }
1498
1499 OUTSET_ROUTINE outset_routine( frame.GetModel(), change_handler, outset_params );
1500
1501 for( EDA_ITEM* item : selection )
1502 {
1503 if( !item->IsBOARD_ITEM() )
1504 continue;
1505
1506 BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
1507 outset_routine.ProcessItem( *board_item );
1508 }
1509
1510 // Deselect all the original items
1511 m_selectionTool->ClearSelection();
1512
1513 // Select added and modified items
1514 for( BOARD_ITEM* item : items_to_select_on_success )
1515 m_selectionTool->AddItemToSel( item, true );
1516
1517 if( any_items_created )
1518 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1519
1520 // Notify other tools of the changes
1522
1523 commit.Push( outset_routine.GetCommitDescription() );
1524
1525 if( const std::optional<wxString> msg = outset_routine.GetStatusMessage() )
1526 frame.ShowInfoBarMsg( *msg );
1527
1528 return 0;
1529}
1530
1531
@ 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:80
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:264
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:457
virtual bool HasLineStroke() const
Check if this item has line stoke properties.
Definition board_item.h:254
int GetMaxError() const
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition board.h:535
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:302
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:284
void SetCenter(const VECTOR2I &aCenter)
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:330
FILL_T GetFillMode() const
Definition eda_shape.h:158
int GetEllipseMajorRadius() const
Definition eda_shape.h:293
SHAPE_POLY_SET & GetPolyShape()
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:311
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:185
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition eda_shape.h:419
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:232
bool IsClosed() const
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:194
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
void SetShape(SHAPE_T aShape)
Definition eda_shape.h:184
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:321
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition eda_shape.h:396
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:236
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
void SetWidth(int aWidth)
int GetCornerRadius() const
void SetFillMode(FILL_T aFill)
VECTOR2I GetArcMid() const
static const TOOL_EVENT SelectedEvent
Definition actions.h: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:77
int GetWidth() const override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the shape to a closed polygon.
STROKE_PARAMS GetStroke() const override
Definition pcb_shape.h:93
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition pcb_shape.h:94
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:1386
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