KiCad PCB EDA Suite
point_editor.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) 2013-2017 CERN
5  * @author Maciej Suminski <maciej.suminski@cern.ch>
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 <functional>
26 using namespace std::placeholders;
27 
28 #include <tool/tool_manager.h>
29 #include <view/view_controls.h>
31 #include <geometry/seg.h>
32 #include <confirm.h>
33 
34 #include "pcb_actions.h"
35 #include "selection_tool.h"
36 #include "point_editor.h"
37 #include <board_commit.h>
38 #include <bitmaps.h>
39 
40 #include <pcb_edit_frame.h>
41 #include <class_edge_mod.h>
42 #include <class_dimension.h>
43 #include <class_zone.h>
44 #include <class_board.h>
45 #include <class_module.h>
46 #include <connectivity_data.h>
47 
48 #include "zone_filler.h"
49 
50 // Point editor
51 TOOL_ACTION PCB_ACTIONS::pointEditorAddCorner( "pcbnew.PointEditor.addCorner",
52  AS_GLOBAL, 0,
53  _( "Create Corner" ), _( "Create a corner" ), add_corner_xpm );
54 
55 TOOL_ACTION PCB_ACTIONS::pointEditorRemoveCorner( "pcbnew.PointEditor.removeCorner",
56  AS_GLOBAL, 0,
57  _( "Remove Corner" ), _( "Remove corner" ), delete_xpm );
58 
59 
60 // Few constants to avoid using bare numbers for point indices
62 {
64 };
65 
67 {
69 };
70 
72 {
74 };
75 
77 {
82 };
83 
85 {
86 private:
87 
88  static void buildForPolyOutline( std::shared_ptr<EDIT_POINTS> points, const SHAPE_POLY_SET* aOutline, KIGFX::GAL* aGal )
89  {
90 
91  int cornersCount = aOutline->TotalVertices();
92 
93  for( auto iterator = aOutline->CIterateWithHoles(); iterator; iterator++ )
94  {
95  points->AddPoint( *iterator );
96 
97  if( iterator.IsEndContour() )
98  points->AddBreak();
99  }
100 
101  // Lines have to be added after creating edit points,
102  // as they use EDIT_POINT references
103  for( int i = 0; i < cornersCount - 1; ++i )
104  {
105  if( points->IsContourEnd( i ) )
106  {
107  points->AddLine( points->Point( i ),
108  points->Point( points->GetContourStartIdx( i ) ) );
109  }
110  else
111  {
112  points->AddLine( points->Point( i ), points->Point( i + 1 ) );
113  }
114 
115  points->Line( i ).SetConstraint( new EC_SNAPLINE( points->Line( i ),
116  std::bind( &KIGFX::GAL::GetGridPoint, aGal, _1 ) ) );
117  }
118 
119  // The last missing line, connecting the last and the first polygon point
120  points->AddLine( points->Point( cornersCount - 1 ),
121  points->Point( points->GetContourStartIdx( cornersCount - 1 ) ) );
122 
123  points->Line( points->LinesSize() - 1 ).SetConstraint(
124  new EC_SNAPLINE( points->Line( points->LinesSize() - 1 ),
125  std::bind( &KIGFX::GAL::GetGridPoint, aGal, _1 ) ) );
126  }
127 
128 public:
129  static std::shared_ptr<EDIT_POINTS> Make( EDA_ITEM* aItem, KIGFX::GAL* aGal )
130  {
131  std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
132 
133  if( !aItem )
134  return points;
135 
136  // Generate list of edit points basing on the item type
137  switch( aItem->Type() )
138  {
139  case PCB_LINE_T:
140  case PCB_MODULE_EDGE_T:
141  {
142  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( aItem );
143 
144  switch( segment->GetShape() )
145  {
146  case S_SEGMENT:
147  points->AddPoint( segment->GetStart() );
148  points->AddPoint( segment->GetEnd() );
149  break;
150 
151  case S_ARC:
152  points->AddPoint( segment->GetCenter() );
153  points->AddPoint( segment->GetArcStart() );
154  points->AddPoint( segment->GetArcEnd() );
155 
156  // Set constraints
157  // Arc end has to stay at the same radius as the start
158  points->Point( ARC_END ).SetConstraint( new EC_CIRCLE( points->Point( ARC_END ),
159  points->Point( ARC_CENTER ),
160  points->Point( ARC_START ) ) );
161  break;
162 
163  case S_CIRCLE:
164  points->AddPoint( segment->GetCenter() );
165  points->AddPoint( segment->GetEnd() );
166  break;
167 
168  case S_POLYGON:
169  {
170  buildForPolyOutline( points, &segment->GetPolyShape(), aGal );
171  break;
172  }
173 
174  default: // suppress warnings
175  break;
176  }
177 
178  break;
179  }
180 
181  case PCB_ZONE_AREA_T:
182  {
183  auto zone = static_cast<const ZONE_CONTAINER*>( aItem );
184  buildForPolyOutline( points, zone->Outline(), aGal );
185  break;
186  }
187 
188  case PCB_DIMENSION_T:
189  {
190  const DIMENSION* dimension = static_cast<const DIMENSION*>( aItem );
191 
192  points->AddPoint( dimension->m_crossBarO );
193  points->AddPoint( dimension->m_crossBarF );
194  points->AddPoint( dimension->m_featureLineGO );
195  points->AddPoint( dimension->m_featureLineDO );
196 
197  // Dimension height setting - edit points should move only along the feature lines
198  points->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARO ),
199  points->Point( DIM_FEATUREGO ) ) );
200  points->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARF ),
201  points->Point( DIM_FEATUREDO ) ) );
202 
203  break;
204  }
205 
206  default:
207  points.reset();
208  break;
209  }
210 
211  return points;
212  }
213 
214 private:
216 };
217 
218 
220  PCB_TOOL( "pcbnew.PointEditor" ), m_selectionTool( NULL ), m_editedPoint( NULL ),
221  m_original( VECTOR2I( 0, 0 ) ), m_altConstrainer( VECTOR2I( 0, 0 ) )
222 {
223 }
224 
225 
227 {
228  m_editPoints.reset();
229  m_altConstraint.reset();
230  getViewControls()->SetAutoPan( false );
231 }
232 
233 
235 {
236  // Find the selection tool, so they can cooperate
237  m_selectionTool = static_cast<SELECTION_TOOL*>( m_toolMgr->FindTool( "pcbnew.InteractiveSelection" ) );
238 
239  if( !m_selectionTool )
240  {
241  DisplayError( NULL, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
242  return false;
243  }
244 
245  auto& menu = m_selectionTool->GetToolMenu().GetMenu();
248  std::bind( &POINT_EDITOR::removeCornerCondition, this, _1 ) );
249 
250  return true;
251 }
252 
253 
255 {
256  EDIT_POINT* point = m_editedPoint;
257 
258  if( aEvent.IsMotion() )
259  {
260  point = m_editPoints->FindPoint( aEvent.Position(), getView() );
261  }
262  else if( aEvent.IsDrag( BUT_LEFT ) )
263  {
264  point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
265  }
266 
267  if( m_editedPoint != point )
268  setEditedPoint( point );
269 }
270 
271 
273 {
275 
276  if( selection.Size() != 1 )
277  return 0;
278 
279  Activate();
280 
282  KIGFX::VIEW* view = getView();
283  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
284  auto item = selection.Front();
285 
286  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
287 
288  if( !m_editPoints )
289  return 0;
290 
291  view->Add( m_editPoints.get() );
292  setEditedPoint( nullptr );
293  bool modified = false;
294  bool revert = false;
295 
296  BOARD_COMMIT commit( editFrame );
297 
298  // Main loop: keep receiving events
299  while( OPT_TOOL_EVENT evt = Wait() )
300  {
301  if( revert )
302  break;
303 
304  if( !m_editPoints ||
305  evt->Matches( m_selectionTool->ClearedEvent ) ||
306  evt->Matches( m_selectionTool->UnselectedEvent ) ||
307  evt->Matches( m_selectionTool->SelectedEvent ) )
308  {
309  break;
310  }
311 
312  if ( !modified )
313  updateEditedPoint( *evt );
314 
315  if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
316  {
317  if( !modified )
318  {
319  commit.StageItems( selection, CHT_MODIFY );
320 
321  controls->ForceCursorPosition( false );
322  m_original = *m_editedPoint; // Save the original position
323  controls->SetAutoPan( true );
324  modified = true;
325  }
326 
327  bool enableAltConstraint = !!evt->Modifier( MD_CTRL );
328 
329  if( enableAltConstraint != (bool) m_altConstraint ) // alternative constraint
330  setAltConstraint( enableAltConstraint );
331 
333 
334  if( m_altConstraint )
335  m_altConstraint->Apply();
336  else
338 
339  updateItem();
340  updatePoints();
341  }
342 
343  else if( evt->IsMouseUp( BUT_LEFT ) )
344  {
345  controls->SetAutoPan( false );
346  setAltConstraint( false );
347 
348  if( modified )
349  {
350  commit.Push( _( "Drag a line ending" ) );
351  modified = false;
352  }
353 
354  m_toolMgr->PassEvent();
355  }
356 
357  else if( evt->IsCancel() )
358  {
359  if( modified ) // Restore the last change
360  revert = true;
361 
362  // Let the selection tool receive the event too
363  m_toolMgr->PassEvent();
364 
365  // Do not exit right now, let the selection clear the selection
366  //break;
367  }
368 
369  else
370  {
371  m_toolMgr->PassEvent();
372  }
373  }
374 
375  if( m_editPoints )
376  {
377  view->Remove( m_editPoints.get() );
378 
379  if( modified && revert )
380  commit.Revert();
381  else
382  finishItem();
383 
384  m_editPoints.reset();
385  }
386 
387  return 0;
388 }
389 
390 
392 {
393  EDA_ITEM* item = m_editPoints->GetParent();
394 
395  if( !item )
396  return;
397 
398  switch( item->Type() )
399  {
400  case PCB_LINE_T:
401  case PCB_MODULE_EDGE_T:
402  {
403  DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item );
404  switch( segment->GetShape() )
405  {
406  case S_SEGMENT:
407  if( isModified( m_editPoints->Point( SEG_START ) ) )
408  segment->SetStart( wxPoint( m_editPoints->Point( SEG_START ).GetPosition().x,
409  m_editPoints->Point( SEG_START ).GetPosition().y ) );
410 
411  else if( isModified( m_editPoints->Point( SEG_END ) ) )
412  segment->SetEnd( wxPoint( m_editPoints->Point( SEG_END ).GetPosition().x,
413  m_editPoints->Point( SEG_END ).GetPosition().y ) );
414 
415  break;
416 
417  case S_ARC:
418  {
419  const VECTOR2I& center = m_editPoints->Point( ARC_CENTER ).GetPosition();
420  const VECTOR2I& start = m_editPoints->Point( ARC_START ).GetPosition();
421  const VECTOR2I& end = m_editPoints->Point( ARC_END ).GetPosition();
422 
423  if( center != segment->GetCenter() )
424  {
425  wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
426  segment->Move( moveVector );
427 
428  m_editPoints->Point( ARC_START ).SetPosition( segment->GetArcStart() );
429  m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
430  }
431 
432  else
433  {
434  segment->SetArcStart( wxPoint( start.x, start.y ) );
435 
436  VECTOR2D startLine = start - center;
437  VECTOR2I endLine = end - center;
438  double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
439 
440  // Adjust the new angle to (counter)clockwise setting
441  bool clockwise = ( segment->GetAngle() > 0 );
442 
443  if( clockwise && newAngle < 0.0 )
444  newAngle += 3600.0;
445  else if( !clockwise && newAngle > 0.0 )
446  newAngle -= 3600.0;
447 
448  segment->SetAngle( newAngle );
449  }
450 
451  break;
452  }
453 
454  case S_CIRCLE:
455  {
456  const VECTOR2I& center = m_editPoints->Point( CIRC_CENTER ).GetPosition();
457  const VECTOR2I& end = m_editPoints->Point( CIRC_END ).GetPosition();
458 
459  if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
460  {
461  wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
462  segment->Move( moveVector );
463  }
464  else
465  {
466  segment->SetEnd( wxPoint( end.x, end.y ) );
467  }
468 
469  break;
470  }
471 
472  case S_POLYGON:
473  {
474  SHAPE_POLY_SET* outline = &segment->GetPolyShape();
475 
476  for( int i = 0; i < outline->TotalVertices(); ++i )
477  {
478  VECTOR2I point = m_editPoints->Point( i ).GetPosition();
479  outline->Vertex( i ) = point;
480  }
481  }
482 
483  default: // suppress warnings
484  break;
485  }
486 
487  // Update relative coordinates for module edges
488  if( EDGE_MODULE* edge = dyn_cast<EDGE_MODULE*>( item ) )
489  edge->SetLocalCoord();
490 
491  break;
492  }
493 
494  case PCB_ZONE_AREA_T:
495  {
496  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
497  zone->ClearFilledPolysList();
498  SHAPE_POLY_SET* outline = zone->Outline();
499 
500  for( int i = 0; i < outline->TotalVertices(); ++i )
501  {
502  VECTOR2I point = m_editPoints->Point( i ).GetPosition();
503  outline->Vertex( i ) = point;
504  }
505 
506  zone->Hatch();
507 
508  break;
509  }
510 
511  case PCB_DIMENSION_T:
512  {
513  DIMENSION* dimension = static_cast<DIMENSION*>( item );
514 
515  // Check which point is currently modified and updated dimension's points respectively
516  if( isModified( m_editPoints->Point( DIM_CROSSBARO ) ) )
517  {
518  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetOrigin() );
519  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() );
520 
521  if( featureLine.Cross( crossBar ) > 0 )
522  dimension->SetHeight( -featureLine.EuclideanNorm() );
523  else
524  dimension->SetHeight( featureLine.EuclideanNorm() );
525  }
526 
527  else if( isModified( m_editPoints->Point( DIM_CROSSBARF ) ) )
528  {
529  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
530  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() );
531 
532  if( featureLine.Cross( crossBar ) > 0 )
533  dimension->SetHeight( -featureLine.EuclideanNorm() );
534  else
535  dimension->SetHeight( featureLine.EuclideanNorm() );
536  }
537 
538  else if( isModified( m_editPoints->Point( DIM_FEATUREGO ) ) )
539  {
541  m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ),
542  m_editPoints->Point( DIM_FEATUREGO ) ) );
543  m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ),
544  m_editPoints->Point( DIM_FEATUREDO ) ) );
545  }
546 
547  else if( isModified( m_editPoints->Point( DIM_FEATUREDO ) ) )
548  {
550  m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ),
551  m_editPoints->Point( DIM_FEATUREGO ) ) );
552  m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ),
553  m_editPoints->Point( DIM_FEATUREDO ) ) );
554  }
555 
556  break;
557  }
558 
559  default:
560  break;
561  }
562 }
563 
564 
566 {
567  auto item = m_editPoints->GetParent();
568  if( !item )
569  return;
570 
571  if( item->Type() == PCB_ZONE_AREA_T )
572  {
573  auto zone = static_cast<ZONE_CONTAINER*>( item );
574 
575  if( zone->IsFilled() )
576  {
577  ZONE_FILLER filler( board() );
578  filler.Fill( { zone } );
579  }
580  }
581 }
582 
583 
585 {
586  if( !m_editPoints )
587  return;
588 
589  EDA_ITEM* item = m_editPoints->GetParent();
590 
591  if( !item )
592  return;
593 
594  switch( item->Type() )
595  {
596  case PCB_LINE_T:
597  case PCB_MODULE_EDGE_T:
598  {
599  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( item );
600 
601  switch( segment->GetShape() )
602  {
603  case S_SEGMENT:
604  m_editPoints->Point( SEG_START ).SetPosition( segment->GetStart() );
605  m_editPoints->Point( SEG_END ).SetPosition( segment->GetEnd() );
606  break;
607 
608  case S_ARC:
609  m_editPoints->Point( ARC_CENTER ).SetPosition( segment->GetCenter() );
610  m_editPoints->Point( ARC_START).SetPosition( segment->GetArcStart() );
611  m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
612  break;
613 
614  case S_CIRCLE:
615  m_editPoints->Point( CIRC_CENTER ).SetPosition( segment->GetCenter() );
616  m_editPoints->Point( CIRC_END ).SetPosition( segment->GetEnd() );
617  break;
618 
619  case S_POLYGON:
620  {
621  const auto& points = segment->BuildPolyPointsList();
622  for( unsigned i = 0; i < points.size(); i++ )
623  {
624  m_editPoints->Point( i ).SetPosition( points[i] );
625  }
626  break;
627  }
628 
629  default: // suppress warnings
630  break;
631  }
632 
633  break;
634  }
635 
636  case PCB_ZONE_AREA_T:
637  {
638  const ZONE_CONTAINER* zone = static_cast<const ZONE_CONTAINER*>( item );
639  const SHAPE_POLY_SET* outline = zone->Outline();
640 
641  if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
642  {
643  getView()->Remove( m_editPoints.get() );
644  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
645  getView()->Add( m_editPoints.get() );
646  }
647  else
648  {
649  for( int i = 0; i < outline->TotalVertices(); ++i )
650  m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
651  }
652 
653  break;
654  }
655 
656  case PCB_DIMENSION_T:
657  {
658  const DIMENSION* dimension = static_cast<const DIMENSION*>( item );
659 
660  m_editPoints->Point( DIM_CROSSBARO ).SetPosition( dimension->m_crossBarO );
661  m_editPoints->Point( DIM_CROSSBARF ).SetPosition( dimension->m_crossBarF );
662  m_editPoints->Point( DIM_FEATUREGO ).SetPosition( dimension->m_featureLineGO );
663  m_editPoints->Point( DIM_FEATUREDO ).SetPosition( dimension->m_featureLineDO );
664  break;
665  }
666 
667  default:
668  break;
669  }
670 
671  getView()->Update( m_editPoints.get() );
672 }
673 
674 
676 {
678 
679  if( aPoint )
680  {
681  controls->ForceCursorPosition( true, aPoint->GetPosition() );
682  controls->ShowCursor( true );
683  }
684  else
685  {
686  controls->ShowCursor( false );
687  controls->ForceCursorPosition( false );
688  }
689 
690  m_editedPoint = aPoint;
691 }
692 
693 
694 void POINT_EDITOR::setAltConstraint( bool aEnabled )
695 {
696  if( aEnabled )
697  {
698  EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
699 
700  if( line )
701  {
702  if( m_editPoints->GetParent()->Type() == PCB_ZONE_AREA_T )
704  }
705  else
706  {
707  // Find a proper constraining point for 45 degrees mode
710  }
711  }
712  else
713  {
714  m_altConstraint.reset();
715  }
716 }
717 
718 
720 {
721  EDA_ITEM* item = m_editPoints->GetParent();
722 
723  switch( item->Type() )
724  {
725  case PCB_LINE_T:
726  case PCB_MODULE_EDGE_T:
727  {
728  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( item );
729  {
730  switch( segment->GetShape() )
731  {
732  case S_SEGMENT:
733  return *( m_editPoints->Next( *m_editedPoint ) ); // select the other end of line
734 
735  case S_ARC:
736  case S_CIRCLE:
737  return m_editPoints->Point( CIRC_CENTER );
738 
739  default: // suppress warnings
740  break;
741  }
742  }
743 
744  break;
745  }
746 
747  case PCB_DIMENSION_T:
748  {
749  // Constraint for crossbar
750  if( isModified( m_editPoints->Point( DIM_FEATUREGO ) ) )
751  return m_editPoints->Point( DIM_FEATUREDO );
752 
753  else if( isModified( m_editPoints->Point( DIM_FEATUREDO ) ) )
754  return m_editPoints->Point( DIM_FEATUREGO );
755 
756  else
757  return EDIT_POINT( m_editedPoint->GetPosition() ); // no constraint
758 
759  break;
760  }
761 
762  default:
763  break;
764  }
765 
766  // In any other case we may align item to its original position
767  return m_original;
768 }
769 
770 
772 {
778 }
779 
780 
782 {
783  if( aSelection.Size() != 1 )
784  return false;
785 
786  auto item = aSelection.Front();
787 
788  // Works only for zones and line segments
789  return item->Type() == PCB_ZONE_AREA_T ||
790  ( ( item->Type() == PCB_LINE_T || item->Type() == PCB_MODULE_EDGE_T ) &&
791  static_cast<DRAWSEGMENT*>( item )->GetShape() == S_SEGMENT );
792 }
793 
794 
795 // Finds a corresponding vertex in a polygon set
796 static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
797 findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
798 {
799  for( auto it = aPolySet.IterateWithHoles(); it; ++it )
800  {
801  auto vertexIdx = it.GetIndex();
802 
803  if( aPolySet.Vertex( vertexIdx ) == aPoint.GetPosition() )
804  return std::make_pair( true, vertexIdx );
805  }
806 
807  return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
808 }
809 
810 
812 {
813  if( !m_editPoints || !m_editedPoint )
814  return false;
815 
816  EDA_ITEM* item = m_editPoints->GetParent();
817 
818  if( !item || item->Type() != PCB_ZONE_AREA_T )
819  return false;
820 
821  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
822  auto& polyset = *zone->Outline();
823  auto vertex = findVertex( polyset, *m_editedPoint );
824 
825  if( !vertex.first )
826  return false;
827 
828  const auto& vertexIdx = vertex.second;
829 
830  // Check if there are enough vertices so one can be removed without
831  // degenerating the polygon.
832  // The first condition allows to remove all corners from holes (when there
833  // are only 2 vertices left, a hole is removed).
834  if( vertexIdx.m_contour == 0 && polyset.Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
835  return false;
836 
837  // Remove corner does not work with lines
838  if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
839  return false;
840 
841  return m_editedPoint != NULL;
842 }
843 
844 
846 {
847  EDA_ITEM* item = m_editPoints->GetParent();
848  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
849  const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
850  BOARD_COMMIT commit( frame );
851 
852  if( item->Type() == PCB_ZONE_AREA_T )
853  {
854  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
855  SHAPE_POLY_SET* zoneOutline = zone->Outline();
856 
857  commit.Modify( zone );
858 
859  unsigned int nearestIdx = 0;
860  unsigned int nextNearestIdx = 0;
861  unsigned int nearestDist = INT_MAX;
862  unsigned int firstPointInContour = 0;
863 
864  // Search the best outline segment to add a new corner
865  // and therefore break this segment into two segments
866 
867  // Object to iterate through the corners of the outlines (main contour and its holes)
868  SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0,
869  zoneOutline->OutlineCount()-1, /* IterateHoles */ true );
870  int curr_idx = 0;
871 
872  // Iterate through all the corners of the outlines and search the best segment
873  for( ; iterator; iterator++, curr_idx++ )
874  {
875  int jj = curr_idx+1;
876 
877  if( iterator.IsEndContour() )
878  { // We reach the last point of the current contour (main or hole)
879  jj = firstPointInContour;
880  firstPointInContour = curr_idx+1; // Prepare next contour analysis
881  }
882 
883  SEG curr_segment( zoneOutline->Vertex( curr_idx ), zoneOutline->Vertex( jj ) );
884 
885  unsigned int distance = curr_segment.Distance( cursorPos );
886 
887  if( distance < nearestDist )
888  {
889  nearestDist = distance;
890  nearestIdx = curr_idx;
891  nextNearestIdx = jj;
892  }
893  }
894 
895 
896  // Find the point on the closest segment
897  VECTOR2I sideOrigin = zoneOutline->Vertex( nearestIdx );
898  VECTOR2I sideEnd = zoneOutline->Vertex( nextNearestIdx );
899  SEG nearestSide( sideOrigin, sideEnd );
900  VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
901 
902  // Do not add points that have the same coordinates as ones that already belong to polygon
903  // instead, add a point in the middle of the side
904  if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
905  nearestPoint = ( sideOrigin + sideEnd ) / 2;
906 
907  // Add corner between nearestIdx and nextNearestIdx:
908  zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
909  zone->Hatch();
910 
911  commit.Push( _( "Add a zone corner" ) );
912  }
913 
914  else if( item->Type() == PCB_LINE_T || item->Type() == PCB_MODULE_EDGE_T )
915  {
916  bool moduleEdge = item->Type() == PCB_MODULE_EDGE_T;
917 
918  DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item );
919 
920  if( segment->GetShape() == S_SEGMENT )
921  {
922  commit.Modify( segment );
923 
924  SEG seg( segment->GetStart(), segment->GetEnd() );
925  VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
926 
927  // Move the end of the line to the break point..
928  segment->SetEnd( wxPoint( nearestPoint.x, nearestPoint.y ) );
929 
930  // and add another one starting from the break point
931  DRAWSEGMENT* newSegment;
932 
933  if( moduleEdge )
934  {
935  EDGE_MODULE* edge = static_cast<EDGE_MODULE*>( segment );
936  assert( edge->Type() == PCB_MODULE_EDGE_T );
937  assert( edge->GetParent()->Type() == PCB_MODULE_T );
938  newSegment = new EDGE_MODULE( *edge );
939  }
940  else
941  {
942  newSegment = new DRAWSEGMENT( *segment );
943  }
944 
945  newSegment->ClearSelected();
946  newSegment->SetStart( wxPoint( nearestPoint.x, nearestPoint.y ) );
947  newSegment->SetEnd( wxPoint( seg.B.x, seg.B.y ) );
948 
949  commit.Add( newSegment );
950  commit.Push( _( "Split segment" ) );
951  }
952  }
953 
954  updatePoints();
955  return 0;
956 }
957 
958 
960 {
961  if( !m_editedPoint )
962  return 0;
963 
964  EDA_ITEM* item = m_editPoints->GetParent();
965 
966  if( !item )
967  return 0;
968 
969  SHAPE_POLY_SET* polygon = nullptr;
970 
971  if( item->Type() == PCB_ZONE_AREA_T)
972  {
973  auto zone = static_cast<ZONE_CONTAINER*>( item );
974  polygon = zone->Outline();
975  }
976  else if( item->Type() == PCB_LINE_T )
977  {
978  auto ds = static_cast<DRAWSEGMENT*>( item );
979 
980  if( ds->GetShape() == S_POLYGON )
981  polygon = &ds->GetPolyShape();
982  }
983 
984  if( !polygon )
985  return 0;
986 
987  PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
988  BOARD_COMMIT commit( frame );
989  auto vertex = findVertex( *polygon, *m_editedPoint );
990 
991  if( vertex.first )
992  {
993  const auto& vertexIdx = vertex.second;
994  auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
995 
996  if( outline.PointCount() > 3 )
997  {
998  // the usual case: remove just the corner when there are >3 vertices
999  commit.Modify( item );
1000  polygon->RemoveVertex( vertexIdx );
1001  }
1002  else
1003  {
1004  // either remove a hole or the polygon when there are <= 3 corners
1005  if( vertexIdx.m_contour > 0 )
1006  {
1007  // remove hole
1008  commit.Modify( item );
1009  polygon->RemoveContour( vertexIdx.m_contour );
1010  }
1011  else
1012  {
1014  commit.Remove( item );
1015  }
1016  }
1017 
1018  setEditedPoint( nullptr );
1019  commit.Push( _( "Remove a zone/polygon corner" ) );
1020  updatePoints();
1021  }
1022 
1023  return 0;
1024 }
1025 
1026 
1028 {
1029  updatePoints();
1030  return 0;
1031 }
static TOOL_ACTION selectionClear
Clears the current selection.
Definition: pcb_actions.h:53
virtual void ShowCursor(bool aEnabled)
Function ShowCursor() Enables or disables display of cursor.
Class EC_CONVERGING.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:227
BOARD_ITEM_CONTAINER * GetParent() const
Class ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:60
int OnSelectionChange(const TOOL_EVENT &aEvent)
Function OnSelected()
COMMIT & Modify(EDA_ITEM *aItem)
Modifies a given item in the model.
Definition: commit.h:103
TOOL_BASE * FindTool(int aId) const
Function FindTool() Searches for a tool with given ID.
void setTransitions() override
Sets up handlers for various events.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Returns the index-th vertex in a given hole outline within a given outline
const std::vector< wxPoint > BuildPolyPointsList() const
Build and return the list of corners in a std::vector<wxPoint> It must be used only to convert the SH...
virtual void Move(const wxPoint &aMoveVector) override
Function Move move this object.
void updateItem() const
Updates item&#39;s points with edit points.
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:280
void SetArcStart(const wxPoint &aArcStartPoint)
Initialize the start arc point.
This file is part of the common library.
wxPoint m_crossBarF
const VECTOR2D & DragOrigin() const
Returns the point where dragging has started.
Definition: tool_event.h:255
void addCorner(const VECTOR2I &aPoint)
Adds a new edit point on a zone outline/line.
bool IsMotion() const
Definition: tool_event.h:290
const wxPoint GetCenter() const override
Function GetCenter()
void ClearSelected()
Definition: base_struct.h:260
COMMIT & Add(EDA_ITEM *aItem)
Adds a new item to the model
Definition: commit.h:78
VIEW_CONTROLS class definition.
void updateEditedPoint(const TOOL_EVENT &aEvent)
Updates which point is being edited.
Class SELECTION_TOOL.
Class BOARD to handle a board.
virtual void Revert() override
Revertes the commit by restoring the modifed items state.
CONDITIONAL_MENU & GetMenu()
Function GetMenu.
Definition: tool_menu.cpp:49
polygon (not yet used for tracks, but could be in microwave apps)
EDA_ITEM * Front() const
Definition: selection.h:144
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:215
CONST_ITERATOR CIterateWithHoles(int aOutline) const
Class EDIT_LINE.
Definition: edit_points.h:189
class ZONE_CONTAINER, a zone area
Definition: typeinfo.h:102
virtual void Remove(VIEW_ITEM *aItem)
Function Remove() Removes a VIEW_ITEM from the view.
Definition: view.cpp:339
OPT_TOOL_EVENT Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Function Wait()
int TotalVertices() const
Returns total number of vertices stored in the set.
SHAPE_POLY_SET * Outline()
Definition: class_zone.h:236
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Function RunAction() Runs the specified action.
Definition: tool_manager.h:125
double RAD2DECIDEG(double rad)
Definition: trigo.h:204
Struct VERTEX_INDEX.
Classes to handle copper zones.
void setAltConstraint(bool aEnabled)
Sets up an alternative constraint (typically enabled upon a modifier key being pressed).
usual segment : line with rounded ends
const VECTOR2D & Position() const
Returns mouse cursor position in world coordinates.
Definition: tool_event.h:248
static const TOOL_EVENT ClearedEvent
Event sent after selection is cleared.
ITERATOR Iterate(int aFirst, int aLast, bool aIterateHoles=false)
Function Iterate returns an object to iterate through the points of the polygons between aFirst and a...
int OutlineCount() const
Returns the number of outlines in the set
CIRCLE_POINTS
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Function Go()
class EDGE_MODULE, a footprint edge
Definition: typeinfo.h:94
Class EC_LINE.
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true) override
Executes the changes.
Class EC_45DEGREE.
static TOOL_ACTION selectionModified
Modified selection notification.
Definition: pcb_actions.h:111
bool removeCornerCondition(const SELECTION &aSelection)
Condition to display "Remove corner" context menu entry.
bool IsEndContour() const
Function IsEndContour.
const wxPoint & GetArcStart() const
Class EC_CIRCLE.
VECTOR2I & Vertex(int aIndex, int aOutline, int aHole)
Returns the index-th vertex in a given hole outline within a given outline
wxPoint m_featureLineGO
void PassEvent()
Allows a tool to pass the already handled event to the next tool on the stack.
Definition: tool_manager.h:351
DIMENSION class definition.
std::shared_ptr< EDIT_POINTS > m_editPoints
Currently available edit points.
Definition: point_editor.h:73
void finishItem()
Applies the last changes to the edited item.
KIGFX::VIEW_CONTROLS * controls() const
Definition: pcb_tool.h:133
class MODULE, a footprint
Definition: typeinfo.h:89
double Angle() const
Function Angle computes the angle of the vector.
Definition: vector2d.h:308
STROKE_T GetShape() const
void SetOrigin(const wxPoint &aOrigin)
Function SetOrigin Sets a new origin of the crossbar line.
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
const wxPoint & GetEnd()
Function GetEnd.
ITERATOR IterateWithHoles(int aOutline)
Function IterateWithHoles.
Class SHAPE_POLY_SET.
SEG_POINTS
SHAPE_LINE_CHAIN & Outline(int aIndex)
Returns the reference to aIndex-th outline in the set
const wxPoint & GetOrigin() const
Function GetOrigin.
static TOOL_ACTION pointEditorAddCorner
Break outline (insert additional points to an edge)
Definition: pcb_actions.h:219
static TOOL_ACTION pointEditorRemoveCorner
Removes a corner.
Definition: pcb_actions.h:222
void SetEnd(const wxPoint &aEnd)
Function SetEnd Sets a new end of the crossbar line.
VECTOR2D GetCursorPosition() const
Returns the current cursor position in world coordinates.
Arcs (with rounded ends)
PCB_EDIT_FRAME * frame() const
Definition: pcb_tool.h:134
Class TOOL_EVENT.
Definition: tool_event.h:162
std::shared_ptr< EDIT_CONSTRAINT< EDIT_POINT > > m_altConstraint
Definition: point_editor.h:76
COMMIT & StageItems(const Range &aRange, CHANGE_TYPE aChangeType)
Definition: commit.h:116
EDIT_POINT get45DegConstrainer() const
Returns a point that should be used as a constrainer for 45 degrees mode.
DIMENSION_POINTS
SELECTION & GetSelection()
Function GetSelection()
bool isModified(const EDIT_POINT &aPoint) const
Returns true if aPoint is the currently modified point.
Definition: point_editor.h:97
Class VIEW_CONTROLS is an interface for classes handling user events controlling the view behaviour (...
static const TOOL_EVENT UnselectedEvent
Event sent after an item is unselected.
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Function ForceCursorPosition() Places the cursor immediately at a given point.
SHAPE_POLY_SET & GetPolyShape()
const SELECTION & selection() const
Definition: pcb_tool.cpp:218
SELECTION_TOOL * m_selectionTool
Selection tool used for obtaining selected items
Definition: point_editor.h:64
All active tools
Definition: tool_event.h:138
virtual void Update(VIEW_ITEM *aItem, int aUpdateFlags)
For dynamic VIEWs, informs the associated VIEW that the graphical representation of this item has cha...
Definition: view.cpp:1382
class DIMENSION, a dimension (graphic item)
Definition: typeinfo.h:100
virtual void SetAutoPan(bool aEnabled)
Function SetAutoPan Turns on/off auto panning (this feature is used when there is a tool active (eg...
KIGFX::VIEW_CONTROLS * getViewControls() const
Function getViewControls()
Definition: tool_base.cpp:41
static const TOOL_EVENT SelectedEvent
Event sent after an item is selected.
void SetStart(const wxPoint &aStart)
KIGFX::VIEW * getView() const
Function getView()
Definition: tool_base.cpp:35
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Function NearestPoint()
Definition: seg.h:364
Definition: seg.h:36
virtual VECTOR2I GetPosition() const
Function GetPosition()
Definition: edit_points.h:65
void Fill(std::vector< ZONE_CONTAINER * > aZones)
Definition: zone_filler.cpp:82
VECTOR2D GetGridPoint(const VECTOR2D &aPoint) const
Function GetGridPoint() For a given point it returns the nearest point belonging to the grid in world...
double GetAngle() const
Common, abstract interface for edit frames.
KIGFX::PCB_VIEW * view() const
Definition: pcb_tool.h:132
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Function AddItem()
void SetHeight(int aHeight)
Function SetHeight Sets the length of feature lines.
void RemoveVertex(int aGlobalIndex)
Function RemoveVertex deletes the aGlobalIndex-th vertex.
Class ITERATOR_TEMPLATE.
Class TOOL_ACTION.
Definition: tool_action.h:46
const wxPoint GetArcEnd() const
void Hatch()
Function Hatch computes the hatch lines depending on the hatch parameters and stores it in the zone&#39;s...
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:180
void removeCorner(EDIT_POINT *aPoint)
Removes a corner.
RESET_REASON
Determines the reason of reset for a tool
Definition: tool_base.h:80
void updatePoints()
Updates edit points with item&#39;s points.
void SetEnd(const wxPoint &aEnd)
static std::pair< bool, SHAPE_POLY_SET::VERTEX_INDEX > findVertex(SHAPE_POLY_SET &aPolySet, const EDIT_POINT &aPoint)
void SetAngle(double aAngle)
Function SetAngle sets the angle for arcs, and normalizes it within the range 0 - 360 degrees...
EDIT_POINT m_original
Original position for the current drag point.
Definition: point_editor.h:70
void Activate()
Function Activate() Runs the tool.
void InsertVertex(int aGlobalIndex, VECTOR2I aNewVertex)
Function InsertVertex Adds a vertex in the globally indexed position aGlobalIndex.
wxPoint m_crossBarO
int Size() const
Returns the number of selected parts.
Definition: selection.h:112
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Function Add() Adds a VIEW_ITEM to the view.
Definition: view.cpp:309
bool Init() override
Function Init() Init() is called once upon a registration of the tool.
Class EDIT_POINT.
Definition: edit_points.h:46
Module description (excepted pads)
POLYGON & Polygon(int aIndex)
Returns the aIndex-th subpolygon in the set
Class VIEW.
Definition: view.h:58
EDIT_POINT m_altConstrainer
Definition: point_editor.h:79
EDGE_MODULE class definition.
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
EDIT_POINT * m_editedPoint
Currently edited point, NULL if there is none.
Definition: point_editor.h:67
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
wxPoint m_featureLineDO
int Distance(const SEG &aSeg) const
Function Distance()
Definition: seg.h:195
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:74
void Reset(RESET_REASON aReason) override
Function Reset() Brings the tool to a known, initial state.
Class EC_SNAPLINE.
class PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer...
void setEditedPoint(EDIT_POINT *aPoint)
Sets the current point being edited. NULL means none.
static void buildForPolyOutline(std::shared_ptr< EDIT_POINTS > points, const SHAPE_POLY_SET *aOutline, KIGFX::GAL *aGal)
Class DIMENSION.
virtual void ApplyConstraint()
Function ApplyConstraint()
Definition: edit_points.h:159
int modifiedSelection(const TOOL_EVENT &aEvent)
ARC_POINTS
void ClearFilledPolysList()
Function ClearFilledPolysList clears the list of filled polygons.
Definition: class_zone.h:521
static bool addCornerCondition(const SELECTION &aSelection)
Condition to display "Create corner" context menu entry.
OPT< TOOL_EVENT > OPT_TOOL_EVENT
Definition: tool_event.h:465
Class GAL is the abstract interface for drawing on a 2D-surface.
static std::shared_ptr< EDIT_POINTS > Make(EDA_ITEM *aItem, KIGFX::GAL *aGal)
BOARD * board() const
Definition: pcb_tool.h:135
void RemoveContour(int aContourIdx, int aPolygonIdx=-1)
Function RemoveContour deletes the aContourIdx-th contour of the aPolygonIdx-th polygon in the set...
virtual void SetPosition(const VECTOR2I &aPosition)
Function SetPosition()
Definition: edit_points.h:97