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 <jon@craftyjon.com>
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_edit_frame.h>
33 #include <fp_shape.h>
35 #include <menus_helpers.h>
36 #include <pcb_edit_frame.h>
37 #include <pcb_shape.h>
38 #include <pcb_track.h>
39 #include <tool/tool_manager.h>
40 #include <tools/edit_tool.h>
41 #include <tools/pcb_actions.h>
43 #include <trigo.h>
44 #include <zone.h>
45 
46 #include "convert_tool.h"
47 
48 
50  TOOL_INTERACTIVE( "pcbnew.Convert" ),
51  m_selectionTool( nullptr ),
52  m_menu( nullptr ),
53  m_frame( nullptr )
54 {
55 }
56 
57 
59 {
60  delete m_menu;
61 }
62 
63 
66 
67 
69 {
71  m_frame = getEditFrame<PCB_BASE_FRAME>();
72 
73  // Create a context menu and make it available through selection tool
74  m_menu = new CONDITIONAL_MENU( this );
76  m_menu->SetTitle( _( "Create from Selection" ) );
77 
78  static KICAD_T convertibleTracks[] = { PCB_TRACE_T, PCB_ARC_T, EOT };
79  static KICAD_T zones[] = { PCB_ZONE_T, PCB_FP_ZONE_T, EOT };
80 
81  auto graphicLines = P_S_C::OnlyGraphicShapeTypes( { SHAPE_T::SEGMENT,
84  SHAPE_T::ARC } )
85  && P_S_C::SameLayer();
86 
87  auto trackLines = S_C::MoreThan( 1 ) && S_C::OnlyTypes( convertibleTracks )
88  && P_S_C::SameLayer();
89 
90  auto anyLines = graphicLines || trackLines;
91  auto anyPolys = S_C::OnlyTypes( zones )
93 
94  auto lineToArc = S_C::Count( 1 )
96  || S_C::OnlyType( PCB_TRACE_T ) );
97 
98  auto showConvert = anyPolys || anyLines || lineToArc;
99  auto canCreatePolyType = anyLines || anyPolys;
100 
101  m_menu->AddItem( PCB_ACTIONS::convertToPoly, canCreatePolyType );
102  m_menu->AddItem( PCB_ACTIONS::convertToZone, canCreatePolyType );
103  m_menu->AddItem( PCB_ACTIONS::convertToKeepout, canCreatePolyType );
105 
106  // Currently the code exists, but tracks are not really existing in footprints
107  // only segments on copper layers
110 
112 
114  selToolMenu.AddMenu( m_menu, showConvert, 100 );
115 
116  return true;
117 }
118 
119 
121 {
122  FOOTPRINT* parentFootprint = nullptr;
123 
124  auto& selection = m_selectionTool->RequestSelection(
125  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
126  {
127  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
128  {
129  BOARD_ITEM* item = aCollector[i];
130 
131  switch( item->Type() )
132  {
133  case PCB_SHAPE_T:
134  case PCB_FP_SHAPE_T:
135  switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
136  {
137  case SHAPE_T::SEGMENT:
138  case SHAPE_T::RECT:
139  case SHAPE_T::CIRCLE:
140  case SHAPE_T::ARC:
141  case SHAPE_T::POLY:
142  break;
143 
144  default:
145  aCollector.Remove( item );
146  }
147 
148  break;
149 
150  case PCB_TRACE_T:
151  case PCB_ARC_T:
152  break;
153 
154  case PCB_ZONE_T:
155  case PCB_FP_ZONE_T:
156  break;
157 
158  default:
159  aCollector.Remove( item );
160  }
161  }
162  } );
163 
164  if( selection.Empty() )
165  return 0;
166 
167  PCB_LAYER_ID destLayer = m_frame->GetActiveLayer();
168  SHAPE_POLY_SET polySet = makePolysFromSegs( selection.GetItems() );
169 
170  polySet.Append( makePolysFromRects( selection.GetItems() ) );
171 
172  polySet.Append( makePolysFromCircles( selection.GetItems() ) );
173 
174  polySet.Append( extractPolygons( selection.GetItems() ) );
175 
176  if( polySet.IsEmpty() )
177  return 0;
178 
179  bool isFootprint = m_frame->IsType( FRAME_FOOTPRINT_EDITOR );
180 
181  if( FP_SHAPE* graphic = dynamic_cast<FP_SHAPE*>( selection.Front() ) )
182  parentFootprint = graphic->GetParentFootprint();
183 
184  BOARD_COMMIT commit( m_frame );
185 
186  // For now, we convert each outline in the returned shape to its own polygon
187  std::vector<SHAPE_POLY_SET> polys;
188 
189  for( int i = 0; i < polySet.OutlineCount(); i++ )
190  polys.emplace_back( SHAPE_POLY_SET( polySet.COutline( i ) ) );
191 
192  if( aEvent.IsAction( &PCB_ACTIONS::convertToPoly ) )
193  {
194  for( const SHAPE_POLY_SET& poly : polys )
195  {
196  PCB_SHAPE* graphic = isFootprint ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
197 
198  graphic->SetShape( SHAPE_T::POLY );
199  graphic->SetFilled( false );
200  graphic->SetWidth( poly.Outline( 0 ).Width() );
201  graphic->SetLayer( destLayer );
202  graphic->SetPolyShape( poly );
203 
204  commit.Add( graphic );
205  }
206 
207  commit.Push( _( "Convert shapes to polygon" ) );
208  }
209  else
210  {
211  // Creating zone or keepout
212  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
213  BOARD_ITEM_CONTAINER* parent = frame->GetModel();
214  ZONE_SETTINGS zoneInfo = frame->GetZoneSettings();
215 
216  bool nonCopper = IsNonCopperLayer( destLayer );
217  zoneInfo.m_Layers.reset().set( destLayer );
218 
219  int ret;
220 
221  if( aEvent.IsAction( &PCB_ACTIONS::convertToKeepout ) )
222  ret = InvokeRuleAreaEditor( frame, &zoneInfo );
223  else if( nonCopper )
224  ret = InvokeNonCopperZonesEditor( frame, &zoneInfo );
225  else
226  ret = InvokeCopperZonesEditor( frame, &zoneInfo );
227 
228  if( ret == wxID_CANCEL )
229  return 0;
230 
231  for( const SHAPE_POLY_SET& poly : polys )
232  {
233  ZONE* zone = isFootprint ? new FP_ZONE( parent ) : new ZONE( parent );
234 
235  *zone->Outline() = poly;
236  zone->HatchBorder();
237 
238  zoneInfo.ExportSetting( *zone );
239 
240  commit.Add( zone );
241  }
242 
243  commit.Push( _( "Convert shapes to zone" ) );
244  }
245 
246  return 0;
247 }
248 
249 
250 SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque<EDA_ITEM*>& aItems )
251 {
252  // TODO: This code has a somewhat-similar purpose to ConvertOutlineToPolygon but is slightly
253  // different, so this remains a separate algorithm. It might be nice to analyze the dfiferences
254  // in requirements and refactor this.
255  const int chainingEpsilon = Millimeter2iu( 0.02 );
256 
257  SHAPE_POLY_SET poly;
258 
259  // Stores pairs of (anchor, item) where anchor == 0 -> SEG.A, anchor == 1 -> SEG.B
260  std::map<VECTOR2I, std::vector<std::pair<int, EDA_ITEM*>>> connections;
261  std::set<EDA_ITEM*> used;
262  std::deque<EDA_ITEM*> toCheck;
263 
264  auto closeEnough =
265  []( VECTOR2I aLeft, VECTOR2I aRight, unsigned aLimit )
266  {
267  return ( aLeft - aRight ).SquaredEuclideanNorm() <= SEG::Square( aLimit );
268  };
269 
270  auto findInsertionPoint =
271  [&]( VECTOR2I aPoint ) -> VECTOR2I
272  {
273  for( const auto& candidatePair : connections )
274  {
275  if( closeEnough( aPoint, candidatePair.first, chainingEpsilon ) )
276  return candidatePair.first;
277  }
278 
279  return aPoint;
280  };
281 
282  for( EDA_ITEM* item : aItems )
283  {
284  if( OPT<SEG> seg = getStartEndPoints( item, nullptr ) )
285  {
286  toCheck.push_back( item );
287  connections[findInsertionPoint( seg->A )].emplace_back( std::make_pair( 0, item ) );
288  connections[findInsertionPoint( seg->B )].emplace_back( std::make_pair( 1, item ) );
289  }
290  }
291 
292  while( !toCheck.empty() )
293  {
294  EDA_ITEM* candidate = toCheck.front();
295  toCheck.pop_front();
296 
297  if( used.count( candidate ) )
298  continue;
299 
300  int width = -1;
301  SHAPE_LINE_CHAIN outline;
302 
303  auto insert =
304  [&]( EDA_ITEM* aItem, VECTOR2I aAnchor, bool aDirection )
305  {
306  if( aItem->Type() == PCB_ARC_T ||
307  ( aItem->Type() == PCB_SHAPE_T &&
308  static_cast<PCB_SHAPE*>( aItem )->GetShape() == SHAPE_T::ARC ) )
309  {
310  SHAPE_ARC arc;
311 
312  if( aItem->Type() == PCB_ARC_T )
313  {
314  std::shared_ptr<SHAPE> es =
315  static_cast<PCB_ARC*>( aItem )->GetEffectiveShape();
316  arc = *static_cast<SHAPE_ARC*>( es.get() );
317  }
318  else
319  {
320  PCB_SHAPE* ps = static_cast<PCB_SHAPE*>( aItem );
321  arc = SHAPE_ARC( ps->GetArcStart(), ps->GetArcMid(), ps->GetArcEnd(),
322  ps->GetWidth() );
323  }
324 
325  if( aDirection )
326  outline.Append( aAnchor == arc.GetP0() ? arc : arc.Reversed() );
327  else
328  outline.Insert( 0, aAnchor == arc.GetP0() ? arc : arc.Reversed() );
329  }
330  else
331  {
332  OPT<SEG> nextSeg = getStartEndPoints( aItem, &width );
333  wxASSERT( nextSeg );
334 
335  VECTOR2I& point = ( aAnchor == nextSeg->A ) ? nextSeg->B : nextSeg->A;
336 
337  if( aDirection )
338  outline.Append( point );
339  else
340  outline.Insert( 0, point );
341  }
342  };
343 
344  // aDirection == true for walking "right" and appending to the end of points
345  // false for walking "left" and prepending to the beginning
346  std::function<void( EDA_ITEM*, VECTOR2I, bool )> process =
347  [&]( EDA_ITEM* aItem, VECTOR2I aAnchor, bool aDirection )
348  {
349  if( used.count( aItem ) )
350  return;
351 
352  used.insert( aItem );
353 
354  insert( aItem, aAnchor, aDirection );
355 
356  OPT<SEG> anchors = getStartEndPoints( aItem, &width );
357  wxASSERT( anchors );
358 
359  VECTOR2I nextAnchor = ( aAnchor == anchors->A ) ? anchors->B : anchors->A;
360 
361  for( std::pair<int, EDA_ITEM*> pair : connections[nextAnchor] )
362  {
363  if( pair.second == aItem )
364  continue;
365 
366  process( pair.second, nextAnchor, aDirection );
367  }
368  };
369 
370  OPT<SEG> anchors = getStartEndPoints( candidate, &width );
371  wxASSERT( anchors );
372 
373  // Start with the first object and walk "right"
374  // Note if the first object is an arc, we don't need to insert its first point here, the
375  // whole arc will be inserted at anchor B inside process()
376  if( !( candidate->Type() == PCB_ARC_T ||
377  ( candidate->Type() == PCB_SHAPE_T &&
378  static_cast<PCB_SHAPE*>( candidate )->GetShape() == SHAPE_T::ARC ) ) )
379  {
380  insert( candidate, anchors->A, true );
381  }
382 
383  process( candidate, anchors->B, true );
384 
385  // check for any candidates on the "left"
386  EDA_ITEM* left = nullptr;
387 
388  for( std::pair<int, EDA_ITEM*> possibleLeft : connections[anchors->A] )
389  {
390  if( possibleLeft.second != candidate )
391  {
392  left = possibleLeft.second;
393  break;
394  }
395  }
396 
397  if( left )
398  process( left, anchors->A, false );
399 
400  if( outline.PointCount() < 3 )
401  continue;
402 
403  outline.SetClosed( true );
404  outline.Simplify();
405 
406  if( width >= 0 )
407  outline.SetWidth( width );
408 
409  poly.AddOutline( outline );
410  }
411 
412  return poly;
413 }
414 
415 
416 SHAPE_POLY_SET CONVERT_TOOL::makePolysFromRects( const std::deque<EDA_ITEM*>& aItems )
417 {
418  SHAPE_POLY_SET poly;
419 
420  for( EDA_ITEM* item : aItems )
421  {
422  if( item->Type() != PCB_SHAPE_T && item->Type() != PCB_FP_SHAPE_T )
423  continue;
424 
425  PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( item );
426 
427  if( graphic->GetShape() != SHAPE_T::RECT )
428  continue;
429 
430  SHAPE_LINE_CHAIN outline;
431  VECTOR2I start( graphic->GetStart() );
432  VECTOR2I end( graphic->GetEnd() );
433 
434  outline.Append( start );
435  outline.Append( VECTOR2I( end.x, start.y ) );
436  outline.Append( end );
437  outline.Append( VECTOR2I( start.x, end.y ) );
438  outline.SetClosed( true );
439 
440  outline.SetWidth( graphic->GetWidth() );
441 
442  poly.AddOutline( outline );
443  }
444 
445  return poly;
446 }
447 
448 
449 SHAPE_POLY_SET CONVERT_TOOL::makePolysFromCircles( const std::deque<EDA_ITEM*>& aItems )
450 {
451  SHAPE_POLY_SET poly;
452 
453  for( EDA_ITEM* item : aItems )
454  {
455  if( item->Type() != PCB_SHAPE_T && item->Type() != PCB_FP_SHAPE_T )
456  continue;
457 
458  PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( item );
459 
460  if( graphic->GetShape() != SHAPE_T::CIRCLE )
461  continue;
462 
463  BOARD_DESIGN_SETTINGS& bds = graphic->GetBoard()->GetDesignSettings();
464  SHAPE_LINE_CHAIN outline;
465 
466  TransformCircleToPolygon( outline, graphic->GetPosition(), graphic->GetRadius(),
467  bds.m_MaxError, ERROR_OUTSIDE );
468 
469  poly.AddOutline( outline );
470  }
471 
472  return poly;
473 }
474 
475 
476 SHAPE_POLY_SET CONVERT_TOOL::extractPolygons( const std::deque<EDA_ITEM*>& aItems )
477 {
478  SHAPE_POLY_SET poly;
479 
480  for( EDA_ITEM* item : aItems )
481  {
482  switch( item->Type() )
483  {
484  case PCB_SHAPE_T:
485  switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
486  {
487  case SHAPE_T::POLY:
488  poly.Append( static_cast<PCB_SHAPE*>( item )->GetPolyShape() );
489  break;
490 
491  default:
492  continue;
493  }
494 
495  break;
496 
497  case PCB_ZONE_T:
498  case PCB_FP_ZONE_T:
499  poly.Append( *static_cast<ZONE*>( item )->Outline() );
500  break;
501 
502  default:
503  continue;
504  }
505  }
506 
507  return poly;
508 }
509 
510 
512 {
513  auto& selection = m_selectionTool->RequestSelection(
514  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
515  {
516  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
517  {
518  BOARD_ITEM* item = aCollector[i];
519 
520  switch( item->Type() )
521  {
522  case PCB_SHAPE_T:
523  case PCB_FP_SHAPE_T:
524  switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
525  {
526  case SHAPE_T::POLY:
527  break;
528 
529  case SHAPE_T::RECT:
530  break;
531 
532  default:
533  aCollector.Remove( item );
534  }
535 
536  break;
537 
538  case PCB_ZONE_T:
539  case PCB_FP_ZONE_T:
540  break;
541 
542  default:
543  aCollector.Remove( item );
544  }
545  }
546  } );
547 
548  if( selection.Empty() )
549  return 0;
550 
551  auto getPolySet =
552  []( EDA_ITEM* aItem )
553  {
554  SHAPE_POLY_SET set;
555 
556  switch( aItem->Type() )
557  {
558  case PCB_ZONE_T:
559  case PCB_FP_ZONE_T:
560  set = *static_cast<ZONE*>( aItem )->Outline();
561  break;
562 
563  case PCB_SHAPE_T:
564  case PCB_FP_SHAPE_T:
565  {
566  PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
567 
568  if( graphic->GetShape() == SHAPE_T::POLY )
569  {
570  set = graphic->GetPolyShape();
571  }
572  else if( graphic->GetShape() == SHAPE_T::RECT )
573  {
574  SHAPE_LINE_CHAIN outline;
575  VECTOR2I start( graphic->GetStart() );
576  VECTOR2I end( graphic->GetEnd() );
577 
578  outline.Append( start );
579  outline.Append( VECTOR2I( end.x, start.y ) );
580  outline.Append( end );
581  outline.Append( VECTOR2I( start.x, end.y ) );
582  outline.SetClosed( true );
583 
584  set.AddOutline( outline );
585  }
586  else
587  {
588  wxFAIL_MSG( "Unhandled graphic shape type in PolyToLines - getPolySet" );
589  }
590  break;
591  }
592 
593  default:
594  wxFAIL_MSG( "Unhandled type in PolyToLines - getPolySet" );
595  break;
596  }
597 
598  return set;
599  };
600 
601  auto getSegList =
602  []( SHAPE_POLY_SET& aPoly )
603  {
604  std::vector<SEG> segs;
605 
606  // Our input should be valid polys, so OK to assert here
607  wxASSERT( aPoly.VertexCount() >= 2 );
608 
609  for( int i = 1; i < aPoly.VertexCount(); i++ )
610  segs.emplace_back( SEG( aPoly.CVertex( i - 1 ), aPoly.CVertex( i ) ) );
611 
612  segs.emplace_back( SEG( aPoly.CVertex( aPoly.VertexCount() - 1 ),
613  aPoly.CVertex( 0 ) ) );
614 
615  return segs;
616  };
617 
618  BOARD_COMMIT commit( m_frame );
619  FOOTPRINT_EDIT_FRAME* fpEditor = dynamic_cast<FOOTPRINT_EDIT_FRAME*>( m_frame );
620  FOOTPRINT* footprint = nullptr;
621  PCB_LAYER_ID targetLayer = m_frame->GetActiveLayer();
622  PCB_LAYER_ID copperLayer = UNSELECTED_LAYER;
623 
624  if( fpEditor )
625  footprint = fpEditor->GetBoard()->GetFirstFootprint();
626 
627  for( EDA_ITEM* item : selection )
628  {
629  SHAPE_POLY_SET polySet = getPolySet( item );
630  std::vector<SEG> segs = getSegList( polySet );
631 
632  if( aEvent.IsAction( &PCB_ACTIONS::convertToLines ) )
633  {
634  for( SEG& seg : segs )
635  {
636  if( fpEditor )
637  {
638  FP_SHAPE* graphic = new FP_SHAPE( footprint, SHAPE_T::SEGMENT );
639 
640  graphic->SetLayer( targetLayer );
641  graphic->SetStart( wxPoint( seg.A ) );
642  graphic->SetStart0( wxPoint( seg.A ) );
643  graphic->SetEnd( wxPoint( seg.B ) );
644  graphic->SetEnd0( wxPoint( seg.B ) );
645  commit.Add( graphic );
646  }
647  else
648  {
649  PCB_SHAPE* graphic = new PCB_SHAPE;
650 
651  graphic->SetShape( SHAPE_T::SEGMENT );
652  graphic->SetLayer( targetLayer );
653  graphic->SetStart( wxPoint( seg.A ) );
654  graphic->SetEnd( wxPoint( seg.B ) );
655  commit.Add( graphic );
656  }
657  }
658  }
659  else
660  {
661  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
662  BOARD_ITEM_CONTAINER* parent = frame->GetModel();
663 
664  if( !IsCopperLayer( targetLayer ) )
665  {
666  if( copperLayer == UNSELECTED_LAYER )
667  copperLayer = frame->SelectOneLayer( F_Cu, LSET::AllNonCuMask() );
668 
669  if( copperLayer == UNDEFINED_LAYER ) // User canceled
670  continue;
671 
672  targetLayer = copperLayer;
673  }
674 
675  // I am really unsure converting a polygon to "tracks" (i.e. segments on
676  // copper layers) make sense for footprints, but anyway this code exists
677  if( fpEditor )
678  {
679  // Creating segments on copper layer
680  for( SEG& seg : segs )
681  {
682  FP_SHAPE* graphic = new FP_SHAPE( footprint, SHAPE_T::SEGMENT );
683  graphic->SetLayer( targetLayer );
684  graphic->SetStart( wxPoint( seg.A ) );
685  graphic->SetStart0( wxPoint( seg.A ) );
686  graphic->SetEnd( wxPoint( seg.B ) );
687  graphic->SetEnd0( wxPoint( seg.B ) );
688  commit.Add( graphic );
689  }
690  }
691  else
692  {
693  // Creating tracks
694  for( SEG& seg : segs )
695  {
696  PCB_TRACK* track = new PCB_TRACK( parent );
697 
698  track->SetLayer( targetLayer );
699  track->SetStart( wxPoint( seg.A ) );
700  track->SetEnd( wxPoint( seg.B ) );
701  commit.Add( track );
702  }
703  }
704  }
705  }
706 
707  commit.Push( _( "Convert polygons to lines" ) );
708 
709  return 0;
710 }
711 
712 
714 {
715  auto& selection = m_selectionTool->RequestSelection(
716  []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
717  {
718  for( int i = aCollector.GetCount() - 1; i >= 0; --i )
719  {
720  BOARD_ITEM* item = aCollector[i];
721 
722  if( !( item->Type() == PCB_SHAPE_T ||
723  item->Type() == PCB_TRACE_T ||
724  item->Type() == PCB_FP_SHAPE_T ) )
725  {
726  aCollector.Remove( item );
727  }
728  }
729  } );
730 
731  EDA_ITEM* source = selection.Front();
732  VECTOR2I start, end, mid;
733 
734  // Offset the midpoint along the normal a little bit so that it's more obviously an arc
735  const double offsetRatio = 0.1;
736 
737  if( OPT<SEG> seg = getStartEndPoints( source, nullptr ) )
738  {
739  start = seg->A;
740  end = seg->B;
741 
742  VECTOR2I normal = ( seg->B - seg->A ).Perpendicular().Resize( offsetRatio * seg->Length() );
743  mid = seg->Center() + normal;
744  }
745  else
746  {
747  return -1;
748  }
749 
750  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
751  BOARD_ITEM_CONTAINER* parent = frame->GetModel();
752 
753  BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( source );
754 
755  // Don't continue processing if we don't actually have a board item
756  if( !boardItem )
757  return 0;
758 
759  PCB_LAYER_ID layer = boardItem->GetLayer();
760 
761  BOARD_COMMIT commit( m_frame );
762 
763  if( source->Type() == PCB_SHAPE_T || source->Type() == PCB_FP_SHAPE_T )
764  {
765  PCB_SHAPE* line = static_cast<PCB_SHAPE*>( source );
766  PCB_SHAPE* arc = new PCB_SHAPE( parent );
767 
768  VECTOR2I center = GetArcCenter( start, mid, end );
769 
770  arc->SetShape( SHAPE_T::ARC );
771  arc->SetFilled( false );
772  arc->SetLayer( layer );
773  arc->SetWidth( line->GetWidth() );
774 
775  arc->SetCenter( wxPoint( center ) );
776  arc->SetArcStart( wxPoint( start ) );
777  arc->SetAngle( GetArcAngle( start, mid, end ) );
778 
779  arc->SetArcEnd( wxPoint( end ) );
780  commit.Add( arc );
781  }
782  else
783  {
784  wxASSERT( source->Type() == PCB_TRACE_T );
785  PCB_TRACK* line = static_cast<PCB_TRACK*>( source );
786  PCB_ARC* arc = new PCB_ARC( parent );
787 
788  arc->SetLayer( layer );
789  arc->SetWidth( line->GetWidth() );
790  arc->SetStart( wxPoint( start ) );
791  arc->SetMid( wxPoint( mid ) );
792  arc->SetEnd( wxPoint( end ) );
793 
794  commit.Add( arc );
795  }
796 
797  commit.Push( _( "Create arc from line segment" ) );
798 
799  return 0;
800 }
801 
802 
804 {
805  switch( aItem->Type() )
806  {
807  case PCB_SHAPE_T:
808  case PCB_FP_SHAPE_T:
809  {
810  PCB_SHAPE* line = static_cast<PCB_SHAPE*>( aItem );
811 
812  if( aWidth )
813  *aWidth = line->GetWidth();
814 
815  if( line->GetShape() == SHAPE_T::SEGMENT )
816  {
817  return boost::make_optional<SEG>( { VECTOR2I( line->GetStart() ),
818  VECTOR2I( line->GetEnd() ) } );
819  }
820  else
821  {
822  return boost::make_optional<SEG>( { VECTOR2I( line->GetArcStart() ),
823  VECTOR2I( line->GetArcEnd() ) } );
824  }
825  }
826 
827  case PCB_TRACE_T:
828  {
829  PCB_TRACK* line = static_cast<PCB_TRACK*>( aItem );
830 
831  if( aWidth )
832  *aWidth = line->GetWidth();
833 
834  return boost::make_optional<SEG>( { VECTOR2I( line->GetStart() ),
835  VECTOR2I( line->GetEnd() ) } );
836  }
837 
838  case PCB_ARC_T:
839  {
840  PCB_ARC* arc = static_cast<PCB_ARC*>( aItem );
841 
842  if( aWidth )
843  *aWidth = arc->GetWidth();
844 
845  return boost::make_optional<SEG>( { VECTOR2I( arc->GetStart() ),
846  VECTOR2I( arc->GetEnd() ) } );
847  }
848 
849  default:
850  return NULLOPT;
851  }
852 }
853 
854 
856 {
863 }
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
wxPoint GetArcEnd() const
Definition: pcb_shape.cpp:417
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
Arcs (with rounded ends)
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.
double GetArcAngle(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Return the subtended angle for a given arc.
Definition: trigo.cpp:496
static TOOL_ACTION convertToTracks
Definition: pcb_actions.h:477
void SetShape(SHAPE_T aShape)
Definition: pcb_shape.h:109
polygon (not yet used for tracks, but could be in microwave apps)
SHAPE_POLY_SET & GetPolyShape()
Definition: pcb_shape.h:240
const wxPoint & GetEnd() const
Definition: pcb_track.h:105
static TOOL_ACTION convertToLines
Definition: pcb_actions.h:475
const wxPoint & GetEnd() const
Return the ending point of the graphic.
Definition: pcb_shape.h:134
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:192
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:80
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
Definition: action_menu.cpp:73
SHAPE_T GetShape() const
Definition: pcb_shape.h:110
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.
static SELECTION_CONDITION OnlyTypes(const KICAD_T aTypes[])
Create a functor that tests if the selected items are only of given types.
void SetFilled(bool aFlag)
Definition: pcb_shape.h:73
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
int GetRadius() const
Return the radius of this item.
Definition: pcb_shape.cpp:487
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
bool IsEmpty() const
int GetWidth() const
Definition: pcb_shape.h:97
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition: lset.cpp:773
usual segment : line with rounded ends
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.
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:623
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:589
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:115
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
wxPoint GetArcStart() const
Definition: pcb_shape.h:156
virtual ~CONVERT_TOOL()
void Insert(size_t aVertex, const VECTOR2I &aP)
virtual PCB_LAYER_ID GetActiveLayer() const
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:87
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.
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:317
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:927
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
void SetMid(const wxPoint &aMid)
Definition: pcb_track.h:272
const VECTOR2I & GetP0() const
Definition: shape_arc.h:111
const ZONE_SETTINGS & GetZoneSettings() const
Generic, UI-independent tool event.
Definition: tool_event.h:152
const wxPoint & GetStart() const
Return the starting point of the graphic.
Definition: pcb_shape.h:124
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:798
static TOOL_ACTION convertToZone
Definition: pcb_actions.h:473
void SetStart(const wxPoint &aStart)
Definition: pcb_track.h:107
static TOOL_ACTION convertToKeepout
Definition: pcb_actions.h:474
#define _(s)
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 &aCenterPoint)
Definition: pcb_shape.h:198
void SetStart0(const wxPoint &aPoint)
Definition: fp_shape.h:111
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:787
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.cpp:70
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:476
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 SetStart(const wxPoint &aStart)
Definition: pcb_shape.h:127
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
Represent a polyline (an zero-thickness chain of connected line segments).
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:64
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:70
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
void process(const BOARD_CONNECTED_ITEM *item, int net)
void SetWidth(int aWidth)
Definition: pcb_shape.h:96
The selection tool: currently supports:
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:100
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.
void SetArcEnd(const wxPoint &aArcEndPoint)
Initialize the end arc point.
Definition: pcb_shape.h:193
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:472
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:50
int CreateLines(const TOOL_EVENT &aEvent)
Convert selected polygon-like object to graphic lines, if possible.
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: pcb_shape.h:248
SHAPE_ARC Reversed() const
Definition: shape_arc.cpp:624
wxPoint GetArcMid() const
Definition: pcb_shape.cpp:435
static SHAPE_POLY_SET makePolysFromSegs(const std::deque< EDA_ITEM * > &aItems)
Try to make polygons from segments in the selected items.
Abstract interface for BOARD_ITEMs capable of storing other items inside.
BOARD * GetBoard() const
const VECTOR2I GetArcCenter(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:454
segment with non rounded ends
virtual void SetAngle(double aAngle, bool aUpdateEnd=true)
Set the angle for arcs, and normalizes it within the range 0 - 360 degrees.
Definition: pcb_shape.cpp:519
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
static constexpr int Millimeter2iu(double mm)
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:171
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:946
const wxPoint & GetStart() const
Definition: pcb_track.h:108
void SetEnd(const wxPoint &aEnd)
Definition: pcb_shape.h:137
void SetArcStart(const wxPoint &aArcStartPoint)
Initialize the start arc point.
Definition: pcb_shape.h:183
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
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...