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 <wxPcbStruct.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 // Point editor
49 TOOL_ACTION PCB_ACTIONS::pointEditorAddCorner( "pcbnew.PointEditor.addCorner",
50  AS_GLOBAL, 0,
51  _( "Create Corner" ), _( "Create a corner" ), add_corner_xpm );
52 
53 TOOL_ACTION PCB_ACTIONS::pointEditorRemoveCorner( "pcbnew.PointEditor.removeCorner",
54  AS_GLOBAL, 0,
55  _( "Remove Corner" ), _( "Remove corner" ), delete_xpm );
56 
57 
58 // Few constants to avoid using bare numbers for point indices
60 {
62 };
63 
65 {
67 };
68 
70 {
72 };
73 
75 {
80 };
81 
83 {
84 private:
85 
86  static void buildForPolyOutline( std::shared_ptr<EDIT_POINTS> points, const SHAPE_POLY_SET* aOutline, KIGFX::GAL* aGal )
87  {
88 
89  int cornersCount = aOutline->TotalVertices();
90 
91  for( auto iterator = aOutline->CIterateWithHoles(); iterator; iterator++ )
92  {
93  points->AddPoint( *iterator );
94 
95  if( iterator.IsEndContour() )
96  points->AddBreak();
97  }
98 
99  // Lines have to be added after creating edit points,
100  // as they use EDIT_POINT references
101  for( int i = 0; i < cornersCount - 1; ++i )
102  {
103  if( points->IsContourEnd( i ) )
104  {
105  points->AddLine( points->Point( i ),
106  points->Point( points->GetContourStartIdx( i ) ) );
107  }
108  else
109  {
110  points->AddLine( points->Point( i ), points->Point( i + 1 ) );
111  }
112 
113  points->Line( i ).SetConstraint( new EC_SNAPLINE( points->Line( i ),
114  std::bind( &KIGFX::GAL::GetGridPoint, aGal, _1 ) ) );
115  }
116 
117  // The last missing line, connecting the last and the first polygon point
118  points->AddLine( points->Point( cornersCount - 1 ),
119  points->Point( points->GetContourStartIdx( cornersCount - 1 ) ) );
120 
121  points->Line( points->LinesSize() - 1 ).SetConstraint(
122  new EC_SNAPLINE( points->Line( points->LinesSize() - 1 ),
123  std::bind( &KIGFX::GAL::GetGridPoint, aGal, _1 ) ) );
124  }
125 
126 public:
127  static std::shared_ptr<EDIT_POINTS> Make( EDA_ITEM* aItem, KIGFX::GAL* aGal )
128  {
129  std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
130 
131  if( !aItem )
132  return points;
133 
134  // Generate list of edit points basing on the item type
135  switch( aItem->Type() )
136  {
137  case PCB_LINE_T:
138  case PCB_MODULE_EDGE_T:
139  {
140  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( aItem );
141 
142  switch( segment->GetShape() )
143  {
144  case S_SEGMENT:
145  points->AddPoint( segment->GetStart() );
146  points->AddPoint( segment->GetEnd() );
147  break;
148 
149  case S_ARC:
150  points->AddPoint( segment->GetCenter() );
151  points->AddPoint( segment->GetArcStart() );
152  points->AddPoint( segment->GetArcEnd() );
153 
154  // Set constraints
155  // Arc end has to stay at the same radius as the start
156  points->Point( ARC_END ).SetConstraint( new EC_CIRCLE( points->Point( ARC_END ),
157  points->Point( ARC_CENTER ),
158  points->Point( ARC_START ) ) );
159  break;
160 
161  case S_CIRCLE:
162  points->AddPoint( segment->GetCenter() );
163  points->AddPoint( segment->GetEnd() );
164  break;
165 
166  case S_POLYGON:
167  {
168  buildForPolyOutline( points, &segment->GetPolyShape(), aGal );
169  break;
170  }
171 
172  default: // suppress warnings
173  break;
174  }
175 
176  break;
177  }
178 
179  case PCB_ZONE_AREA_T:
180  {
181  auto zone = static_cast<const ZONE_CONTAINER*>( aItem );
182  buildForPolyOutline( points, zone->Outline(), aGal );
183  break;
184  }
185 
186  case PCB_DIMENSION_T:
187  {
188  const DIMENSION* dimension = static_cast<const DIMENSION*>( aItem );
189 
190  points->AddPoint( dimension->m_crossBarO );
191  points->AddPoint( dimension->m_crossBarF );
192  points->AddPoint( dimension->m_featureLineGO );
193  points->AddPoint( dimension->m_featureLineDO );
194 
195  // Dimension height setting - edit points should move only along the feature lines
196  points->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARO ),
197  points->Point( DIM_FEATUREGO ) ) );
198  points->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARF ),
199  points->Point( DIM_FEATUREDO ) ) );
200 
201  break;
202  }
203 
204  default:
205  points.reset();
206  break;
207  }
208 
209  return points;
210  }
211 
212 private:
214 };
215 
216 
218  TOOL_INTERACTIVE( "pcbnew.PointEditor" ), m_selectionTool( NULL ), m_editedPoint( NULL ),
219  m_original( VECTOR2I( 0, 0 ) ), m_altConstrainer( VECTOR2I( 0, 0 ) )
220 {
221 }
222 
223 
225 {
226  m_editPoints.reset();
227  m_altConstraint.reset();
228 }
229 
230 
232 {
233  // Find the selection tool, so they can cooperate
234  m_selectionTool = static_cast<SELECTION_TOOL*>( m_toolMgr->FindTool( "pcbnew.InteractiveSelection" ) );
235 
236  if( !m_selectionTool )
237  {
238  DisplayError( NULL, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
239  return false;
240  }
241 
242  auto& menu = m_selectionTool->GetToolMenu().GetMenu();
245  std::bind( &POINT_EDITOR::removeCornerCondition, this, _1 ) );
246 
247  return true;
248 }
249 
250 
252 {
253  EDIT_POINT* point = m_editedPoint;
254 
255  if( aEvent.IsMotion() )
256  {
257  point = m_editPoints->FindPoint( aEvent.Position(), getView() );
258  }
259  else if( aEvent.IsDrag( BUT_LEFT ) )
260  {
261  point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
262  }
263 
264  if( m_editedPoint != point )
265  setEditedPoint( point );
266 }
267 
268 
270 {
271  const SELECTION& selection = m_selectionTool->GetSelection();
272 
273  if( selection.Size() != 1 )
274  return 0;
275 
276  Activate();
277 
279  KIGFX::VIEW* view = getView();
280  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
281  auto item = selection.Front();
282 
283  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
284 
285  if( !m_editPoints )
286  return 0;
287 
288  view->Add( m_editPoints.get() );
289  m_editedPoint = NULL;
290  bool modified = false;
291  bool revert = false;
292 
293  BOARD_COMMIT commit( editFrame );
294 
295  // Main loop: keep receiving events
296  while( OPT_TOOL_EVENT evt = Wait() )
297  {
298  if( revert )
299  break;
300 
301  if( !m_editPoints ||
302  evt->Matches( m_selectionTool->ClearedEvent ) ||
303  evt->Matches( m_selectionTool->UnselectedEvent ) ||
304  evt->Matches( m_selectionTool->SelectedEvent ) )
305  {
306  break;
307  }
308 
309  if ( !modified )
310  updateEditedPoint( *evt );
311 
312  if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
313  {
314  if( !modified )
315  {
316  commit.StageItems( selection, CHT_MODIFY );
317 
318  controls->ForceCursorPosition( false );
319  m_original = *m_editedPoint; // Save the original position
320  controls->SetAutoPan( true );
321  modified = true;
322  }
323 
324  bool enableAltConstraint = !!evt->Modifier( MD_CTRL );
325 
326  if( enableAltConstraint != (bool) m_altConstraint ) // alternative constraint
327  setAltConstraint( enableAltConstraint );
328 
330 
331  if( m_altConstraint )
332  m_altConstraint->Apply();
333  else
335 
336  updateItem();
337  updatePoints();
338  }
339 
340  else if( evt->IsMouseUp( BUT_LEFT ) )
341  {
342  controls->SetAutoPan( false );
343  setAltConstraint( false );
344 
345  if( modified )
346  {
347  commit.Push( _( "Drag a line ending" ) );
348  modified = false;
349  }
350 
351  m_toolMgr->PassEvent();
352  }
353 
354  else if( evt->IsCancel() )
355  {
356  if( modified ) // Restore the last change
357  revert = true;
358 
359  // Let the selection tool receive the event too
360  m_toolMgr->PassEvent();
361 
362  // Do not exit right now, let the selection clear the selection
363  //break;
364  }
365 
366  else
367  {
368  m_toolMgr->PassEvent();
369  }
370  }
371 
372  if( m_editPoints )
373  {
374  view->Remove( m_editPoints.get() );
375 
376  if( modified && revert )
377  commit.Revert();
378  else
379  finishItem();
380 
381  m_editPoints.reset();
382  }
383 
384  return 0;
385 }
386 
387 
389 {
390  EDA_ITEM* item = m_editPoints->GetParent();
391 
392  if( !item )
393  return;
394 
395  switch( item->Type() )
396  {
397  case PCB_LINE_T:
398  case PCB_MODULE_EDGE_T:
399  {
400  DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item );
401  switch( segment->GetShape() )
402  {
403  case S_SEGMENT:
404  if( isModified( m_editPoints->Point( SEG_START ) ) )
405  segment->SetStart( wxPoint( m_editPoints->Point( SEG_START ).GetPosition().x,
406  m_editPoints->Point( SEG_START ).GetPosition().y ) );
407 
408  else if( isModified( m_editPoints->Point( SEG_END ) ) )
409  segment->SetEnd( wxPoint( m_editPoints->Point( SEG_END ).GetPosition().x,
410  m_editPoints->Point( SEG_END ).GetPosition().y ) );
411 
412  break;
413 
414  case S_ARC:
415  {
416  const VECTOR2I& center = m_editPoints->Point( ARC_CENTER ).GetPosition();
417  const VECTOR2I& start = m_editPoints->Point( ARC_START ).GetPosition();
418  const VECTOR2I& end = m_editPoints->Point( ARC_END ).GetPosition();
419 
420  if( center != segment->GetCenter() )
421  {
422  wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
423  segment->Move( moveVector );
424 
425  m_editPoints->Point( ARC_START ).SetPosition( segment->GetArcStart() );
426  m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
427  }
428 
429  else
430  {
431  segment->SetArcStart( wxPoint( start.x, start.y ) );
432 
433  VECTOR2D startLine = start - center;
434  VECTOR2I endLine = end - center;
435  double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
436 
437  // Adjust the new angle to (counter)clockwise setting
438  bool clockwise = ( segment->GetAngle() > 0 );
439 
440  if( clockwise && newAngle < 0.0 )
441  newAngle += 3600.0;
442  else if( !clockwise && newAngle > 0.0 )
443  newAngle -= 3600.0;
444 
445  segment->SetAngle( newAngle );
446  }
447 
448  break;
449  }
450 
451  case S_CIRCLE:
452  {
453  const VECTOR2I& center = m_editPoints->Point( CIRC_CENTER ).GetPosition();
454  const VECTOR2I& end = m_editPoints->Point( CIRC_END ).GetPosition();
455 
456  if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
457  {
458  wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
459  segment->Move( moveVector );
460  }
461  else
462  {
463  segment->SetEnd( wxPoint( end.x, end.y ) );
464  }
465 
466  break;
467  }
468 
469  case S_POLYGON:
470  {
471  SHAPE_POLY_SET* outline = &segment->GetPolyShape();
472 
473  for( int i = 0; i < outline->TotalVertices(); ++i )
474  {
475  VECTOR2I point = m_editPoints->Point( i ).GetPosition();
476  outline->Vertex( i ) = point;
477  }
478  }
479 
480  default: // suppress warnings
481  break;
482  }
483 
484  // Update relative coordinates for module edges
485  if( EDGE_MODULE* edge = dyn_cast<EDGE_MODULE*>( item ) )
486  edge->SetLocalCoord();
487 
488  break;
489  }
490 
491  case PCB_ZONE_AREA_T:
492  {
493  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
494  zone->ClearFilledPolysList();
495  SHAPE_POLY_SET* outline = zone->Outline();
496 
497  for( int i = 0; i < outline->TotalVertices(); ++i )
498  {
499  VECTOR2I point = m_editPoints->Point( i ).GetPosition();
500  outline->Vertex( i ) = point;
501  }
502 
503  zone->Hatch();
504 
505  break;
506  }
507 
508  case PCB_DIMENSION_T:
509  {
510  DIMENSION* dimension = static_cast<DIMENSION*>( item );
511 
512  // Check which point is currently modified and updated dimension's points respectively
513  if( isModified( m_editPoints->Point( DIM_CROSSBARO ) ) )
514  {
515  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetOrigin() );
516  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() );
517 
518  if( featureLine.Cross( crossBar ) > 0 )
519  dimension->SetHeight( -featureLine.EuclideanNorm() );
520  else
521  dimension->SetHeight( featureLine.EuclideanNorm() );
522  }
523 
524  else if( isModified( m_editPoints->Point( DIM_CROSSBARF ) ) )
525  {
526  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
527  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() );
528 
529  if( featureLine.Cross( crossBar ) > 0 )
530  dimension->SetHeight( -featureLine.EuclideanNorm() );
531  else
532  dimension->SetHeight( featureLine.EuclideanNorm() );
533  }
534 
535  else if( isModified( m_editPoints->Point( DIM_FEATUREGO ) ) )
536  {
538  m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ),
539  m_editPoints->Point( DIM_FEATUREGO ) ) );
540  m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ),
541  m_editPoints->Point( DIM_FEATUREDO ) ) );
542  }
543 
544  else if( isModified( m_editPoints->Point( DIM_FEATUREDO ) ) )
545  {
547  m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ),
548  m_editPoints->Point( DIM_FEATUREGO ) ) );
549  m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ),
550  m_editPoints->Point( DIM_FEATUREDO ) ) );
551  }
552 
553  break;
554  }
555 
556  default:
557  break;
558  }
559 }
560 
561 
563 {
564  EDA_ITEM* item = m_editPoints->GetParent();
565 
566  if( !item )
567  return;
568 
569  if( item->Type() == PCB_ZONE_AREA_T )
570  {
571  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
572 
573  if( zone->IsFilled() )
574  {
575  getEditFrame<PCB_EDIT_FRAME>()->Fill_Zone( zone );
576 // zone->GetBoard()->GetRatsnest()->Recalculate( zone->GetNetCode() );
577  }
578  }
579 }
580 
581 
583 {
584  if( !m_editPoints )
585  return;
586 
587  EDA_ITEM* item = m_editPoints->GetParent();
588 
589  if( !item )
590  return;
591 
592  switch( item->Type() )
593  {
594  case PCB_LINE_T:
595  case PCB_MODULE_EDGE_T:
596  {
597  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( item );
598  {
599  switch( segment->GetShape() )
600  {
601  case S_SEGMENT:
602  m_editPoints->Point( SEG_START ).SetPosition( segment->GetStart() );
603  m_editPoints->Point( SEG_END ).SetPosition( segment->GetEnd() );
604  break;
605 
606  case S_ARC:
607  m_editPoints->Point( ARC_CENTER ).SetPosition( segment->GetCenter() );
608  m_editPoints->Point( ARC_START).SetPosition( segment->GetArcStart() );
609  m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
610  break;
611 
612  case S_CIRCLE:
613  m_editPoints->Point( CIRC_CENTER ).SetPosition( segment->GetCenter() );
614  m_editPoints->Point( CIRC_END ).SetPosition( segment->GetEnd() );
615  break;
616 
617  default: // suppress warnings
618  break;
619  }
620 
621  break;
622  }
623  }
624 
625  case PCB_ZONE_AREA_T:
626  {
627  const ZONE_CONTAINER* zone = static_cast<const ZONE_CONTAINER*>( item );
628  const SHAPE_POLY_SET* outline = zone->Outline();
629 
630  if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
631  {
632  getView()->Remove( m_editPoints.get() );
633  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
634  getView()->Add( m_editPoints.get() );
635  }
636  else
637  {
638  for( int i = 0; i < outline->TotalVertices(); ++i )
639  m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
640  }
641 
642  break;
643  }
644 
645  case PCB_DIMENSION_T:
646  {
647  const DIMENSION* dimension = static_cast<const DIMENSION*>( item );
648 
649  m_editPoints->Point( DIM_CROSSBARO ).SetPosition( dimension->m_crossBarO );
650  m_editPoints->Point( DIM_CROSSBARF ).SetPosition( dimension->m_crossBarF );
651  m_editPoints->Point( DIM_FEATUREGO ).SetPosition( dimension->m_featureLineGO );
652  m_editPoints->Point( DIM_FEATUREDO ).SetPosition( dimension->m_featureLineDO );
653  break;
654  }
655 
656  default:
657  break;
658  }
659 
660  getView()->Update( m_editPoints.get() );
661 }
662 
663 
665 {
667 
668  if( aPoint )
669  {
670  controls->ForceCursorPosition( true, aPoint->GetPosition() );
671  controls->ShowCursor( true );
672  controls->SetSnapping( true );
673  }
674  else
675  {
676  controls->ShowCursor( false );
677  controls->SetSnapping( false );
678  controls->ForceCursorPosition( false );
679  }
680 
681  m_editedPoint = aPoint;
682 }
683 
684 
685 void POINT_EDITOR::setAltConstraint( bool aEnabled )
686 {
687  if( aEnabled )
688  {
689  EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
690 
691  if( line )
692  {
693  if( m_editPoints->GetParent()->Type() == PCB_ZONE_AREA_T )
695  }
696  else
697  {
698  // Find a proper constraining point for 45 degrees mode
701  }
702  }
703  else
704  {
705  m_altConstraint.reset();
706  }
707 }
708 
709 
711 {
712  EDA_ITEM* item = m_editPoints->GetParent();
713 
714  switch( item->Type() )
715  {
716  case PCB_LINE_T:
717  case PCB_MODULE_EDGE_T:
718  {
719  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( item );
720  {
721  switch( segment->GetShape() )
722  {
723  case S_SEGMENT:
724  return *( m_editPoints->Next( *m_editedPoint ) ); // select the other end of line
725 
726  case S_ARC:
727  case S_CIRCLE:
728  return m_editPoints->Point( CIRC_CENTER );
729 
730  default: // suppress warnings
731  break;
732  }
733  }
734 
735  break;
736  }
737 
738  case PCB_DIMENSION_T:
739  {
740  // Constraint for crossbar
741  if( isModified( m_editPoints->Point( DIM_FEATUREGO ) ) )
742  return m_editPoints->Point( DIM_FEATUREDO );
743 
744  else if( isModified( m_editPoints->Point( DIM_FEATUREDO ) ) )
745  return m_editPoints->Point( DIM_FEATUREGO );
746 
747  else
748  return EDIT_POINT( m_editedPoint->GetPosition() ); // no constraint
749 
750  break;
751  }
752 
753  default:
754  break;
755  }
756 
757  // In any other case we may align item to its original position
758  return m_original;
759 }
760 
761 
763 {
769 }
770 
771 
773 {
774  if( aSelection.Size() != 1 )
775  return false;
776 
777  auto item = aSelection.Front();
778 
779  // Works only for zones and line segments
780  return item->Type() == PCB_ZONE_AREA_T ||
781  ( ( item->Type() == PCB_LINE_T || item->Type() == PCB_MODULE_EDGE_T ) &&
782  static_cast<DRAWSEGMENT*>( item )->GetShape() == S_SEGMENT );
783 }
784 
785 
787 {
788  if( !m_editPoints )
789  return false;
790 
791  EDA_ITEM* item = m_editPoints->GetParent();
792 
793  if( item->Type() != PCB_ZONE_AREA_T )
794  return false;
795 
796  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
797 
798  if( zone->GetNumCorners() <= 3 )
799  return false;
800 
801  // Remove corner does not work with lines
802  if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
803  return false;
804 
805  return m_editedPoint != NULL;
806 }
807 
808 
810 {
811  EDA_ITEM* item = m_editPoints->GetParent();
812  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
813  const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
814  BOARD_COMMIT commit( frame );
815 
816  if( item->Type() == PCB_ZONE_AREA_T )
817  {
818  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
819  SHAPE_POLY_SET* zoneOutline = zone->Outline();
820 
821  commit.Modify( zone );
822 
823  unsigned int nearestIdx = 0;
824  unsigned int nextNearestIdx = 0;
825  unsigned int nearestDist = INT_MAX;
826  unsigned int firstPointInContour = 0;
827 
828  // Search the best outline segment to add a new corner
829  // and therefore break this segment into two segments
830 
831  // Object to iterate through the corners of the outlines (main contour and its holes)
832  SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0,
833  zoneOutline->OutlineCount()-1, /* IterateHoles */ true );
834  int curr_idx = 0;
835 
836  // Iterate through all the corners of the outlines and search the best segment
837  for( ; iterator; iterator++, curr_idx++ )
838  {
839  int jj = curr_idx+1;
840 
841  if( iterator.IsEndContour() )
842  { // We reach the last point of the current contour (main or hole)
843  jj = firstPointInContour;
844  firstPointInContour = curr_idx+1; // Prepare next contour analysis
845  }
846 
847  SEG curr_segment( zoneOutline->Vertex( curr_idx ), zoneOutline->Vertex( jj ) );
848 
849  unsigned int distance = curr_segment.Distance( cursorPos );
850 
851  if( distance < nearestDist )
852  {
853  nearestDist = distance;
854  nearestIdx = curr_idx;
855  nextNearestIdx = jj;
856  }
857  }
858 
859 
860  // Find the point on the closest segment
861  VECTOR2I sideOrigin = zoneOutline->Vertex( nearestIdx );
862  VECTOR2I sideEnd = zoneOutline->Vertex( nextNearestIdx );
863  SEG nearestSide( sideOrigin, sideEnd );
864  VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
865 
866  // Do not add points that have the same coordinates as ones that already belong to polygon
867  // instead, add a point in the middle of the side
868  if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
869  nearestPoint = ( sideOrigin + sideEnd ) / 2;
870 
871  // Add corner between nearestIdx and nextNearestIdx:
872  zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
873  zone->Hatch();
874 
875  commit.Push( _( "Add a zone corner" ) );
876  }
877 
878  else if( item->Type() == PCB_LINE_T || item->Type() == PCB_MODULE_EDGE_T )
879  {
880  bool moduleEdge = item->Type() == PCB_MODULE_EDGE_T;
881 
882  DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item );
883 
884  if( segment->GetShape() == S_SEGMENT )
885  {
886  commit.Modify( segment );
887 
888  SEG seg( segment->GetStart(), segment->GetEnd() );
889  VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
890 
891  // Move the end of the line to the break point..
892  segment->SetEnd( wxPoint( nearestPoint.x, nearestPoint.y ) );
893 
894  // and add another one starting from the break point
895  DRAWSEGMENT* newSegment;
896 
897  if( moduleEdge )
898  {
899  EDGE_MODULE* edge = static_cast<EDGE_MODULE*>( segment );
900  assert( edge->Type() == PCB_MODULE_EDGE_T );
901  assert( edge->GetParent()->Type() == PCB_MODULE_T );
902  newSegment = new EDGE_MODULE( *edge );
903  }
904  else
905  {
906  newSegment = new DRAWSEGMENT( *segment );
907  }
908 
909  newSegment->ClearSelected();
910  newSegment->SetStart( wxPoint( nearestPoint.x, nearestPoint.y ) );
911  newSegment->SetEnd( wxPoint( seg.B.x, seg.B.y ) );
912 
913  commit.Add( newSegment );
914  commit.Push( _( "Split segment" ) );
915  }
916  }
917 
918  updatePoints();
919  return 0;
920 }
921 
922 
924 {
925  if( !m_editedPoint )
926  return 0;
927 
928  EDA_ITEM* item = m_editPoints->GetParent();
929 
930  if( !item )
931  return 0;
932 
933  SHAPE_POLY_SET *outline = nullptr;
934 
935  if( item->Type() == PCB_ZONE_AREA_T)
936  {
937  auto zone = static_cast<ZONE_CONTAINER*>( item );
938  outline = zone->Outline();
939  }
940  else if ( item->Type() == PCB_LINE_T )
941  {
942  auto ds = static_cast<DRAWSEGMENT*>( item );
943  if( ds->GetShape() == S_POLYGON )
944  {
945  outline = &ds->GetPolyShape();
946  }
947  }
948 
949  if( !outline )
950  return 0;
951 
952  PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
953  BOARD_COMMIT commit( frame );
954 
955  commit.Modify( item );
956 
957  for( int i = 0; i < outline->TotalVertices(); ++i )
958  {
959  if( outline->Vertex( i ) == m_editedPoint->GetPosition() )
960  {
961  outline->RemoveVertex( i );
962  setEditedPoint( NULL );
963  commit.Push( _( "Remove a zone/polygon corner" ) );
964  break;
965  }
966  }
967 
968  updatePoints();
969 
970 
971  return 0;
972 }
973 
974 
976 {
977  updatePoints();
978  return 0;
979 }
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:212
BOARD_ITEM_CONTAINER * GetParent() const
Class ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:78
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
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition: class_zone.h:480
void updateItem() const
Updates item'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:245
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
bool IsFilled() const
Definition: class_zone.h:215
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:342
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:262
virtual void SetSnapping(bool aEnabled)
Function SetSnapping() Enables/disables snapping cursor to grid.
double RAD2DECIDEG(double rad)
Definition: trigo.h:204
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:108
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
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.
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:213
static TOOL_ACTION pointEditorRemoveCorner
Removes a corner.
Definition: pcb_actions.h:216
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)
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()
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:1385
class DIMENSION, a dimension (graphic item)
Definition: typeinfo.h:100
virtual void Move(const wxPoint &aMoveVector) override
Function Move move this object.
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:354
Definition: seg.h:36
virtual VECTOR2I GetPosition() const
Function GetPosition()
Definition: edit_points.h:65
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.
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's...
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:165
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's points.
void SetEnd(const wxPoint &aEnd)
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:312
bool Init() override
Function Init() Init() is called once upon a registration of the tool.
void finishItem() const
Applies the last changes to the edited item.
Class EDIT_POINT.
Definition: edit_points.h:46
Module description (excepted pads)
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:185
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:71
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:587
static bool addCornerCondition(const SELECTION &aSelection)
Condition to display "Create corner" context menu entry.
OPT< TOOL_EVENT > OPT_TOOL_EVENT
Definition: tool_event.h:460
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)
virtual void SetPosition(const VECTOR2I &aPosition)
Function SetPosition()
Definition: edit_points.h:97