KiCad PCB EDA Suite
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 (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
5  * @author Jon Evans <[email protected]>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <bitmaps.h>
26 #include <board.h>
27 #include <board_commit.h>
28 #include <board_design_settings.h>
29 #include <collectors.h>
30 #include <confirm.h>
32 #include <footprint.h>
33 #include <footprint_edit_frame.h>
34 #include <fp_shape.h>
36 #include <menus_helpers.h>
37 #include <pcb_edit_frame.h>
38 #include <pcb_shape.h>
39 #include <pcb_track.h>
40 #include <tool/tool_manager.h>
41 #include <tools/edit_tool.h>
42 #include <tools/pcb_actions.h>
44 #include <trigo.h>
45 #include <zone.h>
46 
47 #include "convert_tool.h"
48 
49 
51  TOOL_INTERACTIVE( "pcbnew.Convert" ),
52  m_selectionTool( nullptr ),
53  m_menu( nullptr ),
54  m_frame( nullptr )
55 {
56 }
57 
58 
60 {
61  delete m_menu;
62 }
63 
64 
67 
68 
70 {
72  m_frame = getEditFrame<PCB_BASE_FRAME>();
73 
74  // Create a context menu and make it available through selection tool
75  m_menu = new CONDITIONAL_MENU( this );
77  m_menu->SetTitle( _( "Create from Selection" ) );
78 
79  static KICAD_T convertibleTracks[] = { PCB_TRACE_T, PCB_ARC_T, EOT };
80  static KICAD_T zones[] = { PCB_ZONE_T, PCB_FP_ZONE_T, EOT };
81 
82  auto graphicLines = P_S_C::OnlyGraphicShapeTypes( { SHAPE_T::SEGMENT,
85  SHAPE_T::ARC } )
86  && P_S_C::SameLayer();
87 
88  auto graphicToTrack = P_S_C::OnlyGraphicShapeTypes( { SHAPE_T::SEGMENT, SHAPE_T::ARC } );
89 
90  auto trackLines = S_C::MoreThan( 1 ) && S_C::OnlyTypes( convertibleTracks )
91  && P_S_C::SameLayer();
92 
93  auto anyLines = graphicLines || trackLines;
94  auto anyPolys = S_C::OnlyTypes( zones )
96 
97  auto lineToArc = S_C::Count( 1 )
99  || S_C::OnlyType( PCB_TRACE_T ) );
100 
101  auto showConvert = anyPolys || anyLines || lineToArc;
102  auto canCreatePolyType = anyLines || anyPolys;
103  auto canCreateTracks = anyPolys || graphicToTrack;
104 
105  m_menu->AddItem( PCB_ACTIONS::convertToPoly, canCreatePolyType );
106  m_menu->AddItem( PCB_ACTIONS::convertToZone, canCreatePolyType );
107  m_menu->AddItem( PCB_ACTIONS::convertToKeepout, canCreatePolyType );
109 
110  // Currently the code exists, but tracks are not really existing in footprints
111  // only segments on copper layers
113  m_menu->AddItem( PCB_ACTIONS::convertToTracks, canCreateTracks );
114 
116 
118  selToolMenu.AddMenu( m_menu, showConvert, 100 );
119 
120  return true;
121 }
122 
123 
125 {
126  FOOTPRINT* parentFootprint = nullptr;
127 
128  auto& selection = m_selectionTool->RequestSelection(
129  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
130  {
131  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
132  {
133  BOARD_ITEM* item = aCollector[i];
134 
135  switch( item->Type() )
136  {
137  case PCB_SHAPE_T:
138  case PCB_FP_SHAPE_T:
139  {
140  PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
141 
142  switch( shape->GetShape() )
143  {
144  case SHAPE_T::SEGMENT:
145  case SHAPE_T::RECT:
146  case SHAPE_T::CIRCLE:
147  case SHAPE_T::ARC:
148  if( shape->GetStart() == shape->GetEnd() )
149  aCollector.Remove( item );
150 
151  break;
152 
153  case SHAPE_T::POLY:
154  break;
155 
156  default:
157  aCollector.Remove( item );
158  }
159 
160  break;
161  }
162  case PCB_TRACE_T:
163  case PCB_ARC_T:
164  break;
165 
166  case PCB_ZONE_T:
167  case PCB_FP_ZONE_T:
168  break;
169 
170  default:
171  aCollector.Remove( item );
172  }
173  }
174  } );
175 
176  if( selection.Empty() )
177  return 0;
178 
179  PCB_LAYER_ID destLayer = m_frame->GetActiveLayer();
180  SHAPE_POLY_SET polySet = makePolysFromSegs( selection.GetItems() );
181 
182  polySet.Append( makePolysFromRects( selection.GetItems() ) );
183 
184  polySet.Append( makePolysFromCircles( selection.GetItems() ) );
185 
186  polySet.Append( extractPolygons( selection.GetItems() ) );
187 
188  if( polySet.IsEmpty() )
189  return 0;
190 
191  bool isFootprint = m_frame->IsType( FRAME_FOOTPRINT_EDITOR );
192 
193  if( isFootprint )
194  {
195  if( FP_SHAPE* graphic = dynamic_cast<FP_SHAPE*>( selection.Front() ) )
196  parentFootprint = graphic->GetParentFootprint();
197  else if( FP_ZONE* zone = dynamic_cast<FP_ZONE*>( selection.Front() ) )
198  parentFootprint = static_cast<FOOTPRINT*>( zone->GetParent() );
199  else
200  wxFAIL_MSG( wxT( "Unimplemented footprint parent in CONVERT_TOOL::CreatePolys" ) );
201  }
202 
203  BOARD_COMMIT commit( m_frame );
204 
205  // For now, we convert each outline in the returned shape to its own polygon
206  std::vector<SHAPE_POLY_SET> polys;
207 
208  for( int i = 0; i < polySet.OutlineCount(); i++ )
209  polys.emplace_back( SHAPE_POLY_SET( polySet.COutline( i ) ) );
210 
211  if( aEvent.IsAction( &PCB_ACTIONS::convertToPoly ) )
212  {
213  for( const SHAPE_POLY_SET& poly : polys )
214  {
215  PCB_SHAPE* graphic = isFootprint ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
216 
217  graphic->SetShape( SHAPE_T::POLY );
218  graphic->SetFilled( false );
219  graphic->SetWidth( poly.Outline( 0 ).Width() );
220  graphic->SetLayer( destLayer );
221  graphic->SetPolyShape( poly );
222 
223  commit.Add( graphic );
224  }
225 
226  commit.Push( _( "Convert shapes to polygon" ) );
227  }
228  else
229  {
230  // Creating zone or keepout
231  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
232  BOARD_ITEM_CONTAINER* parent = frame->GetModel();
233  ZONE_SETTINGS zoneInfo = frame->GetZoneSettings();
234 
235  bool nonCopper = IsNonCopperLayer( destLayer );
236  zoneInfo.m_Layers.reset().set( destLayer );
237  zoneInfo.m_Name.Empty();
238 
239  int ret;
240 
241  if( aEvent.IsAction( &PCB_ACTIONS::convertToKeepout ) )
242  {
243  zoneInfo.SetIsRuleArea( true );
244  ret = InvokeRuleAreaEditor( frame, &zoneInfo );
245  }
246  else if( nonCopper )
247  {
248  zoneInfo.SetIsRuleArea( false );
249  ret = InvokeNonCopperZonesEditor( frame, &zoneInfo );
250  }
251  else
252  {
253  zoneInfo.SetIsRuleArea( false );
254  ret = InvokeCopperZonesEditor( frame, &zoneInfo );
255  }
256 
257  if( ret == wxID_CANCEL )
258  return 0;
259 
260  for( const SHAPE_POLY_SET& poly : polys )
261  {
262  ZONE* zone = isFootprint ? new FP_ZONE( parent ) : new ZONE( parent );
263 
264  *zone->Outline() = poly;
265  zone->HatchBorder();
266 
267  zoneInfo.ExportSetting( *zone );
268 
269  commit.Add( zone );
270  }
271 
272  commit.Push( _( "Convert shapes to zone" ) );
273  }
274 
275  return 0;
276 }
277 
278 
279 SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque<EDA_ITEM*>& aItems )
280 {
281  // TODO: This code has a somewhat-similar purpose to ConvertOutlineToPolygon but is slightly
282  // different, so this remains a separate algorithm. It might be nice to analyze the dfiferences
283  // in requirements and refactor this.
284 
285  // Very tight epsilon used here to account for rounding errors in import, not sloppy drawing
286  const int chainingEpsilonSquared = SEG::Square( 100 );
287 
288  SHAPE_POLY_SET poly;
289 
290  // Stores pairs of (anchor, item) where anchor == 0 -> SEG.A, anchor == 1 -> SEG.B
291  std::map<VECTOR2I, std::vector<std::pair<int, EDA_ITEM*>>> connections;
292  std::set<EDA_ITEM*> used;
293  std::deque<EDA_ITEM*> toCheck;
294 
295  auto closeEnough =
296  []( VECTOR2I aLeft, VECTOR2I aRight, unsigned aLimit )
297  {
298  return ( aLeft - aRight ).SquaredEuclideanNorm() <= aLimit;
299  };
300 
301  auto findInsertionPoint =
302  [&]( VECTOR2I aPoint ) -> VECTOR2I
303  {
304  if( connections.count( aPoint ) )
305  return aPoint;
306 
307  for( const auto& candidatePair : connections )
308  {
309  if( closeEnough( aPoint, candidatePair.first, chainingEpsilonSquared ) )
310  return candidatePair.first;
311  }
312 
313  return aPoint;
314  };
315 
316  for( EDA_ITEM* item : aItems )
317  {
318  if( OPT<SEG> seg = getStartEndPoints( item, nullptr ) )
319  {
320  toCheck.push_back( item );
321  connections[findInsertionPoint( seg->A )].emplace_back( std::make_pair( 0, item ) );
322  connections[findInsertionPoint( seg->B )].emplace_back( std::make_pair( 1, item ) );
323  }
324  }
325 
326  while( !toCheck.empty() )
327  {
328  EDA_ITEM* candidate = toCheck.front();
329  toCheck.pop_front();
330 
331  if( used.count( candidate ) )
332  continue;
333 
334  int width = -1;
335  SHAPE_LINE_CHAIN outline;
336 
337  auto insert =
338  [&]( EDA_ITEM* aItem, VECTOR2I aAnchor, bool aDirection )
339  {
340  if( aItem->Type() == PCB_ARC_T
341  || ( ( aItem->Type() == PCB_SHAPE_T || aItem->Type() == PCB_FP_SHAPE_T )
342  && static_cast<PCB_SHAPE*>( aItem )->GetShape() == SHAPE_T::ARC ) )
343  {
344  SHAPE_ARC arc;
345 
346  if( aItem->Type() == PCB_ARC_T )
347  {
348  std::shared_ptr<SHAPE> es =
349  static_cast<PCB_ARC*>( aItem )->GetEffectiveShape();
350  arc = *static_cast<SHAPE_ARC*>( es.get() );
351  }
352  else
353  {
354  PCB_SHAPE* ps = static_cast<PCB_SHAPE*>( aItem );
355  arc = SHAPE_ARC( ps->GetStart(), ps->GetArcMid(), ps->GetEnd(),
356  ps->GetWidth() );
357  }
358 
359  if( aDirection )
360  outline.Append( aAnchor == arc.GetP0() ? arc : arc.Reversed() );
361  else
362  outline.Insert( 0, aAnchor == arc.GetP0() ? arc : arc.Reversed() );
363  }
364  else
365  {
366  OPT<SEG> nextSeg = getStartEndPoints( aItem, &width );
367  wxASSERT( nextSeg );
368 
369  VECTOR2I& point = ( aAnchor == nextSeg->A ) ? nextSeg->B : nextSeg->A;
370 
371  if( aDirection )
372  outline.Append( point );
373  else
374  outline.Insert( 0, point );
375  }
376  };
377 
378  // aDirection == true for walking "right" and appending to the end of points
379  // false for walking "left" and prepending to the beginning
380  std::function<void( EDA_ITEM*, VECTOR2I, bool )> process =
381  [&]( EDA_ITEM* aItem, VECTOR2I aAnchor, bool aDirection )
382  {
383  if( used.count( aItem ) )
384  return;
385 
386  used.insert( aItem );
387 
388  insert( aItem, aAnchor, aDirection );
389 
390  OPT<SEG> anchors = getStartEndPoints( aItem, &width );
391  wxASSERT( anchors );
392 
393  VECTOR2I nextAnchor = ( aAnchor == anchors->A ) ? anchors->B : anchors->A;
394 
395  for( std::pair<int, EDA_ITEM*> pair : connections[nextAnchor] )
396  {
397  if( pair.second == aItem )
398  continue;
399 
400  process( pair.second, nextAnchor, aDirection );
401  }
402  };
403 
404  OPT<SEG> anchors = getStartEndPoints( candidate, &width );
405  wxASSERT( anchors );
406 
407  // Start with the first object and walk "right"
408  // Note if the first object is an arc, we don't need to insert its first point here, the
409  // whole arc will be inserted at anchor B inside process()
410  if( !( candidate->Type() == PCB_ARC_T
411  || ( ( candidate->Type() == PCB_SHAPE_T || candidate->Type() == PCB_FP_SHAPE_T )
412  && static_cast<PCB_SHAPE*>( candidate )->GetShape() == SHAPE_T::ARC ) ) )
413  {
414  insert( candidate, anchors->A, true );
415  }
416 
417  process( candidate, anchors->B, true );
418 
419  // check for any candidates on the "left"
420  EDA_ITEM* left = nullptr;
421 
422  for( std::pair<int, EDA_ITEM*> possibleLeft : connections[anchors->A] )
423  {
424  if( possibleLeft.second != candidate )
425  {
426  left = possibleLeft.second;
427  break;
428  }
429  }
430 
431  if( left )
432  process( left, anchors->A, false );
433 
434  if( outline.PointCount() < 3 )
435  continue;
436 
437  outline.SetClosed( true );
438  outline.Simplify();
439 
440  if( width >= 0 )
441  outline.SetWidth( width );
442 
443  poly.AddOutline( outline );
444  }
445 
446  return poly;
447 }
448 
449 
450 SHAPE_POLY_SET CONVERT_TOOL::makePolysFromRects( const std::deque<EDA_ITEM*>& aItems )
451 {
452  SHAPE_POLY_SET poly;
453 
454  for( EDA_ITEM* item : aItems )
455  {
456  if( item->Type() != PCB_SHAPE_T && item->Type() != PCB_FP_SHAPE_T )
457  continue;
458 
459  PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( item );
460 
461  if( graphic->GetShape() != SHAPE_T::RECT )
462  continue;
463 
464  SHAPE_LINE_CHAIN outline;
465  VECTOR2I start( graphic->GetStart() );
466  VECTOR2I end( graphic->GetEnd() );
467 
468  outline.Append( start );
469  outline.Append( VECTOR2I( end.x, start.y ) );
470  outline.Append( end );
471  outline.Append( VECTOR2I( start.x, end.y ) );
472  outline.SetClosed( true );
473 
474  outline.SetWidth( graphic->GetWidth() );
475 
476  poly.AddOutline( outline );
477  }
478 
479  return poly;
480 }
481 
482 
483 SHAPE_POLY_SET CONVERT_TOOL::makePolysFromCircles( const std::deque<EDA_ITEM*>& aItems )
484 {
485  SHAPE_POLY_SET poly;
486 
487  for( EDA_ITEM* item : aItems )
488  {
489  if( item->Type() != PCB_SHAPE_T && item->Type() != PCB_FP_SHAPE_T )
490  continue;
491 
492  PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( item );
493 
494  if( graphic->GetShape() != SHAPE_T::CIRCLE )
495  continue;
496 
497  BOARD_DESIGN_SETTINGS& bds = graphic->GetBoard()->GetDesignSettings();
498  SHAPE_LINE_CHAIN outline;
499 
500  TransformCircleToPolygon( outline, graphic->GetPosition(), graphic->GetRadius(),
501  bds.m_MaxError, ERROR_OUTSIDE );
502 
503  poly.AddOutline( outline );
504  }
505 
506  return poly;
507 }
508 
509 
510 SHAPE_POLY_SET CONVERT_TOOL::extractPolygons( const std::deque<EDA_ITEM*>& aItems )
511 {
512  SHAPE_POLY_SET poly;
513 
514  for( EDA_ITEM* item : aItems )
515  {
516  switch( item->Type() )
517  {
518  case PCB_SHAPE_T:
519  case PCB_FP_SHAPE_T:
520  switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
521  {
522  case SHAPE_T::POLY:
523  poly.Append( static_cast<PCB_SHAPE*>( item )->GetPolyShape() );
524  break;
525 
526  default:
527  continue;
528  }
529 
530  break;
531 
532  case PCB_ZONE_T:
533  case PCB_FP_ZONE_T:
534  poly.Append( *static_cast<ZONE*>( item )->Outline() );
535  break;
536 
537  default:
538  continue;
539  }
540  }
541 
542  return poly;
543 }
544 
545 
547 {
548  auto& selection = m_selectionTool->RequestSelection(
549  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
550  {
551  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
552  {
553  BOARD_ITEM* item = aCollector[i];
554 
555  switch( item->Type() )
556  {
557  case PCB_SHAPE_T:
558  case PCB_FP_SHAPE_T:
559  switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
560  {
561  case SHAPE_T::SEGMENT:
562  case SHAPE_T::ARC:
563  case SHAPE_T::POLY:
564  case SHAPE_T::RECT:
565  break;
566 
567  default:
568  aCollector.Remove( item );
569  }
570 
571  break;
572 
573  case PCB_ZONE_T:
574  case PCB_FP_ZONE_T:
575  break;
576 
577  default:
578  aCollector.Remove( item );
579  }
580  }
581  } );
582 
583  if( selection.Empty() )
584  return 0;
585 
586  auto getPolySet =
587  []( EDA_ITEM* aItem )
588  {
589  SHAPE_POLY_SET set;
590 
591  switch( aItem->Type() )
592  {
593  case PCB_ZONE_T:
594  case PCB_FP_ZONE_T:
595  set = *static_cast<ZONE*>( aItem )->Outline();
596  break;
597 
598  case PCB_SHAPE_T:
599  case PCB_FP_SHAPE_T:
600  {
601  PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
602 
603  if( graphic->GetShape() == SHAPE_T::POLY )
604  {
605  set = graphic->GetPolyShape();
606  }
607  else if( graphic->GetShape() == SHAPE_T::RECT )
608  {
609  SHAPE_LINE_CHAIN outline;
610  VECTOR2I start( graphic->GetStart() );
611  VECTOR2I end( graphic->GetEnd() );
612 
613  outline.Append( start );
614  outline.Append( VECTOR2I( end.x, start.y ) );
615  outline.Append( end );
616  outline.Append( VECTOR2I( start.x, end.y ) );
617  outline.SetClosed( true );
618 
619  set.AddOutline( outline );
620  }
621  else
622  {
623  wxFAIL_MSG( wxT( "Unhandled graphic shape type in PolyToLines - getPolySet" ) );
624  }
625  break;
626  }
627 
628  default:
629  wxFAIL_MSG( wxT( "Unhandled type in PolyToLines - getPolySet" ) );
630  break;
631  }
632 
633  return set;
634  };
635 
636  auto getSegList =
637  []( SHAPE_POLY_SET& aPoly )
638  {
639  std::vector<SEG> segs;
640 
641  // Our input should be valid polys, so OK to assert here
642  wxASSERT( aPoly.VertexCount() >= 2 );
643 
644  for( int i = 1; i < aPoly.VertexCount(); i++ )
645  segs.emplace_back( SEG( aPoly.CVertex( i - 1 ), aPoly.CVertex( i ) ) );
646 
647  segs.emplace_back( SEG( aPoly.CVertex( aPoly.VertexCount() - 1 ),
648  aPoly.CVertex( 0 ) ) );
649 
650  return segs;
651  };
652 
653  BOARD_COMMIT commit( m_frame );
654  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
655  FOOTPRINT_EDIT_FRAME* fpEditor = dynamic_cast<FOOTPRINT_EDIT_FRAME*>( m_frame );
656  FOOTPRINT* footprint = nullptr;
657  PCB_LAYER_ID targetLayer = m_frame->GetActiveLayer();
658  PCB_LAYER_ID copperLayer = F_Cu;
659  BOARD_ITEM_CONTAINER* parent = frame->GetModel();
660 
661  if( fpEditor )
662  footprint = fpEditor->GetBoard()->GetFirstFootprint();
663 
664  auto handleGraphicSeg =
665  [&]( EDA_ITEM* aItem )
666  {
667  if( aItem->Type() != PCB_SHAPE_T && aItem->Type() != PCB_FP_SHAPE_T )
668  return false;
669 
670  PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
671 
672  if( graphic->GetShape() == SHAPE_T::SEGMENT )
673  {
674  PCB_TRACK* track = new PCB_TRACK( parent );
675 
676  track->SetLayer( targetLayer );
677  track->SetStart( graphic->GetStart() );
678  track->SetEnd( graphic->GetEnd() );
679  commit.Add( track );
680 
681  return true;
682  }
683  else if( graphic->GetShape() == SHAPE_T::ARC )
684  {
685  PCB_ARC* arc = new PCB_ARC( parent );
686 
687  arc->SetLayer( targetLayer );
688  arc->SetStart( graphic->GetStart() );
689  arc->SetEnd( graphic->GetEnd() );
690  arc->SetMid( graphic->GetArcMid() );
691  commit.Add( arc );
692 
693  return true;
694  }
695 
696  return false;
697  };
698 
699  if( aEvent.IsAction( &PCB_ACTIONS::convertToTracks ) )
700  {
701  if( !IsCopperLayer( targetLayer ) )
702  {
703  copperLayer = frame->SelectOneLayer( F_Cu, LSET::AllNonCuMask() );
704 
705  if( copperLayer == UNDEFINED_LAYER ) // User canceled
706  return true;
707 
708  targetLayer = copperLayer;
709  }
710  }
711 
712  for( EDA_ITEM* item : selection )
713  {
714  if( handleGraphicSeg( item ) )
715  continue;
716 
717  SHAPE_POLY_SET polySet = getPolySet( item );
718  std::vector<SEG> segs = getSegList( polySet );
719 
720  if( aEvent.IsAction( &PCB_ACTIONS::convertToLines ) )
721  {
722  for( SEG& seg : segs )
723  {
724  if( fpEditor )
725  {
726  FP_SHAPE* graphic = new FP_SHAPE( footprint, SHAPE_T::SEGMENT );
727 
728  graphic->SetLayer( targetLayer );
729  graphic->SetStart( wxPoint( seg.A ) );
730  graphic->SetStart0( wxPoint( seg.A ) );
731  graphic->SetEnd( wxPoint( seg.B ) );
732  graphic->SetEnd0( wxPoint( seg.B ) );
733  commit.Add( graphic );
734  }
735  else
736  {
737  PCB_SHAPE* graphic = new PCB_SHAPE( nullptr, SHAPE_T::SEGMENT );
738 
739  graphic->SetLayer( targetLayer );
740  graphic->SetStart( wxPoint( seg.A ) );
741  graphic->SetEnd( wxPoint( seg.B ) );
742  commit.Add( graphic );
743  }
744  }
745  }
746  else
747  {
748  // I am really unsure converting a polygon to "tracks" (i.e. segments on
749  // copper layers) make sense for footprints, but anyway this code exists
750  if( fpEditor )
751  {
752  // Creating segments on copper layer
753  for( SEG& seg : segs )
754  {
755  FP_SHAPE* graphic = new FP_SHAPE( footprint, SHAPE_T::SEGMENT );
756  graphic->SetLayer( targetLayer );
757  graphic->SetStart( wxPoint( seg.A ) );
758  graphic->SetStart0( wxPoint( seg.A ) );
759  graphic->SetEnd( wxPoint( seg.B ) );
760  graphic->SetEnd0( wxPoint( seg.B ) );
761  commit.Add( graphic );
762  }
763  }
764  else
765  {
766  // Creating tracks
767  for( SEG& seg : segs )
768  {
769  PCB_TRACK* track = new PCB_TRACK( parent );
770 
771  track->SetLayer( targetLayer );
772  track->SetStart( wxPoint( seg.A ) );
773  track->SetEnd( wxPoint( seg.B ) );
774  commit.Add( track );
775  }
776  }
777  }
778  }
779 
780  commit.Push( _( "Convert polygons to lines" ) );
781 
782  return 0;
783 }
784 
785 
787 {
788  auto& selection = m_selectionTool->RequestSelection(
789  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
790  {
791  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
792  {
793  BOARD_ITEM* item = aCollector[i];
794 
795  if( !( item->Type() == PCB_SHAPE_T ||
796  item->Type() == PCB_TRACE_T ||
797  item->Type() == PCB_FP_SHAPE_T ) )
798  {
799  aCollector.Remove( item );
800  }
801  }
802  } );
803 
804  EDA_ITEM* source = selection.Front();
805  VECTOR2I start, end, mid;
806 
807  // Offset the midpoint along the normal a little bit so that it's more obviously an arc
808  const double offsetRatio = 0.1;
809 
810  if( OPT<SEG> seg = getStartEndPoints( source, nullptr ) )
811  {
812  start = seg->A;
813  end = seg->B;
814 
815  VECTOR2I normal = ( seg->B - seg->A ).Perpendicular().Resize( offsetRatio * seg->Length() );
816  mid = seg->Center() + normal;
817  }
818  else
819  {
820  return -1;
821  }
822 
823  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
824  BOARD_ITEM_CONTAINER* parent = frame->GetModel();
825 
826  BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( source );
827 
828  // Don't continue processing if we don't actually have a board item
829  if( !boardItem )
830  return 0;
831 
832  PCB_LAYER_ID layer = boardItem->GetLayer();
833 
834  BOARD_COMMIT commit( m_frame );
835 
836  if( source->Type() == PCB_SHAPE_T || source->Type() == PCB_FP_SHAPE_T )
837  {
838  PCB_SHAPE* line = static_cast<PCB_SHAPE*>( source );
839  PCB_SHAPE* arc = new PCB_SHAPE( parent, SHAPE_T::ARC );
840 
841  VECTOR2I center = CalcArcCenter( start, mid, end );
842 
843  arc->SetFilled( false );
844  arc->SetLayer( layer );
845  arc->SetWidth( line->GetWidth() );
846 
847  arc->SetCenter( wxPoint( center ) );
848  arc->SetStart( wxPoint( start ) );
849  arc->SetEnd( wxPoint( end ) );
850 
851  commit.Add( arc );
852  }
853  else
854  {
855  wxASSERT( source->Type() == PCB_TRACE_T );
856  PCB_TRACK* line = static_cast<PCB_TRACK*>( source );
857  PCB_ARC* arc = new PCB_ARC( parent );
858 
859  arc->SetLayer( layer );
860  arc->SetWidth( line->GetWidth() );
861  arc->SetStart( wxPoint( start ) );
862  arc->SetMid( wxPoint( mid ) );
863  arc->SetEnd( wxPoint( end ) );
864 
865  commit.Add( arc );
866  }
867 
868  commit.Push( _( "Create arc from line segment" ) );
869 
870  return 0;
871 }
872 
873 
875 {
876  switch( aItem->Type() )
877  {
878  case PCB_SHAPE_T:
879  case PCB_FP_SHAPE_T:
880  {
881  PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
882 
883  if( aWidth )
884  *aWidth = shape->GetWidth();
885 
886  return boost::make_optional<SEG>( { VECTOR2I( shape->GetStart() ),
887  VECTOR2I( shape->GetEnd() ) } );
888  }
889 
890  case PCB_TRACE_T:
891  {
892  PCB_TRACK* line = static_cast<PCB_TRACK*>( aItem );
893 
894  if( aWidth )
895  *aWidth = line->GetWidth();
896 
897  return boost::make_optional<SEG>( { VECTOR2I( line->GetStart() ),
898  VECTOR2I( line->GetEnd() ) } );
899  }
900 
901  case PCB_ARC_T:
902  {
903  PCB_ARC* arc = static_cast<PCB_ARC*>( aItem );
904 
905  if( aWidth )
906  *aWidth = arc->GetWidth();
907 
908  return boost::make_optional<SEG>( { VECTOR2I( arc->GetStart() ),
909  VECTOR2I( arc->GetEnd() ) } );
910  }
911 
912  default:
913  return NULLOPT;
914  }
915 }
916 
917 
919 {
926 }
PCB_LAYER_ID SelectOneLayer(PCB_LAYER_ID aDefaultLayer, LSET aNotAllowedLayersMask=LSET(), wxPoint aDlgPosition=wxDefaultPosition)
Show the dialog box for a layer selection.
Definition: sel_layer.cpp:274
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
int GetWidth() const
Definition: eda_shape.h:98
int OutlineCount() const
Return the number of vertices in a given outline/hole.
void SetEnd0(const wxPoint &aPoint)
Definition: fp_shape.h:114
static OPT< SEG > getStartEndPoints(EDA_ITEM *aItem, int *aWidth)
Retrieve the start and end points for a generic item.
static TOOL_ACTION convertToTracks
Definition: pcb_actions.h:479
void SetFilled(bool aFlag)
Definition: eda_shape.h:92
void SetEnd(const wxPoint &aEnd)
Definition: eda_shape.h:135
const wxPoint & GetEnd() const
Definition: pcb_track.h:105
static TOOL_ACTION convertToLines
Definition: pcb_actions.h:477
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:164
This file is part of the common library.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
const wxPoint & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:106
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
Definition: action_menu.cpp:73
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...
void SetEnd(const wxPoint &aEnd)
Definition: pcb_track.h:104
COMMIT & Add(EDA_ITEM *aItem)
Notify observers that aItem has been added.
Definition: commit.h:78
SHAPE_LINE_CHAIN & Simplify(bool aRemoveColinear=true)
Simplify the line chain by removing colinear adjacent segments and duplicate vertices.
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:235
static SELECTION_CONDITION OnlyTypes(const KICAD_T aTypes[])
Create a functor that tests if the selected items are only of given types.
int InvokeNonCopperZonesEditor(PCB_BASE_FRAME *aParent, ZONE_SETTINGS *aSettings)
Function InvokeNonCopperZonesEditor invokes up a modal dialog window for non-copper zone editing.
SHAPE_POLY_SET * Outline()
Definition: zone.h:320
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
bool IsEmpty() const
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition: lset.cpp:782
static SHAPE_POLY_SET makePolysFromCircles(const std::deque< EDA_ITEM * > &aItems)
Try to make polygons from circles.
TOOL_MENU & GetToolMenu()
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
Class that groups generic conditions for selected items.
wxString m_Name
Definition: zone_settings.h:95
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
int GetWidth() const
Definition: pcb_track.h:102
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.
PCB_SELECTION_TOOL * m_selectionTool
Definition: convert_tool.h:109
VECTOR2< int > VECTOR2I
Definition: vector2d.h:622
int PointCount() const
Return the number of points (vertices) in this line chain.
static SEG::ecoord Square(int a)
Definition: seg.h:122
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:590
static SELECTION_CONDITION SameLayer()
Creates a functor that tests if selection contains items that belong exclusively to the same layer.
static SELECTION_CONDITION OnlyGraphicShapeTypes(const std::set< SHAPE_T > aTypes)
Create a functor that tests if the selection contains PCB_SHAPE* items of certain shapes.
static SHAPE_POLY_SET makePolysFromRects(const std::deque< EDA_ITEM * > &aItems)
Try to make polygons from rectangles.
int CreatePolys(const TOOL_EVENT &aEvent)
Convert selected lines to a polygon, if possible.
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).
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition: collector.h:110
search types array terminator (End Of Types)
Definition: typeinfo.h:81
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
Definition: tool_event.cpp:88
virtual ~CONVERT_TOOL()
void Insert(size_t aVertex, const VECTOR2I &aP)
void SetStart(const wxPoint &aStart)
Definition: eda_shape.h:110
virtual PCB_LAYER_ID GetActiveLayer() const
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:82
void SetWidth(int aWidth)
Set the width of all segments in the chain.
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
const wxPoint & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:131
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:318
const auto NULLOPT
Definition: optional.h:9
void ExportSetting(ZONE &aTarget, bool aFullExport=true) const
Function ExportSetting copy settings to a given zone.
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:906
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
void SetMid(const wxPoint &aMid)
Definition: pcb_track.h:271
const VECTOR2I & GetP0() const
Definition: shape_arc.h:111
const ZONE_SETTINGS & GetZoneSettings() const
Generic, UI-independent tool event.
Definition: tool_event.h:152
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, const wxPoint &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
bool IsNonCopperLayer(LAYER_NUM aLayerId)
Test whether a layer is a non copper layer.
Definition: layer_ids.h:819
static TOOL_ACTION convertToZone
Definition: pcb_actions.h:475
void SetStart(const wxPoint &aStart)
Definition: pcb_track.h:107
static TOOL_ACTION convertToKeepout
Definition: pcb_actions.h:476
#define _(s)
void SetWidth(int aWidth)
Definition: eda_shape.h:97
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
class ZONE, a copper pour area
Definition: typeinfo.h:105
void SetCenter(const wxPoint &aCenter)
Definition: eda_shape.cpp:419
void SetIsRuleArea(bool aEnable)
void SetStart0(const wxPoint &aPoint)
Definition: fp_shape.h:111
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:227
int InvokeRuleAreaEditor(PCB_BASE_FRAME *aCaller, ZONE_SETTINGS *aSettings)
Function InvokeRuleAreaEditor invokes up a modal dialog window for copper zone editing.
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:808
ZONE_SETTINGS handles zones parameters.
Definition: zone_settings.h:67
virtual BOARD_ITEM_CONTAINER * GetModel() const =0
Definition: seg.h:40
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new hole to the given outline (default: last) and returns its index.
wxPoint GetPosition() const override
Definition: pcb_shape.h:77
PCB_BASE_FRAME * m_frame
Definition: convert_tool.h:111
void SetWidth(int aWidth)
Definition: pcb_track.h:101
static TOOL_ACTION convertToArc
Definition: pcb_actions.h:478
CONDITIONAL_MENU * m_menu
Definition: convert_tool.h:110
bool Init() override
Init() is called once upon a registration of the tool.
void SetTitle(const wxString &aTitle) override
Set title for the menu.
Definition: action_menu.cpp:87
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter, bool aConfirmLockedItems=false)
Return the current selection set, filtered according to aFlags and aClientFilter.
bool IsType(FRAME_T aType) const
Common, abstract interface for edit frames.
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:240
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Definition: layer_ids.h:71
static SELECTION_CONDITION OnlyType(KICAD_T aType)
Create a functor that tests if the selected items are only of given type.
class ZONE, managed by a footprint
Definition: typeinfo.h:94
wxPoint GetArcMid() const
Definition: eda_shape.cpp:437
void process(const BOARD_CONNECTED_ITEM *item, int net)
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:100
The selection tool: currently supports:
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:99
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true) override
Revert the commit by restoring the modified items state.
boost::optional< T > OPT
Definition: optional.h:7
int InvokeCopperZonesEditor(PCB_BASE_FRAME *aCaller, ZONE_SETTINGS *aSettings)
Function InvokeCopperZonesEditor invokes up a modal dialog window for copper zone editing.
static TOOL_ACTION convertToPoly
Definition: pcb_actions.h:474
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:36
int CreateLines(const TOOL_EVENT &aEvent)
Convert selected polygon-like object to graphic lines, if possible.
SHAPE_ARC Reversed() const
Definition: shape_arc.cpp:584
static SHAPE_POLY_SET makePolysFromSegs(const std::deque< EDA_ITEM * > &aItems)
Try to make polygons from segments in the selected items.
SHAPE_T GetShape() const
Definition: eda_shape.h:101
Abstract interface for BOARD_ITEMs capable of storing other items inside.
BOARD * GetBoard() const
int GetRadius() const
Definition: eda_shape.cpp:472
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:525
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:143
static SHAPE_POLY_SET extractPolygons(const std::deque< EDA_ITEM * > &aItems)
For any polygon shapes (zones, keepouts, graphic polys) in aItems, extracts the polygon outlines into...
A specialization of ZONE for use in footprints.
Definition: zone.h:947
const wxPoint & GetStart() const
Definition: pcb_track.h:108
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:112
BOARD_ITEM_CONTAINER * GetParentFootprint() const
Definition: board_item.cpp:191
int SegmentToArc(const TOOL_EVENT &aEvent)
Convert selected segment (graphic or track) to an arc of the same type.
Container for design settings for a BOARD object.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...