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 #include <status_popup.h>
40 
41 #include <pcb_edit_frame.h>
42 #include <class_edge_mod.h>
43 #include <class_dimension.h>
44 #include <class_zone.h>
45 #include <class_board.h>
46 #include <class_module.h>
47 #include <connectivity_data.h>
48 
49 #include "zone_filler.h"
50 
51 // Point editor
52 TOOL_ACTION PCB_ACTIONS::pointEditorAddCorner( "pcbnew.PointEditor.addCorner",
53  AS_GLOBAL, 0,
54  _( "Create Corner" ), _( "Create a corner" ), add_corner_xpm );
55 
56 TOOL_ACTION PCB_ACTIONS::pointEditorRemoveCorner( "pcbnew.PointEditor.removeCorner",
57  AS_GLOBAL, 0,
58  _( "Remove Corner" ), _( "Remove corner" ), delete_xpm );
59 
60 
61 // Few constants to avoid using bare numbers for point indices
63 {
65 };
66 
68 {
70 };
71 
73 {
75 };
76 
78 {
83 };
84 
86 {
87 private:
88 
89  static void buildForPolyOutline( std::shared_ptr<EDIT_POINTS> points, const SHAPE_POLY_SET* aOutline, KIGFX::GAL* aGal )
90  {
91 
92  int cornersCount = aOutline->TotalVertices();
93 
94  for( auto iterator = aOutline->CIterateWithHoles(); iterator; iterator++ )
95  {
96  points->AddPoint( *iterator );
97 
98  if( iterator.IsEndContour() )
99  points->AddBreak();
100  }
101 
102  // Lines have to be added after creating edit points,
103  // as they use EDIT_POINT references
104  for( int i = 0; i < cornersCount - 1; ++i )
105  {
106  if( points->IsContourEnd( i ) )
107  {
108  points->AddLine( points->Point( i ),
109  points->Point( points->GetContourStartIdx( i ) ) );
110  }
111  else
112  {
113  points->AddLine( points->Point( i ), points->Point( i + 1 ) );
114  }
115 
116  points->Line( i ).SetConstraint( new EC_SNAPLINE( points->Line( i ),
117  std::bind( &KIGFX::GAL::GetGridPoint, aGal, _1 ) ) );
118  }
119 
120  // The last missing line, connecting the last and the first polygon point
121  points->AddLine( points->Point( cornersCount - 1 ),
122  points->Point( points->GetContourStartIdx( cornersCount - 1 ) ) );
123 
124  points->Line( points->LinesSize() - 1 ).SetConstraint(
125  new EC_SNAPLINE( points->Line( points->LinesSize() - 1 ),
126  std::bind( &KIGFX::GAL::GetGridPoint, aGal, _1 ) ) );
127  }
128 
129 public:
130  static std::shared_ptr<EDIT_POINTS> Make( EDA_ITEM* aItem, KIGFX::GAL* aGal )
131  {
132  std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
133 
134  if( !aItem )
135  return points;
136 
137  // Generate list of edit points basing on the item type
138  switch( aItem->Type() )
139  {
140  case PCB_LINE_T:
141  case PCB_MODULE_EDGE_T:
142  {
143  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( aItem );
144 
145  switch( segment->GetShape() )
146  {
147  case S_SEGMENT:
148  points->AddPoint( segment->GetStart() );
149  points->AddPoint( segment->GetEnd() );
150  break;
151 
152  case S_ARC:
153  points->AddPoint( segment->GetCenter() );
154  points->AddPoint( segment->GetArcStart() );
155  points->AddPoint( segment->GetArcEnd() );
156 
157  // Set constraints
158  // Arc end has to stay at the same radius as the start
159  points->Point( ARC_END ).SetConstraint( new EC_CIRCLE( points->Point( ARC_END ),
160  points->Point( ARC_CENTER ),
161  points->Point( ARC_START ) ) );
162  break;
163 
164  case S_CIRCLE:
165  points->AddPoint( segment->GetCenter() );
166  points->AddPoint( segment->GetEnd() );
167  break;
168 
169  case S_POLYGON:
170  {
171  buildForPolyOutline( points, &segment->GetPolyShape(), aGal );
172  break;
173  }
174 
175  default: // suppress warnings
176  break;
177  }
178 
179  break;
180  }
181 
182  case PCB_ZONE_AREA_T:
183  {
184  auto zone = static_cast<const ZONE_CONTAINER*>( aItem );
185  buildForPolyOutline( points, zone->Outline(), aGal );
186  break;
187  }
188 
189  case PCB_DIMENSION_T:
190  {
191  const DIMENSION* dimension = static_cast<const DIMENSION*>( aItem );
192 
193  points->AddPoint( dimension->m_crossBarO );
194  points->AddPoint( dimension->m_crossBarF );
195  points->AddPoint( dimension->m_featureLineGO );
196  points->AddPoint( dimension->m_featureLineDO );
197 
198  // Dimension height setting - edit points should move only along the feature lines
199  points->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARO ),
200  points->Point( DIM_FEATUREGO ) ) );
201  points->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARF ),
202  points->Point( DIM_FEATUREDO ) ) );
203 
204  break;
205  }
206 
207  default:
208  points.reset();
209  break;
210  }
211 
212  return points;
213  }
214 
215 private:
217 };
218 
219 
221  PCB_TOOL( "pcbnew.PointEditor" ), m_selectionTool( NULL ), m_editedPoint( NULL ),
222  m_original( VECTOR2I( 0, 0 ) ), m_altConstrainer( VECTOR2I( 0, 0 ) )
223 {
224 }
225 
226 
228 {
229  m_refill = false;
230  m_editPoints.reset();
231  m_altConstraint.reset();
232  getViewControls()->SetAutoPan( false );
233 
234  m_statusPopup.reset( new STATUS_TEXT_POPUP( getEditFrame<PCB_BASE_EDIT_FRAME>() ) );
235  m_statusPopup->SetTextColor( wxColour( 255, 0, 0 ) );
236  m_statusPopup->SetText( _( "Self-intersecting polygons are not allowed" ) );
237 }
238 
239 
241 {
242  // Find the selection tool, so they can cooperate
243  m_selectionTool = static_cast<SELECTION_TOOL*>( m_toolMgr->FindTool( "pcbnew.InteractiveSelection" ) );
244 
245  if( !m_selectionTool )
246  {
247  DisplayError( NULL, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
248  return false;
249  }
250 
251  auto& menu = m_selectionTool->GetToolMenu().GetMenu();
254  std::bind( &POINT_EDITOR::removeCornerCondition, this, _1 ) );
255 
256  return true;
257 }
258 
259 
261 {
262  EDIT_POINT* point = m_editedPoint;
263 
264  if( aEvent.IsMotion() )
265  {
266  point = m_editPoints->FindPoint( aEvent.Position(), getView() );
267  }
268  else if( aEvent.IsDrag( BUT_LEFT ) )
269  {
270  point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
271  }
272 
273  if( m_editedPoint != point )
274  setEditedPoint( point );
275 }
276 
277 
279 {
281 
282  if( selection.Size() != 1 )
283  return 0;
284 
285  Activate();
286 
288  KIGFX::VIEW* view = getView();
289  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
290  auto item = selection.Front();
291 
292  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
293 
294  if( !m_editPoints )
295  return 0;
296 
297  view->Add( m_editPoints.get() );
298  setEditedPoint( nullptr );
299  m_refill = false;
300  bool modified = false;
301  bool revert = false;
302 
303  BOARD_COMMIT commit( editFrame );
304 
305  // Main loop: keep receiving events
306  while( OPT_TOOL_EVENT evt = Wait() )
307  {
308  if( revert )
309  break;
310 
311  if( !m_editPoints ||
312  evt->Matches( m_selectionTool->ClearedEvent ) ||
313  evt->Matches( m_selectionTool->UnselectedEvent ) ||
314  evt->Matches( m_selectionTool->SelectedEvent ) )
315  {
316  break;
317  }
318 
319  if ( !modified )
320  updateEditedPoint( *evt );
321 
322  if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
323  {
324  if( !modified )
325  {
326  commit.StageItems( selection, CHT_MODIFY );
327 
328  controls->ForceCursorPosition( false );
329  m_original = *m_editedPoint; // Save the original position
330  controls->SetAutoPan( true );
331  modified = true;
332  }
333 
334  bool enableAltConstraint = !!evt->Modifier( MD_CTRL );
335 
336  if( enableAltConstraint != (bool) m_altConstraint ) // alternative constraint
337  setAltConstraint( enableAltConstraint );
338 
340 
341  if( m_altConstraint )
342  m_altConstraint->Apply();
343  else
345 
346  updateItem();
347  updatePoints();
348  }
349 
350  else if( evt->IsMouseUp( BUT_LEFT ) )
351  {
352  controls->SetAutoPan( false );
353  setAltConstraint( false );
354 
355  if( modified )
356  {
357  commit.Push( _( "Drag a corner" ) );
358  modified = false;
359  m_refill = true;
360  }
361 
362  m_toolMgr->PassEvent();
363  }
364 
365  else if( evt->IsCancel() )
366  {
367  if( modified ) // Restore the last change
368  revert = true;
369 
370  // Let the selection tool receive the event too
371  m_toolMgr->PassEvent();
372 
373  // Do not exit right now, let the selection clear the selection
374  //break;
375  }
376 
377  else
378  {
379  m_toolMgr->PassEvent();
380  }
381  }
382 
383  if( m_editPoints )
384  {
385  view->Remove( m_editPoints.get() );
386 
387  if( modified && revert )
388  commit.Revert();
389 
390  finishItem();
391  m_editPoints.reset();
392  }
393 
394  frame()->SetMsgPanel( board() );
395 
396  return 0;
397 }
398 
399 
401 {
402  EDA_ITEM* item = m_editPoints->GetParent();
403 
404  if( !item )
405  return;
406 
407  switch( item->Type() )
408  {
409  case PCB_LINE_T:
410  case PCB_MODULE_EDGE_T:
411  {
412  DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item );
413  switch( segment->GetShape() )
414  {
415  case S_SEGMENT:
416  if( isModified( m_editPoints->Point( SEG_START ) ) )
417  segment->SetStart( wxPoint( m_editPoints->Point( SEG_START ).GetPosition().x,
418  m_editPoints->Point( SEG_START ).GetPosition().y ) );
419 
420  else if( isModified( m_editPoints->Point( SEG_END ) ) )
421  segment->SetEnd( wxPoint( m_editPoints->Point( SEG_END ).GetPosition().x,
422  m_editPoints->Point( SEG_END ).GetPosition().y ) );
423 
424  break;
425 
426  case S_ARC:
427  {
428  const VECTOR2I& center = m_editPoints->Point( ARC_CENTER ).GetPosition();
429  const VECTOR2I& start = m_editPoints->Point( ARC_START ).GetPosition();
430  const VECTOR2I& end = m_editPoints->Point( ARC_END ).GetPosition();
431 
432  if( center != segment->GetCenter() )
433  {
434  wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
435  segment->Move( moveVector );
436 
437  m_editPoints->Point( ARC_START ).SetPosition( segment->GetArcStart() );
438  m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
439  }
440 
441  else
442  {
443  segment->SetArcStart( wxPoint( start.x, start.y ) );
444 
445  VECTOR2D startLine = start - center;
446  VECTOR2I endLine = end - center;
447  double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
448 
449  // Adjust the new angle to (counter)clockwise setting
450  bool clockwise = ( segment->GetAngle() > 0 );
451 
452  if( clockwise && newAngle < 0.0 )
453  newAngle += 3600.0;
454  else if( !clockwise && newAngle > 0.0 )
455  newAngle -= 3600.0;
456 
457  segment->SetAngle( newAngle );
458  }
459 
460  break;
461  }
462 
463  case S_CIRCLE:
464  {
465  const VECTOR2I& center = m_editPoints->Point( CIRC_CENTER ).GetPosition();
466  const VECTOR2I& end = m_editPoints->Point( CIRC_END ).GetPosition();
467 
468  if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
469  {
470  wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
471  segment->Move( moveVector );
472  }
473  else
474  {
475  segment->SetEnd( wxPoint( end.x, end.y ) );
476  }
477 
478  break;
479  }
480 
481  case S_POLYGON:
482  {
483  SHAPE_POLY_SET& outline = segment->GetPolyShape();
484 
485  for( int i = 0; i < outline.TotalVertices(); ++i )
486  outline.Vertex( i ) = m_editPoints->Point( i ).GetPosition();
487 
488  validatePolygon( outline );
489  break;
490  }
491 
492  default: // suppress warnings
493  break;
494  }
495 
496  // Update relative coordinates for module edges
497  if( EDGE_MODULE* edge = dyn_cast<EDGE_MODULE*>( item ) )
498  edge->SetLocalCoord();
499 
500  break;
501  }
502 
503  case PCB_ZONE_AREA_T:
504  {
505  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
506  zone->ClearFilledPolysList();
507  SHAPE_POLY_SET& outline = *zone->Outline();
508 
509  for( int i = 0; i < outline.TotalVertices(); ++i )
510  outline.Vertex( i ) = m_editPoints->Point( i ).GetPosition();
511 
512  validatePolygon( outline );
513  zone->Hatch();
514  break;
515  }
516 
517  case PCB_DIMENSION_T:
518  {
519  DIMENSION* dimension = static_cast<DIMENSION*>( item );
520 
521  // Check which point is currently modified and updated dimension's points respectively
522  if( isModified( m_editPoints->Point( DIM_CROSSBARO ) ) )
523  {
524  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetOrigin() );
525  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() );
526 
527  if( featureLine.Cross( crossBar ) > 0 )
528  dimension->SetHeight( -featureLine.EuclideanNorm() );
529  else
530  dimension->SetHeight( featureLine.EuclideanNorm() );
531  }
532 
533  else if( isModified( m_editPoints->Point( DIM_CROSSBARF ) ) )
534  {
535  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
536  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() );
537 
538  if( featureLine.Cross( crossBar ) > 0 )
539  dimension->SetHeight( -featureLine.EuclideanNorm() );
540  else
541  dimension->SetHeight( featureLine.EuclideanNorm() );
542  }
543 
544  else if( isModified( m_editPoints->Point( DIM_FEATUREGO ) ) )
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  else if( isModified( m_editPoints->Point( DIM_FEATUREDO ) ) )
554  {
556  m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ),
557  m_editPoints->Point( DIM_FEATUREGO ) ) );
558  m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ),
559  m_editPoints->Point( DIM_FEATUREDO ) ) );
560  }
561 
562  break;
563  }
564 
565  default:
566  break;
567  }
568 
569  if( frame() )
570  frame()->SetMsgPanel( item );
571 }
572 
573 
575 {
576  auto item = m_editPoints->GetParent();
577 
578  if( !item )
579  return;
580 
581  if( item->Type() == PCB_ZONE_AREA_T )
582  {
583  auto zone = static_cast<ZONE_CONTAINER*>( item );
584 
585  if( zone->IsFilled() && m_refill )
586  {
587  ZONE_FILLER filler( board() );
588  filler.Fill( { zone } );
589  }
590  }
591 }
592 
593 
594 bool POINT_EDITOR::validatePolygon( SHAPE_POLY_SET& aModified, const SHAPE_POLY_SET* aOriginal ) const
595 {
596  if( !aModified.IsSelfIntersecting() )
597  {
598  m_statusPopup->Hide();
599  return true;
600  }
601 
602  if( m_statusPopup )
603  {
604  wxPoint p = wxGetMousePosition() + wxPoint( 20, 20 );
605  m_statusPopup->Move( p );
606  m_statusPopup->Popup( getEditFrame<PCB_BASE_FRAME>() );
607  m_statusPopup->Expire( 1500 );
608  }
609 
610  if( aOriginal )
611  aModified = *aOriginal;
612 
613  return false;
614 }
615 
616 
618 {
619  if( !m_editPoints )
620  return;
621 
622  EDA_ITEM* item = m_editPoints->GetParent();
623 
624  if( !item )
625  return;
626 
627  switch( item->Type() )
628  {
629  case PCB_LINE_T:
630  case PCB_MODULE_EDGE_T:
631  {
632  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( item );
633 
634  switch( segment->GetShape() )
635  {
636  case S_SEGMENT:
637  m_editPoints->Point( SEG_START ).SetPosition( segment->GetStart() );
638  m_editPoints->Point( SEG_END ).SetPosition( segment->GetEnd() );
639  break;
640 
641  case S_ARC:
642  m_editPoints->Point( ARC_CENTER ).SetPosition( segment->GetCenter() );
643  m_editPoints->Point( ARC_START).SetPosition( segment->GetArcStart() );
644  m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
645  break;
646 
647  case S_CIRCLE:
648  m_editPoints->Point( CIRC_CENTER ).SetPosition( segment->GetCenter() );
649  m_editPoints->Point( CIRC_END ).SetPosition( segment->GetEnd() );
650  break;
651 
652  case S_POLYGON:
653  {
654  const auto& points = segment->BuildPolyPointsList();
655  for( unsigned i = 0; i < points.size(); i++ )
656  {
657  m_editPoints->Point( i ).SetPosition( points[i] );
658  }
659  break;
660  }
661 
662  default: // suppress warnings
663  break;
664  }
665 
666  break;
667  }
668 
669  case PCB_ZONE_AREA_T:
670  {
671  const ZONE_CONTAINER* zone = static_cast<const ZONE_CONTAINER*>( item );
672  const SHAPE_POLY_SET* outline = zone->Outline();
673 
674  if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
675  {
676  getView()->Remove( m_editPoints.get() );
677  m_editedPoint = nullptr;
678  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
679  getView()->Add( m_editPoints.get() );
680  }
681  else
682  {
683  for( int i = 0; i < outline->TotalVertices(); ++i )
684  m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
685  }
686 
687  break;
688  }
689 
690  case PCB_DIMENSION_T:
691  {
692  const DIMENSION* dimension = static_cast<const DIMENSION*>( item );
693 
694  m_editPoints->Point( DIM_CROSSBARO ).SetPosition( dimension->m_crossBarO );
695  m_editPoints->Point( DIM_CROSSBARF ).SetPosition( dimension->m_crossBarF );
696  m_editPoints->Point( DIM_FEATUREGO ).SetPosition( dimension->m_featureLineGO );
697  m_editPoints->Point( DIM_FEATUREDO ).SetPosition( dimension->m_featureLineDO );
698  break;
699  }
700 
701  default:
702  break;
703  }
704 
705  getView()->Update( m_editPoints.get() );
706 }
707 
708 
710 {
712 
713  if( aPoint )
714  {
715  controls->ForceCursorPosition( true, aPoint->GetPosition() );
716  controls->ShowCursor( true );
717  }
718  else
719  {
720  controls->ShowCursor( false );
721  controls->ForceCursorPosition( false );
722  }
723 
724  m_editedPoint = aPoint;
725 }
726 
727 
728 void POINT_EDITOR::setAltConstraint( bool aEnabled )
729 {
730  if( aEnabled )
731  {
732  EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
733 
734  if( line )
735  {
736  if( m_editPoints->GetParent()->Type() == PCB_ZONE_AREA_T )
738  }
739  else
740  {
741  // Find a proper constraining point for 45 degrees mode
744  }
745  }
746  else
747  {
748  m_altConstraint.reset();
749  }
750 }
751 
752 
754 {
755  EDA_ITEM* item = m_editPoints->GetParent();
756 
757  switch( item->Type() )
758  {
759  case PCB_LINE_T:
760  case PCB_MODULE_EDGE_T:
761  {
762  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( item );
763  {
764  switch( segment->GetShape() )
765  {
766  case S_SEGMENT:
767  return *( m_editPoints->Next( *m_editedPoint ) ); // select the other end of line
768 
769  case S_ARC:
770  case S_CIRCLE:
771  return m_editPoints->Point( CIRC_CENTER );
772 
773  default: // suppress warnings
774  break;
775  }
776  }
777 
778  break;
779  }
780 
781  case PCB_DIMENSION_T:
782  {
783  // Constraint for crossbar
784  if( isModified( m_editPoints->Point( DIM_FEATUREGO ) ) )
785  return m_editPoints->Point( DIM_FEATUREDO );
786 
787  else if( isModified( m_editPoints->Point( DIM_FEATUREDO ) ) )
788  return m_editPoints->Point( DIM_FEATUREGO );
789 
790  else
791  return EDIT_POINT( m_editedPoint->GetPosition() ); // no constraint
792 
793  break;
794  }
795 
796  default:
797  break;
798  }
799 
800  // In any other case we may align item to its original position
801  return m_original;
802 }
803 
804 
806 {
812 }
813 
814 
816 {
817  if( aSelection.Size() != 1 )
818  return false;
819 
820  auto item = aSelection.Front();
821 
822  // Works only for zones and line segments
823  return item->Type() == PCB_ZONE_AREA_T ||
824  ( ( item->Type() == PCB_LINE_T || item->Type() == PCB_MODULE_EDGE_T ) &&
825  static_cast<DRAWSEGMENT*>( item )->GetShape() == S_SEGMENT );
826 }
827 
828 
829 // Finds a corresponding vertex in a polygon set
830 static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
831 findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
832 {
833  for( auto it = aPolySet.IterateWithHoles(); it; ++it )
834  {
835  auto vertexIdx = it.GetIndex();
836 
837  if( aPolySet.Vertex( vertexIdx ) == aPoint.GetPosition() )
838  return std::make_pair( true, vertexIdx );
839  }
840 
841  return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
842 }
843 
844 
846 {
847  if( !m_editPoints || !m_editedPoint )
848  return false;
849 
850  EDA_ITEM* item = m_editPoints->GetParent();
851 
852  if( !item || item->Type() != PCB_ZONE_AREA_T )
853  return false;
854 
855  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
856  auto& polyset = *zone->Outline();
857  auto vertex = findVertex( polyset, *m_editedPoint );
858 
859  if( !vertex.first )
860  return false;
861 
862  const auto& vertexIdx = vertex.second;
863 
864  // Check if there are enough vertices so one can be removed without
865  // degenerating the polygon.
866  // The first condition allows to remove all corners from holes (when there
867  // are only 2 vertices left, a hole is removed).
868  if( vertexIdx.m_contour == 0 && polyset.Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
869  return false;
870 
871  // Remove corner does not work with lines
872  if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
873  return false;
874 
875  return m_editedPoint != NULL;
876 }
877 
878 
880 {
881  EDA_ITEM* item = m_editPoints->GetParent();
882  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
883  const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
884  BOARD_COMMIT commit( frame );
885 
886  if( item->Type() == PCB_ZONE_AREA_T )
887  {
888  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
889  SHAPE_POLY_SET* zoneOutline = zone->Outline();
890 
891  commit.Modify( zone );
892 
893  unsigned int nearestIdx = 0;
894  unsigned int nextNearestIdx = 0;
895  unsigned int nearestDist = INT_MAX;
896  unsigned int firstPointInContour = 0;
897 
898  // Search the best outline segment to add a new corner
899  // and therefore break this segment into two segments
900 
901  // Object to iterate through the corners of the outlines (main contour and its holes)
902  SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0,
903  zoneOutline->OutlineCount()-1, /* IterateHoles */ true );
904  int curr_idx = 0;
905 
906  // Iterate through all the corners of the outlines and search the best segment
907  for( ; iterator; iterator++, curr_idx++ )
908  {
909  int jj = curr_idx+1;
910 
911  if( iterator.IsEndContour() )
912  { // We reach the last point of the current contour (main or hole)
913  jj = firstPointInContour;
914  firstPointInContour = curr_idx+1; // Prepare next contour analysis
915  }
916 
917  SEG curr_segment( zoneOutline->Vertex( curr_idx ), zoneOutline->Vertex( jj ) );
918 
919  unsigned int distance = curr_segment.Distance( cursorPos );
920 
921  if( distance < nearestDist )
922  {
923  nearestDist = distance;
924  nearestIdx = curr_idx;
925  nextNearestIdx = jj;
926  }
927  }
928 
929 
930  // Find the point on the closest segment
931  VECTOR2I sideOrigin = zoneOutline->Vertex( nearestIdx );
932  VECTOR2I sideEnd = zoneOutline->Vertex( nextNearestIdx );
933  SEG nearestSide( sideOrigin, sideEnd );
934  VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
935 
936  // Do not add points that have the same coordinates as ones that already belong to polygon
937  // instead, add a point in the middle of the side
938  if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
939  nearestPoint = ( sideOrigin + sideEnd ) / 2;
940 
941  // Add corner between nearestIdx and nextNearestIdx:
942  zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
943  zone->Hatch();
944 
945  commit.Push( _( "Add a zone corner" ) );
946  }
947 
948  else if( item->Type() == PCB_LINE_T || item->Type() == PCB_MODULE_EDGE_T )
949  {
950  bool moduleEdge = item->Type() == PCB_MODULE_EDGE_T;
951 
952  DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item );
953 
954  if( segment->GetShape() == S_SEGMENT )
955  {
956  commit.Modify( segment );
957 
958  SEG seg( segment->GetStart(), segment->GetEnd() );
959  VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
960 
961  // Move the end of the line to the break point..
962  segment->SetEnd( wxPoint( nearestPoint.x, nearestPoint.y ) );
963 
964  // and add another one starting from the break point
965  DRAWSEGMENT* newSegment;
966 
967  if( moduleEdge )
968  {
969  EDGE_MODULE* edge = static_cast<EDGE_MODULE*>( segment );
970  assert( edge->Type() == PCB_MODULE_EDGE_T );
971  assert( edge->GetParent()->Type() == PCB_MODULE_T );
972  newSegment = new EDGE_MODULE( *edge );
973  }
974  else
975  {
976  newSegment = new DRAWSEGMENT( *segment );
977  }
978 
979  newSegment->ClearSelected();
980  newSegment->SetStart( wxPoint( nearestPoint.x, nearestPoint.y ) );
981  newSegment->SetEnd( wxPoint( seg.B.x, seg.B.y ) );
982 
983  commit.Add( newSegment );
984  commit.Push( _( "Split segment" ) );
985  }
986  }
987 
988  updatePoints();
989  return 0;
990 }
991 
992 
994 {
995  if( !m_editedPoint )
996  return 0;
997 
998  EDA_ITEM* item = m_editPoints->GetParent();
999 
1000  if( !item )
1001  return 0;
1002 
1003  SHAPE_POLY_SET* polygon = nullptr;
1004 
1005  if( item->Type() == PCB_ZONE_AREA_T)
1006  {
1007  auto zone = static_cast<ZONE_CONTAINER*>( item );
1008  polygon = zone->Outline();
1009  }
1010  else if( item->Type() == PCB_LINE_T )
1011  {
1012  auto ds = static_cast<DRAWSEGMENT*>( item );
1013 
1014  if( ds->GetShape() == S_POLYGON )
1015  polygon = &ds->GetPolyShape();
1016  }
1017 
1018  if( !polygon )
1019  return 0;
1020 
1021  PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
1022  BOARD_COMMIT commit( frame );
1023  auto vertex = findVertex( *polygon, *m_editedPoint );
1024 
1025  if( vertex.first )
1026  {
1027  const auto& vertexIdx = vertex.second;
1028  auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
1029  bool valid = true;
1030 
1031  if( outline.PointCount() > 3 )
1032  {
1033  // the usual case: remove just the corner when there are >3 vertices
1034  commit.Modify( item );
1035  polygon->RemoveVertex( vertexIdx );
1036  valid = validatePolygon( *polygon );
1037  }
1038  else
1039  {
1040  // either remove a hole or the polygon when there are <= 3 corners
1041  if( vertexIdx.m_contour > 0 )
1042  {
1043  // remove hole
1044  commit.Modify( item );
1045  polygon->RemoveContour( vertexIdx.m_contour );
1046  }
1047  else
1048  {
1050  commit.Remove( item );
1051  }
1052  }
1053 
1054  setEditedPoint( nullptr );
1055 
1056  if( valid )
1057  commit.Push( _( "Remove a zone/polygon corner" ) );
1058  else
1059  commit.Revert();
1060 
1061  // Refresh zone hatching
1062  if( item->Type() == PCB_ZONE_AREA_T)
1063  static_cast<ZONE_CONTAINER*>( item )->Hatch();
1064 
1065  updatePoints();
1066  }
1067 
1068  return 0;
1069 }
1070 
1071 
1073 {
1074  m_refill = true; // zone has been modified outside the point editor tool
1075  updatePoints();
1076  return 0;
1077 }
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:209
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:242
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.
void Fill(std::vector< ZONE_CONTAINER * > aZones, bool aCheck=false)
Definition: zone_filler.cpp:84
Class BOARD to handle a board.
Class STATUS_TEXT_POPUP.
Definition: status_popup.h:74
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:147
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:364
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
std::unique_ptr< STATUS_TEXT_POPUP > m_statusPopup
Definition: point_editor.h:86
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.
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:75
void finishItem()
Applies the last changes to the edited item.
KIGFX::VIEW_CONTROLS * controls() const
Definition: pcb_tool.h:134
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.
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Function SetMsgPanel clears the message panel and populates it with the contents of aList...
Definition: draw_frame.cpp:832
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:135
Class TOOL_EVENT.
Definition: tool_event.h:162
std::shared_ptr< EDIT_CONSTRAINT< EDIT_POINT > > m_altConstraint
Definition: point_editor.h:78
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:112
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:219
SELECTION_TOOL * m_selectionTool
Selection tool used for obtaining selected items
Definition: point_editor.h:66
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:1499
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:370
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.
KIGFX::PCB_VIEW * view() const
Definition: pcb_tool.h:133
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
size_t i
Definition: json11.cpp:597
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:162
void removeCorner(EDIT_POINT *aPoint)
Removes a corner.
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true) override
Executes the changes.
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:72
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:115
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Function Add() Adds a VIEW_ITEM to the view.
Definition: view.cpp:334
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:81
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:69
bool IsSelfIntersecting()
Function IsSelfIntersecting Checks whether any of the polygons in the set is self intersecting...
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:185
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.
bool validatePolygon(SHAPE_POLY_SET &aModified, const SHAPE_POLY_SET *aOriginal=nullptr) const
Validates a polygon and restores it to its original version if available.
static std::shared_ptr< EDIT_POINTS > Make(EDA_ITEM *aItem, KIGFX::GAL *aGal)
BOARD * board() const
Definition: pcb_tool.h:136
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