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-2019 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 #include <memory>
27 using namespace std::placeholders;
28 #include <tool/tool_manager.h>
29 #include <view/view_controls.h>
31 #include <geometry/seg.h>
32 #include <confirm.h>
33 #include "pcb_actions.h"
34 #include "selection_tool.h"
35 #include "point_editor.h"
36 #include "grid_helper.h"
37 #include <board_commit.h>
38 #include <bitmaps.h>
39 #include <status_popup.h>
40 #include <pcb_edit_frame.h>
41 #include <class_edge_mod.h>
42 #include <class_dimension.h>
43 #include <class_zone.h>
46 
47 // Few constants to avoid using bare numbers for point indices
49 {
51 };
52 
54 {
56 };
57 
59 {
61 };
62 
64 {
66 };
67 
69 {
74 };
75 
77 {
82 };
83 
85 {
86 private:
87 
88  static void buildForPolyOutline( std::shared_ptr<EDIT_POINTS> points,
89  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_RECT:
153  points->AddPoint( segment->GetStart() );
154  points->AddPoint( wxPoint( segment->GetEnd().x, segment->GetStart().y ) );
155  points->AddPoint( segment->GetEnd() );
156  points->AddPoint( wxPoint( segment->GetStart().x, segment->GetEnd().y ) );
157  break;
158 
159  case S_ARC:
160  points->AddPoint( segment->GetCenter() );
161  points->AddPoint( segment->GetArcStart() );
162  points->AddPoint( segment->GetArcMid() );
163  points->AddPoint( segment->GetArcEnd() );
164 
165  // Set constraints
166  // Arc end has to stay at the same radius as the start
167  points->Point( ARC_END ).SetConstraint( new EC_CIRCLE( points->Point( ARC_END ),
168  points->Point( ARC_CENTER ),
169  points->Point( ARC_START ) ) );
170 
171  points->Point( ARC_MID ).SetConstraint( new EC_LINE( points->Point( ARC_MID ),
172  points->Point( ARC_CENTER ) ) );
173  break;
174 
175  case S_CIRCLE:
176  points->AddPoint( segment->GetCenter() );
177  points->AddPoint( segment->GetEnd() );
178  break;
179 
180  case S_POLYGON:
181  buildForPolyOutline( points, &segment->GetPolyShape(), aGal );
182  break;
183 
184  case S_CURVE:
185  points->AddPoint( segment->GetStart() );
186  points->AddPoint( segment->GetBezControl1() );
187  points->AddPoint( segment->GetBezControl2() );
188  points->AddPoint( segment->GetEnd() );
189  break;
190 
191  default: // suppress warnings
192  break;
193  }
194 
195  break;
196  }
197 
198  case PCB_PAD_T:
199  {
200  const D_PAD* pad = static_cast<const D_PAD*>( aItem );
201  wxPoint shapePos = pad->ShapePos();
202  wxPoint halfSize( pad->GetSize().x / 2, pad->GetSize().y / 2 );
203 
204  if( pad->GetParent() && pad->GetParent()->PadsLocked() )
205  break;
206 
207  switch( pad->GetShape() )
208  {
209  case PAD_SHAPE_CIRCLE:
210  points->AddPoint( shapePos );
211  points->AddPoint( wxPoint( shapePos.x + halfSize.x, shapePos.y ) );
212  break;
213 
214  case PAD_SHAPE_OVAL:
215  case PAD_SHAPE_TRAPEZOID:
216  case PAD_SHAPE_RECT:
217  case PAD_SHAPE_ROUNDRECT:
219  {
220  if( (int) pad->GetOrientation() % 900 != 0 )
221  break;
222 
223  if( pad->GetOrientation() == 900 || pad->GetOrientation() == 2700 )
224  std::swap( halfSize.x, halfSize.y );
225 
226  points->AddPoint( shapePos - halfSize );
227  points->AddPoint( wxPoint( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
228  points->AddPoint( shapePos + halfSize );
229  points->AddPoint( wxPoint( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
230  }
231  break;
232 
233  default: // suppress warnings
234  break;
235  }
236  }
237  break;
238 
240  case PCB_ZONE_AREA_T:
241  {
242  auto zone = static_cast<const ZONE_CONTAINER*>( aItem );
243  buildForPolyOutline( points, zone->Outline(), aGal );
244  }
245  break;
246 
247  case PCB_DIMENSION_T:
248  {
249  const DIMENSION* dimension = static_cast<const DIMENSION*>( aItem );
250 
251  points->AddPoint( dimension->m_crossBarO );
252  points->AddPoint( dimension->m_crossBarF );
253  points->AddPoint( dimension->m_featureLineGO );
254  points->AddPoint( dimension->m_featureLineDO );
255 
256  // Dimension height setting - edit points should move only along the feature lines
257  points->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARO ),
258  points->Point( DIM_FEATUREGO ) ) );
259  points->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARF ),
260  points->Point( DIM_FEATUREDO ) ) );
261  }
262  break;
263 
264  default:
265  points.reset();
266  break;
267  }
268 
269  return points;
270  }
271 
272 private:
274 };
275 
276 
278  PCB_TOOL_BASE( "pcbnew.PointEditor" ),
279  m_selectionTool( NULL ),
280  m_editedPoint( NULL ),
281  m_original( VECTOR2I( 0, 0 ) ),
282  m_altConstrainer( VECTOR2I( 0, 0 ) ),
283  m_refill( false )
284 {
285 }
286 
287 
289 {
290  m_refill = false;
291  m_editPoints.reset();
292  m_altConstraint.reset();
293  getViewControls()->SetAutoPan( false );
294 
295  m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( getEditFrame<PCB_BASE_EDIT_FRAME>() );
296  m_statusPopup->SetTextColor( wxColour( 255, 0, 0 ) );
297  m_statusPopup->SetText( _( "Self-intersecting polygons are not allowed." ) );
298 }
299 
300 
302 {
303  // Find the selection tool, so they can cooperate
305 
306  wxASSERT_MSG( m_selectionTool, _( "pcbnew.InteractiveSelection tool is not available" ) );
307 
308  auto& menu = m_selectionTool->GetToolMenu().GetMenu();
311  std::bind( &POINT_EDITOR::removeCornerCondition, this, _1 ) );
312 
313  return true;
314 }
315 
316 
318 {
319  EDIT_POINT* point;
320 
321  if( aEvent.IsMotion() )
322  {
323  point = m_editPoints->FindPoint( aEvent.Position(), getView() );
324  }
325  else if( aEvent.IsDrag( BUT_LEFT ) )
326  {
327  point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
328  }
329  else
330  {
331  point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
332  }
333 
334  if( m_editedPoint != point )
335  setEditedPoint( point );
336 }
337 
338 
340 {
341  if( !m_selectionTool )
342  return 0;
343 
345 
346  if( selection.Size() != 1 || selection.Front()->GetEditFlags() )
347  return 0;
348 
349  Activate();
350 
352  KIGFX::VIEW* view = getView();
353  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
354 
355  controls->ShowCursor( true );
356 
357  GRID_HELPER grid( m_toolMgr, editFrame->GetMagneticItemsSettings() );
358  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
359 
360  if( !item )
361  return 0;
362 
363  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
364 
365  if( !m_editPoints )
366  return 0;
367 
368  view->Add( m_editPoints.get() );
369  setEditedPoint( nullptr );
370  updateEditedPoint( aEvent );
371  m_refill = false;
372  bool inDrag = false;
373 
374  BOARD_COMMIT commit( editFrame );
375  LSET snapLayers = item->GetLayerSet();
376 
377  if( item->Type() == PCB_DIMENSION_T )
378  snapLayers = LSET::AllLayersMask();
379 
380  // Main loop: keep receiving events
381  while( TOOL_EVENT* evt = Wait() )
382  {
383  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
384  grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
385  controls->SetSnapping( !evt->Modifier( MD_ALT ) );
386 
387  if( !m_editPoints || evt->IsSelectionEvent() )
388  break;
389 
390  if ( !inDrag )
391  updateEditedPoint( *evt );
392 
393  if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
394  {
395  if( !inDrag )
396  {
397  commit.StageItems( selection, CHT_MODIFY );
398 
399  controls->ForceCursorPosition( false );
400  m_original = *m_editedPoint; // Save the original position
401  controls->SetAutoPan( true );
402  inDrag = true;
403  grid.SetAuxAxes( true, m_original.GetPosition() );
405  }
406 
407  //TODO: unify the constraints to solve simultaneously instead of sequentially
408  m_editedPoint->SetPosition( grid.BestSnapAnchor( evt->Position(),
409  snapLayers, { item } ) );
410 
411  // The alternative constraint limits to 45°
412  bool enableAltConstraint = !!evt->Modifier( MD_CTRL );
413 
414  if( enableAltConstraint != (bool) m_altConstraint ) // alternative constraint
415  setAltConstraint( enableAltConstraint );
416 
417  if( m_altConstraint )
418  m_altConstraint->Apply();
419  else
421 
422  m_editedPoint->SetPosition( grid.BestSnapAnchor( m_editedPoint->GetPosition(),
423  snapLayers, { item } ) );
424 
425  updateItem();
426  updatePoints();
427  }
428 
429  else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
430  {
431  if( m_editedPoint )
432  {
433  m_editedPoint->SetActive( false );
434  getView()->Update( m_editPoints.get() );
435  }
436 
437  controls->SetAutoPan( false );
438  setAltConstraint( false );
439 
440  commit.Push( _( "Drag a corner" ) );
441  inDrag = false;
442  m_refill = true;
443  }
444 
445  else if( evt->IsCancelInteractive() || evt->IsActivate() )
446  {
447  if( inDrag ) // Restore the last change
448  commit.Revert();
449  else if( evt->IsCancelInteractive() )
450  break;
451 
452  if( evt->IsActivate() && !evt->IsMoveTool() )
453  break;
454  }
455 
456  else
457  evt->SetPassEvent();
458  }
459 
460  if( m_editPoints )
461  {
462  view->Remove( m_editPoints.get() );
463 
464  finishItem();
465  m_editPoints.reset();
466  }
467 
468  frame()->UpdateMsgPanel();
469 
470  return 0;
471 }
472 
473 
475 {
476  EDA_ITEM* item = m_editPoints->GetParent();
477 
478  const BOARD_DESIGN_SETTINGS& boardSettings = board()->GetDesignSettings();
479 
480  if( !item )
481  return;
482 
483  switch( item->Type() )
484  {
485  case PCB_LINE_T:
486  case PCB_MODULE_EDGE_T:
487  {
488  DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item );
489 
490  switch( segment->GetShape() )
491  {
492  case S_SEGMENT:
493  if( isModified( m_editPoints->Point( SEG_START ) ) )
494  segment->SetStart( wxPoint( m_editPoints->Point( SEG_START ).GetPosition().x,
495  m_editPoints->Point( SEG_START ).GetPosition().y ) );
496 
497  else if( isModified( m_editPoints->Point( SEG_END ) ) )
498  segment->SetEnd( wxPoint( m_editPoints->Point( SEG_END ).GetPosition().x,
499  m_editPoints->Point( SEG_END ).GetPosition().y ) );
500 
501  break;
502 
503  case S_RECT:
504  {
505  if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) ) )
506  {
507  segment->SetStart( (wxPoint) m_editPoints->Point( RECT_TOP_LEFT ).GetPosition() );
508  }
509  else if( isModified( m_editPoints->Point( RECT_TOP_RIGHT ) ) )
510  {
511  segment->SetStartY( m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().y );
512  segment->SetEndX( m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().x );
513  }
514  else if( isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
515  {
516  segment->SetEnd( (wxPoint) m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition() );
517  }
518  else if( isModified( m_editPoints->Point( RECT_BOT_LEFT ) ) )
519  {
520  segment->SetStartX( m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().x );
521  segment->SetEndY( m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().y );
522  }
523  }
524  break;
525 
526  case S_ARC:
527  {
528  VECTOR2I center = m_editPoints->Point( ARC_CENTER ).GetPosition();
529  VECTOR2I mid = m_editPoints->Point( ARC_MID ).GetPosition();
530  VECTOR2I start = m_editPoints->Point( ARC_START ).GetPosition();
531  VECTOR2I end = m_editPoints->Point( ARC_END ).GetPosition();
532 
533  if( center != segment->GetCenter() )
534  {
535  wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
536  segment->Move( moveVector );
537 
538  m_editPoints->Point( ARC_START ).SetPosition( segment->GetArcStart() );
539  m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
540  m_editPoints->Point( ARC_MID ).SetPosition( segment->GetArcMid() );
541  }
542  else
543  {
544  if( mid != segment->GetArcMid() )
545  {
546  center = GetArcCenter( start, mid, end );
547  segment->SetCenter( wxPoint( center.x, center.y ) );
548  m_editPoints->Point( ARC_CENTER ).SetPosition( center );
549  }
550 
551  segment->SetArcStart( wxPoint( start.x, start.y ) );
552 
553  VECTOR2D startLine = start - center;
554  VECTOR2D endLine = end - center;
555  double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
556 
557  // Adjust the new angle to (counter)clockwise setting
558  bool clockwise = ( segment->GetAngle() > 0 );
559 
560  if( clockwise && newAngle < 0.0 )
561  newAngle += 3600.0;
562  else if( !clockwise && newAngle > 0.0 )
563  newAngle -= 3600.0;
564 
565  segment->SetAngle( newAngle );
566  }
567  }
568  break;
569 
570  case S_CIRCLE:
571  {
572  const VECTOR2I& center = m_editPoints->Point( CIRC_CENTER ).GetPosition();
573  const VECTOR2I& end = m_editPoints->Point( CIRC_END ).GetPosition();
574 
575  if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
576  {
577  wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
578  segment->Move( moveVector );
579  }
580  else
581  {
582  segment->SetEnd( wxPoint( end.x, end.y ) );
583  }
584  }
585  break;
586 
587  case S_POLYGON:
588  {
589  SHAPE_POLY_SET& outline = segment->GetPolyShape();
590 
591  for( int i = 0; i < outline.TotalVertices(); ++i )
592  outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
593 
594  validatePolygon( outline );
595  }
596  break;
597 
598  case S_CURVE:
599  if( isModified( m_editPoints->Point( BEZIER_CURVE_START ) ) )
600  segment->SetStart( wxPoint( m_editPoints->Point( BEZIER_CURVE_START ).GetPosition().x,
601  m_editPoints->Point( BEZIER_CURVE_START ).GetPosition().y ) );
602  else if( isModified( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ) ) )
603  segment->SetBezControl1( wxPoint( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).GetPosition().x,
604  m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).GetPosition().y ) );
605  else if( isModified( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ) ) )
606  segment->SetBezControl2( wxPoint( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).GetPosition().x,
607  m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).GetPosition().y ) );
608  else if( isModified( m_editPoints->Point( BEZIER_CURVE_END ) ) )
609  segment->SetEnd( wxPoint( m_editPoints->Point( BEZIER_CURVE_END ).GetPosition().x,
610  m_editPoints->Point( BEZIER_CURVE_END ).GetPosition().y ) );
611 
612  segment->RebuildBezierToSegmentsPointsList( segment->GetWidth() );
613  break;
614 
615  default: // suppress warnings
616  break;
617  }
618 
619  // Update relative coordinates for module edges
620  if( EDGE_MODULE* edge = dyn_cast<EDGE_MODULE*>( item ) )
621  edge->SetLocalCoord();
622 
623  break;
624  }
625 
626  case PCB_PAD_T:
627  {
628  D_PAD* pad = static_cast<D_PAD*>( item );
629 
630  switch( pad->GetShape() )
631  {
632  case PAD_SHAPE_CIRCLE:
633  {
634  wxPoint center = (wxPoint) m_editPoints->Point( CIRC_CENTER ).GetPosition();
635  wxPoint end = (wxPoint) m_editPoints->Point( CIRC_END ).GetPosition();
636 
637  if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
638  {
639  wxPoint moveVector = center - pad->ShapePos();
640  pad->SetOffset( pad->GetOffset() + moveVector );
641  }
642  else
643  {
644  int diameter = (int) EuclideanNorm( end - center ) * 2;
645  pad->SetSize( wxSize( diameter, diameter ) );
646  }
647  }
648  break;
649 
650  case PAD_SHAPE_OVAL:
651  case PAD_SHAPE_TRAPEZOID:
652  case PAD_SHAPE_RECT:
653  case PAD_SHAPE_ROUNDRECT:
655  {
656  if( ( pad->GetOffset().x || pad->GetOffset().y )
657  || ( pad->GetDrillSize().x && pad->GetDrillSize().y ) )
658  {
659  // Keep hole pinned at the current location; adjust the pad around the hole
660 
661  wxPoint center = pad->GetPosition();
662  int dist[4];
663 
664  if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
665  || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
666  {
667  dist[0] = center.x - m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().x;
668  dist[1] = center.y - m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().y;
669  dist[2] = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().x - center.x;
670  dist[3] = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().y - center.y;
671  }
672  else
673  {
674  dist[0] = center.x - m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().x;
675  dist[1] = center.y - m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().y;
676  dist[2] = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().x - center.x;
677  dist[3] = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().y - center.y;
678  }
679 
680  wxSize padSize( dist[0] + dist[2], dist[1] + dist[3] );
681  wxPoint deltaOffset( padSize.x / 2 - dist[2], padSize.y / 2 - dist[3] );
682 
683  if( pad->GetOrientation() == 900 || pad->GetOrientation() == 2700 )
684  std::swap( padSize.x, padSize.y );
685 
686  RotatePoint( &deltaOffset, -pad->GetOrientation() );
687 
688  pad->SetSize( padSize );
689  pad->SetOffset( -deltaOffset );
690  }
691  else
692  {
693  // Keep pad position at the center of the pad shape
694 
695  int left, top, right, bottom;
696 
697  if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
698  || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
699  {
700  left = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().x;
701  top = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().y;
702  right = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().x;
703  bottom = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().y;
704  }
705  else
706  {
707  left = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().x;
708  top = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().y;
709  right = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().x;
710  bottom = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().y;
711  }
712 
713  wxSize padSize( abs( right - left ), abs( bottom - top ) );
714 
715  if( pad->GetOrientation() == 900 || pad->GetOrientation() == 2700 )
716  std::swap( padSize.x, padSize.y );
717 
718  pad->SetSize( padSize );
719  pad->SetPosition( wxPoint( ( left + right ) / 2, ( top + bottom ) / 2 ) );
720  }
721  }
722  break;
723 
724  default: // suppress warnings
725  break;
726  }
727  }
728  break;
729 
731  case PCB_ZONE_AREA_T:
732  {
733  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
734  zone->ClearFilledPolysList();
735  SHAPE_POLY_SET& outline = *zone->Outline();
736 
737  for( int i = 0; i < outline.TotalVertices(); ++i )
738  {
739  if( outline.CVertex( i ) != m_editPoints->Point( i ).GetPosition() )
740  zone->SetNeedRefill( true );
741 
742  outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
743  }
744 
745  validatePolygon( outline );
746  zone->Hatch();
747  break;
748  }
749 
750  case PCB_DIMENSION_T:
751  {
752  DIMENSION* dimension = static_cast<DIMENSION*>( item );
753 
754  // Check which point is currently modified and updated dimension's points respectively
755  if( isModified( m_editPoints->Point( DIM_CROSSBARO ) ) )
756  {
757  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetOrigin() );
758  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() );
759 
760  if( featureLine.Cross( crossBar ) > 0 )
761  dimension->SetHeight( -featureLine.EuclideanNorm(), boardSettings.m_DimensionPrecision );
762  else
763  dimension->SetHeight( featureLine.EuclideanNorm(), boardSettings.m_DimensionPrecision );
764  }
765 
766  else if( isModified( m_editPoints->Point( DIM_CROSSBARF ) ) )
767  {
768  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
769  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetOrigin() );
770 
771  if( featureLine.Cross( crossBar ) > 0 )
772  dimension->SetHeight( -featureLine.EuclideanNorm(), boardSettings.m_DimensionPrecision );
773  else
774  dimension->SetHeight( featureLine.EuclideanNorm(), boardSettings.m_DimensionPrecision );
775  }
776 
777  else if( isModified( m_editPoints->Point( DIM_FEATUREGO ) ) )
778  {
780  boardSettings.m_DimensionPrecision );
781  m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ),
782  m_editPoints->Point( DIM_FEATUREGO ) ) );
783  m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ),
784  m_editPoints->Point( DIM_FEATUREDO ) ) );
785  }
786 
787  else if( isModified( m_editPoints->Point( DIM_FEATUREDO ) ) )
788  {
790  boardSettings.m_DimensionPrecision );
791  m_editPoints->Point( DIM_CROSSBARO ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARO ),
792  m_editPoints->Point( DIM_FEATUREGO ) ) );
793  m_editPoints->Point( DIM_CROSSBARF ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARF ),
794  m_editPoints->Point( DIM_FEATUREDO ) ) );
795  }
796 
797  break;
798  }
799 
800  default:
801  break;
802  }
803 
804  getView()->Update( item );
805 
806  if( frame() )
807  frame()->SetMsgPanel( item );
808 }
809 
810 
812 {
813  auto item = m_editPoints->GetParent();
814 
815  if( !item )
816  return;
817 
818  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T )
819  {
820  auto zone = static_cast<ZONE_CONTAINER*>( item );
821 
822  if( zone->IsFilled() && m_refill && zone->NeedRefill() )
823  m_toolMgr->RunAction( PCB_ACTIONS::zoneFill, true, zone );
824  }
825 }
826 
827 
829 {
830  bool valid = !aPoly.IsSelfIntersecting();
831 
832  if( m_statusPopup )
833  {
834  if( valid )
835  {
836  m_statusPopup->Hide();
837  }
838  else
839  {
840  wxPoint p = wxGetMousePosition() + wxPoint( 20, 20 );
841  m_statusPopup->Move( p );
842  m_statusPopup->PopupFor( 1500 );
843  }
844  }
845 
846  return valid;
847 }
848 
849 
851 {
852  if( !m_editPoints )
853  return;
854 
855  EDA_ITEM* item = m_editPoints->GetParent();
856 
857  if( !item )
858  return;
859 
860  switch( item->Type() )
861  {
862  case PCB_LINE_T:
863  case PCB_MODULE_EDGE_T:
864  {
865  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( item );
866 
867  switch( segment->GetShape() )
868  {
869  case S_SEGMENT:
870  m_editPoints->Point( SEG_START ).SetPosition( segment->GetStart() );
871  m_editPoints->Point( SEG_END ).SetPosition( segment->GetEnd() );
872  break;
873 
874  case S_RECT:
875  m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( segment->GetStart() );
876  m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( segment->GetEnd().x,
877  segment->GetStart().y );
878  m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( segment->GetEnd() );
879  m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( segment->GetStart().x,
880  segment->GetEnd().y );
881  break;
882 
883  case S_ARC:
884  m_editPoints->Point( ARC_CENTER ).SetPosition( segment->GetCenter() );
885  m_editPoints->Point( ARC_START ).SetPosition( segment->GetArcStart() );
886  m_editPoints->Point( ARC_MID ).SetPosition( segment->GetArcMid() );
887  m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
888  break;
889 
890  case S_CIRCLE:
891  m_editPoints->Point( CIRC_CENTER ).SetPosition( segment->GetCenter() );
892  m_editPoints->Point( CIRC_END ).SetPosition( segment->GetEnd() );
893  break;
894 
895  case S_POLYGON:
896  {
897  const auto& points = segment->BuildPolyPointsList();
898 
899  if( m_editPoints->PointsSize() != (unsigned) points.size() )
900  {
901  getView()->Remove( m_editPoints.get() );
902  m_editedPoint = nullptr;
903  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
904  getView()->Add( m_editPoints.get() );
905  }
906  else
907  {
908  for( unsigned i = 0; i < points.size(); i++ )
909  m_editPoints->Point( i ).SetPosition( points[i] );
910  }
911  break;
912  }
913 
914  case S_CURVE:
915  m_editPoints->Point( BEZIER_CURVE_START ).SetPosition( segment->GetStart() );
916  m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).SetPosition( segment->GetBezControl1() );
917  m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).SetPosition( segment->GetBezControl2() );
918  m_editPoints->Point( BEZIER_CURVE_END ).SetPosition( segment->GetEnd() );
919  break;
920 
921  default: // suppress warnings
922  break;
923  }
924 
925  break;
926  }
927 
928  case PCB_PAD_T:
929  {
930  const D_PAD* pad = static_cast<const D_PAD*>( item );
931  bool locked = pad->GetParent() && pad->GetParent()->PadsLocked();
932  wxPoint shapePos = pad->ShapePos();
933  wxPoint halfSize( pad->GetSize().x / 2, pad->GetSize().y / 2 );
934 
935  switch( pad->GetShape() )
936  {
937  case PAD_SHAPE_CIRCLE:
938  {
939  int target = locked ? 0 : 2;
940 
941  // Careful; pad shape is mutable...
942  if( int( m_editPoints->PointsSize() ) != target )
943  {
944  getView()->Remove( m_editPoints.get() );
945  m_editedPoint = nullptr;
946  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
947  getView()->Add( m_editPoints.get() );
948  }
949  else if( target == 2 )
950  {
951  VECTOR2I vec = m_editPoints->Point( CIRC_END ).GetPosition()
952  - m_editPoints->Point( CIRC_CENTER ).GetPosition();
953  vec.Resize( halfSize.x );
954 
955  m_editPoints->Point( CIRC_CENTER ).SetPosition( shapePos );
956  m_editPoints->Point( CIRC_END ).SetPosition( vec + shapePos );
957  }
958  }
959  break;
960 
961  case PAD_SHAPE_OVAL:
962  case PAD_SHAPE_TRAPEZOID:
963  case PAD_SHAPE_RECT:
964  case PAD_SHAPE_ROUNDRECT:
966  {
967  // Careful; pad shape and orientation are mutable...
968  int target = locked || (int) pad->GetOrientation() % 900 > 0 ? 0 : 4;
969 
970  if( int( m_editPoints->PointsSize() ) != target )
971  {
972  getView()->Remove( m_editPoints.get() );
973  m_editedPoint = nullptr;
974  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
975  getView()->Add( m_editPoints.get() );
976  }
977  else if( target == 4 )
978  {
979  if( pad->GetOrientation() == 900 || pad->GetOrientation() == 2700 )
980  std::swap( halfSize.x, halfSize.y );
981 
982  m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shapePos - halfSize );
983  m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( wxPoint( shapePos.x + halfSize.x,
984  shapePos.y - halfSize.y ) );
985  m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shapePos + halfSize );
986  m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( wxPoint( shapePos.x - halfSize.x,
987  shapePos.y + halfSize.y ) );
988  }
989  }
990  break;
991 
992  default: // suppress warnings
993  break;
994  }
995  }
996  break;
997 
999  case PCB_ZONE_AREA_T:
1000  {
1001  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
1002  const SHAPE_POLY_SET* outline = zone->Outline();
1003 
1004  if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
1005  {
1006  getView()->Remove( m_editPoints.get() );
1007  m_editedPoint = nullptr;
1008  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
1009  getView()->Add( m_editPoints.get() );
1010  }
1011  else
1012  {
1013  for( int i = 0; i < outline->TotalVertices(); ++i )
1014  m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
1015  }
1016 
1017  break;
1018  }
1019 
1020  case PCB_DIMENSION_T:
1021  {
1022  const DIMENSION* dimension = static_cast<const DIMENSION*>( item );
1023 
1024  m_editPoints->Point( DIM_CROSSBARO ).SetPosition( dimension->m_crossBarO );
1025  m_editPoints->Point( DIM_CROSSBARF ).SetPosition( dimension->m_crossBarF );
1026  m_editPoints->Point( DIM_FEATUREGO ).SetPosition( dimension->m_featureLineGO );
1027  m_editPoints->Point( DIM_FEATUREDO ).SetPosition( dimension->m_featureLineDO );
1028  break;
1029  }
1030 
1031  default:
1032  break;
1033  }
1034 
1035  getView()->Update( m_editPoints.get() );
1036 }
1037 
1038 
1040 {
1042 
1043  if( aPoint )
1044  {
1045  frame()->GetCanvas()->SetCurrentCursor( wxCURSOR_ARROW );
1046  controls->ForceCursorPosition( true, aPoint->GetPosition() );
1047  controls->ShowCursor( true );
1048  }
1049  else
1050  {
1051  if( frame()->ToolStackIsEmpty() )
1052  controls->ShowCursor( false );
1053 
1054  controls->ForceCursorPosition( false );
1055  }
1056 
1057  m_editedPoint = aPoint;
1058 }
1059 
1060 
1061 void POINT_EDITOR::setAltConstraint( bool aEnabled )
1062 {
1063  if( aEnabled )
1064  {
1065  EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
1066 
1067  if( line &&
1068  ( m_editPoints->GetParent()->Type() == PCB_ZONE_AREA_T
1069  || m_editPoints->GetParent()->Type() == PCB_MODULE_ZONE_AREA_T ) )
1070  {
1072  }
1073  else
1074  {
1075  // Find a proper constraining point for 45 degrees mode
1078  }
1079  }
1080  else
1081  {
1082  m_altConstraint.reset();
1083  }
1084 }
1085 
1086 
1088 {
1089  EDA_ITEM* item = m_editPoints->GetParent();
1090 
1091  switch( item->Type() )
1092  {
1093  case PCB_LINE_T:
1094  case PCB_MODULE_EDGE_T:
1095  {
1096  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( item );
1097  {
1098  switch( segment->GetShape() )
1099  {
1100  case S_SEGMENT:
1101  return *( m_editPoints->Next( *m_editedPoint ) ); // select the other end of line
1102 
1103  case S_ARC:
1104  case S_CIRCLE:
1105  return m_editPoints->Point( CIRC_CENTER );
1106 
1107  default: // suppress warnings
1108  break;
1109  }
1110  }
1111 
1112  break;
1113  }
1114 
1115  case PCB_DIMENSION_T:
1116  {
1117  // Constraint for crossbar
1118  if( isModified( m_editPoints->Point( DIM_FEATUREGO ) ) )
1119  return m_editPoints->Point( DIM_FEATUREDO );
1120 
1121  else if( isModified( m_editPoints->Point( DIM_FEATUREDO ) ) )
1122  return m_editPoints->Point( DIM_FEATUREGO );
1123 
1124  else
1125  return EDIT_POINT( m_editedPoint->GetPosition() ); // no constraint
1126 
1127  break;
1128  }
1129 
1130  default:
1131  break;
1132  }
1133 
1134  // In any other case we may align item to its original position
1135  return m_original;
1136 }
1137 
1138 
1140 {
1141  const auto type = aItem.Type();
1142 
1143  // Works only for zones and line segments
1144  return type == PCB_ZONE_AREA_T || type == PCB_MODULE_ZONE_AREA_T ||
1145  ( ( type == PCB_LINE_T || type == PCB_MODULE_EDGE_T ) &&
1146  ( static_cast<const DRAWSEGMENT&>( aItem ).GetShape() == S_SEGMENT ||
1147  static_cast<const DRAWSEGMENT&>( aItem ).GetShape() == S_POLYGON ) );
1148 }
1149 
1150 
1152 {
1153  if( aSelection.Size() != 1 )
1154  return false;
1155 
1156  const EDA_ITEM* item = aSelection.Front();
1157 
1158  return ( item != nullptr ) && canAddCorner( *item );
1159 }
1160 
1161 
1162 // Finds a corresponding vertex in a polygon set
1163 static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
1164 findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
1165 {
1166  for( auto it = aPolySet.IterateWithHoles(); it; ++it )
1167  {
1168  auto vertexIdx = it.GetIndex();
1169 
1170  if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
1171  return std::make_pair( true, vertexIdx );
1172  }
1173 
1174  return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
1175 }
1176 
1177 
1179 {
1180  if( !m_editPoints || !m_editedPoint )
1181  return false;
1182 
1183  EDA_ITEM* item = m_editPoints->GetParent();
1184 
1185  if( !item || !( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T ||
1186  ( ( item->Type() == PCB_MODULE_EDGE_T || item->Type() == PCB_LINE_T ) &&
1187  static_cast<DRAWSEGMENT*>( item )->GetShape() == S_POLYGON ) ) )
1188  return false;
1189 
1190  SHAPE_POLY_SET *polyset;
1191 
1192  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T )
1193  polyset = static_cast<ZONE_CONTAINER*>( item )->Outline();
1194  else
1195  polyset = &static_cast<DRAWSEGMENT*>( item )->GetPolyShape();
1196 
1197  auto vertex = findVertex( *polyset, *m_editedPoint );
1198 
1199  if( !vertex.first )
1200  return false;
1201 
1202  const auto& vertexIdx = vertex.second;
1203 
1204  // Check if there are enough vertices so one can be removed without
1205  // degenerating the polygon.
1206  // The first condition allows one to remove all corners from holes (when
1207  // there are only 2 vertices left, a hole is removed).
1208  if( vertexIdx.m_contour == 0 && polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
1209  return false;
1210 
1211  // Remove corner does not work with lines
1212  if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
1213  return false;
1214 
1215  return m_editedPoint != NULL;
1216 }
1217 
1218 
1220 {
1221  if( !m_editPoints )
1222  return 0;
1223 
1224  EDA_ITEM* item = m_editPoints->GetParent();
1225  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1226  const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
1227 
1228  // called without an active edited polygon
1229  if( !item || !canAddCorner( *item ) )
1230  return 0;
1231 
1232  DRAWSEGMENT* graphicItem = dynamic_cast<DRAWSEGMENT*>( item );
1233  BOARD_COMMIT commit( frame );
1234 
1235  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T ||
1236  ( graphicItem && graphicItem->GetShape() == S_POLYGON ) )
1237  {
1238  unsigned int nearestIdx = 0;
1239  unsigned int nextNearestIdx = 0;
1240  unsigned int nearestDist = INT_MAX;
1241  unsigned int firstPointInContour = 0;
1242  SHAPE_POLY_SET* zoneOutline;
1243 
1244  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T )
1245  {
1246  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
1247  zoneOutline = zone->Outline();
1248  zone->SetNeedRefill( true );
1249  }
1250  else
1251  zoneOutline = &( graphicItem->GetPolyShape() );
1252 
1253  commit.Modify( item );
1254 
1255  // Search the best outline segment to add a new corner
1256  // and therefore break this segment into two segments
1257 
1258  // Object to iterate through the corners of the outlines (main contour and its holes)
1259  SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0,
1260  zoneOutline->OutlineCount()-1, /* IterateHoles */ true );
1261  int curr_idx = 0;
1262 
1263  // Iterate through all the corners of the outlines and search the best segment
1264  for( ; iterator; iterator++, curr_idx++ )
1265  {
1266  int jj = curr_idx+1;
1267 
1268  if( iterator.IsEndContour() )
1269  { // We reach the last point of the current contour (main or hole)
1270  jj = firstPointInContour;
1271  firstPointInContour = curr_idx+1; // Prepare next contour analysis
1272  }
1273 
1274  SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
1275 
1276  unsigned int distance = curr_segment.Distance( cursorPos );
1277 
1278  if( distance < nearestDist )
1279  {
1280  nearestDist = distance;
1281  nearestIdx = curr_idx;
1282  nextNearestIdx = jj;
1283  }
1284  }
1285 
1286  // Find the point on the closest segment
1287  auto& sideOrigin = zoneOutline->CVertex( nearestIdx );
1288  auto& sideEnd = zoneOutline->CVertex( nextNearestIdx );
1289  SEG nearestSide( sideOrigin, sideEnd );
1290  VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
1291 
1292  // Do not add points that have the same coordinates as ones that already belong to polygon
1293  // instead, add a point in the middle of the side
1294  if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
1295  nearestPoint = ( sideOrigin + sideEnd ) / 2;
1296 
1297  zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
1298 
1299  // We re-hatch the filled zones but not polygons
1300  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T )
1301  static_cast<ZONE_CONTAINER*>( item )->Hatch();
1302 
1303 
1304  commit.Push( _( "Add a zone corner" ) );
1305  }
1306 
1307  else if( graphicItem && graphicItem->GetShape() == S_SEGMENT )
1308  {
1309  commit.Modify( graphicItem );
1310 
1311  SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
1312  VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
1313 
1314  // Move the end of the line to the break point..
1315  graphicItem->SetEnd( wxPoint( nearestPoint.x, nearestPoint.y ) );
1316 
1317  if( graphicItem->Type() == PCB_MODULE_EDGE_T )
1318  static_cast<EDGE_MODULE*>( graphicItem )->SetLocalCoord();
1319 
1320  // and add another one starting from the break point
1321  DRAWSEGMENT* newSegment;
1322 
1323  if( item->Type() == PCB_MODULE_EDGE_T )
1324  {
1325  EDGE_MODULE* edge = static_cast<EDGE_MODULE*>( graphicItem );
1326  assert( edge->GetParent()->Type() == PCB_MODULE_T );
1327  newSegment = new EDGE_MODULE( *edge );
1328  }
1329  else
1330  {
1331  newSegment = new DRAWSEGMENT( *graphicItem );
1332  }
1333 
1334  newSegment->ClearSelected();
1335  newSegment->SetStart( wxPoint( nearestPoint.x, nearestPoint.y ) );
1336  newSegment->SetEnd( wxPoint( seg.B.x, seg.B.y ) );
1337 
1338  if( newSegment->Type() == PCB_MODULE_EDGE_T )
1339  static_cast<EDGE_MODULE*>( newSegment )->SetLocalCoord();
1340 
1341  commit.Add( newSegment );
1342  commit.Push( _( "Split segment" ) );
1343  }
1344 
1345  updatePoints();
1346  return 0;
1347 }
1348 
1349 
1351 {
1352  if( !m_editPoints || !m_editedPoint )
1353  return 0;
1354 
1355  EDA_ITEM* item = m_editPoints->GetParent();
1356 
1357  if( !item )
1358  return 0;
1359 
1360  SHAPE_POLY_SET* polygon = nullptr;
1361 
1362  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T )
1363  {
1364  auto zone = static_cast<ZONE_CONTAINER*>( item );
1365  polygon = zone->Outline();
1366  zone->SetNeedRefill( true );
1367  }
1368  else if( (item->Type() == PCB_MODULE_EDGE_T ) || ( item->Type() == PCB_LINE_T ) )
1369  {
1370  auto ds = static_cast<DRAWSEGMENT*>( item );
1371 
1372  if( ds->GetShape() == S_POLYGON )
1373  polygon = &ds->GetPolyShape();
1374  }
1375 
1376  if( !polygon )
1377  return 0;
1378 
1379  PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
1380  BOARD_COMMIT commit( frame );
1381  auto vertex = findVertex( *polygon, *m_editedPoint );
1382 
1383  if( vertex.first )
1384  {
1385  const auto& vertexIdx = vertex.second;
1386  auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
1387 
1388  if( outline.PointCount() > 3 )
1389  {
1390  // the usual case: remove just the corner when there are >3 vertices
1391  commit.Modify( item );
1392  polygon->RemoveVertex( vertexIdx );
1393  validatePolygon( *polygon );
1394  }
1395  else
1396  {
1397  // either remove a hole or the polygon when there are <= 3 corners
1398  if( vertexIdx.m_contour > 0 )
1399  {
1400  // remove hole
1401  commit.Modify( item );
1402  polygon->RemoveContour( vertexIdx.m_contour );
1403  }
1404  else
1405  {
1407  commit.Remove( item );
1408  }
1409  }
1410 
1411  setEditedPoint( nullptr );
1412 
1413  commit.Push( _( "Remove a zone/polygon corner" ) );
1414 
1415  // Refresh zone hatching
1416  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T )
1417  static_cast<ZONE_CONTAINER*>( item )->Hatch();
1418 
1419  updatePoints();
1420  }
1421 
1422  return 0;
1423 }
1424 
1425 
1427 {
1428  updatePoints();
1429  return 0;
1430 }
1431 
1432 
1434 {
1441 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:128
static TOOL_ACTION selectionClear
Clears the current selection.
Definition: pcb_actions.h:62
const wxPoint GetArcMid() const
int TotalVertices() const
Returns total number of vertices stored in the set.
virtual void ShowCursor(bool aEnabled)
Function ShowCursor() Enables or disables display of cursor.
EC_CONVERGING.
ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:61
int OnSelectionChange(const TOOL_EVENT &aEvent)
Function OnSelected()
COMMIT & Modify(EDA_ITEM *aItem)
Modifies a given item in the model.
Definition: commit.h:103
static const TOOL_EVENT SelectedEvent
Definition: actions.h:202
void setTransitions() override
Sets up handlers for various events.
int OutlineCount() const
Returns the number of outlines in the set
bool IsEndContour() const
Function IsEndContour.
virtual void Move(const wxPoint &aMoveVector) override
Function Move move this object.
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Function Wait()
BOARD * board() const
static TOOL_ACTION activatePointEditor
Definition: actions.h:163
void SetArcStart(const wxPoint &aArcStartPoint)
Initialize the start arc point.
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:203
This file is part of the common library.
wxPoint m_crossBarF
BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class,...
const wxPoint GetCenter() const override
Function GetCenter()
void ClearSelected()
Definition: base_struct.h:211
COMMIT & Add(EDA_ITEM *aItem)
Adds a new item to the model
Definition: commit.h:78
void SetCurrentCursor(wxStockCursor aStockCursorID)
Function SetCurrentCursor Set the current cursor shape for this panel.
VIEW_CONTROLS class definition.
STROKE_T GetShape() const
void SetStartY(int y)
const wxPoint GetArcEnd() const
void updateEditedPoint(const TOOL_EVENT &aEvent)
Updates which point is being edited.
SELECTION_TOOL.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Returns the index-th vertex in a given hole outline within a given outline
virtual void Revert() override
Revertes the commit by restoring the modifed items state.
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
CONDITIONAL_MENU & GetMenu()
Function GetMenu.
Definition: tool_menu.cpp:46
polygon (not yet used for tracks, but could be in microwave apps)
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:219
EDIT_LINE.
Definition: edit_points.h:221
int removeCorner(const TOOL_EVENT &aEvent)
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_BezierPoints vertex list that approximate the Bezier curve by a list of segments Has me...
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:376
SHAPE_POLY_SET * Outline()
Definition: class_zone.h:271
CONST_ITERATOR CIterateWithHoles(int aOutline) const
TOOL_MENU & GetToolMenu()
bool IsMotion() const
Definition: tool_event.h:306
virtual void SetSnapping(bool aEnabled)
Function SetSnapping() Enables/disables snapping cursor to grid.
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Function RunAction() Runs the specified action.
Definition: tool_manager.h:140
double RAD2DECIDEG(double rad)
Definition: trigo.h:219
Struct VERTEX_INDEX.
void SetHeight(int aHeight, int aPrecision)
Function SetHeight Sets the length of feature lines.
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:155
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:551
void setAltConstraint(bool aEnabled)
Sets up an alternative constraint (typically enabled upon a modifier key being pressed).
usual segment : line with rounded ends
class D_PAD, a pad in a footprint
Definition: typeinfo.h:90
bool isModified(const EDIT_POINT &aPoint) const
Returns true if aPoint is the currently modified point.
Definition: point_editor.h:117
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
std::unique_ptr< STATUS_TEXT_POPUP > m_statusPopup
Definition: point_editor.h:92
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...
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:208
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...
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
void SetCenter(const wxPoint &aCenterPoint)
For arcs and circles:
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
void SetBezControl2(const wxPoint &aPoint)
static TOOL_ACTION zoneFill
Definition: pcb_actions.h:285
EC_LINE.
virtual void Remove(VIEW_ITEM *aItem) override
Function Remove() Removes a VIEW_ITEM from the view.
Definition: pcb_view.cpp:74
EC_45DEGREE.
void updateItem() const
Updates item's points with edit points.
static const TOOL_EVENT SelectedItemsModified
Definition: actions.h:206
bool removeCornerCondition(const SELECTION &aSelection)
Condition to display "Remove corner" context menu entry.
bool clockwise(const Point &p0, const Point &p1, const Point &p2)
Definition: delauney.h:217
PCBNEW_SELECTION & GetSelection()
Function GetSelection()
EC_CIRCLE.
wxPoint m_featureLineGO
VECTOR2D GetGridPoint(const VECTOR2D &aPoint) const
Function GetGridPoint() For a given point it returns the nearest point belonging to the grid in world...
PCB_BASE_EDIT_FRAME * frame() const
double dist(const double ax, const double ay, const double bx, const double by)
Definition: delauney.h:168
void SetVertex(const VERTEX_INDEX &aIndex, const VECTOR2I &aPos)
Function SetVertex Accessor function to set the position of a specific point.
DIMENSION class definition.
std::shared_ptr< EDIT_POINTS > m_editPoints
Currently available edit points.
Definition: point_editor.h:81
void finishItem()
Applies the last changes to the edited item.
segment with non rounded ends
class MODULE, a footprint
Definition: typeinfo.h:89
void SetActive(bool aActive=true)
Definition: edit_points.h:185
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:296
LSET is a set of PCB_LAYER_IDs.
const PCBNEW_SELECTION & selection() const
void SetOrigin(const wxPoint &aOrigin, int aPrecision)
Function SetOrigin Sets a new origin of the crossbar line.
virtual VECTOR2I GetPosition() const
Function GetPosition()
Definition: edit_points.h:69
#define NULL
const wxPoint & GetEnd()
Function GetEnd.
void SetEnd(const wxPoint &aEnd, int aPrecision)
Function SetEnd Sets a new end of the crossbar line.
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
STATUS_FLAGS GetEditFlags() const
Definition: base_struct.h:237
ITERATOR IterateWithHoles(int aOutline)
Function IterateWithHoles.
SHAPE_POLY_SET.
SEG_POINTS
SHAPE_LINE_CHAIN & Outline(int aIndex)
Returns the reference to aIndex-th outline in the set
static TOOL_ACTION pointEditorAddCorner
Break outline (insert additional points to an edge)
Definition: pcb_actions.h:198
static TOOL_ACTION pointEditorRemoveCorner
Removes a corner.
Definition: pcb_actions.h:201
Arcs (with rounded ends)
const wxPoint & GetOffset() const
Definition: class_pad.h:229
TOOL_EVENT.
Definition: tool_event.h:171
std::shared_ptr< EDIT_CONSTRAINT< EDIT_POINT > > m_altConstraint
Definition: point_editor.h:84
COMMIT & StageItems(const Range &aRange, CHANGE_TYPE aChangeType)
Definition: commit.h:116
KIGFX::PCB_VIEW * view() const
DIMENSION_POINTS
void SetSize(const wxSize &aSize)
Definition: class_pad.h:219
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Function NearestPoint()
Definition: seg.h:395
VIEW_CONTROLS is an interface for classes handling user events controlling the view behaviour (such a...
void SetEndY(int y)
const VECTOR2D DragOrigin() const
Returns the point where dragging has started.
Definition: tool_event.h:280
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Function ForceCursorPosition() Places the cursor immediately at a given point.
static LSET AllLayersMask()
Definition: lset.cpp:749
const wxPoint & GetArcStart() const
COMMIT & Remove(EDA_ITEM *aItem)
Removes a new item from the model
Definition: commit.h:90
SHAPE_POLY_SET & GetPolyShape()
SELECTION_TOOL * m_selectionTool
Selection tool used for obtaining selected items
Definition: point_editor.h:72
Bezier Curve.
const wxPoint & GetOrigin() const
Function GetOrigin.
double Angle() const
Function Angle computes the angle of the vector.
Definition: vector2d.h:313
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:1540
class DIMENSION, a dimension (graphic item)
Definition: typeinfo.h:100
KIGFX::VIEW * getView() const
Function getView()
Definition: tool_base.cpp:36
virtual void SetAutoPan(bool aEnabled)
Function SetAutoPan Turns on/off auto panning (this feature is used when there is a tool active (eg.
int GetWidth() const
void SetStart(const wxPoint &aStart)
MODULE * GetParent() const
Definition: class_pad.h:107
Definition: seg.h:39
EDIT_POINT get45DegConstrainer() const
Returns a point that should be used as a constrainer for 45 degrees mode.
VECTOR2< T > Resize(T aNewLength) const
Function Resize returns a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:392
double GetAngle() const
RECT_POINTS
bool validatePolygon(SHAPE_POLY_SET &aModified) const
Validates a polygon and displays a popup warning if invalid.
ARC_POINTS
KIGFX::VIEW_CONTROLS * controls() const
Common, abstract interface for edit frames.
#define _(s)
Definition: 3d_actions.cpp:33
double GetOrientation() const
Function GetOrientation returns the rotation angle of the pad in tenths of degrees,...
Definition: class_pad.h:313
void RemoveVertex(int aGlobalIndex)
Function RemoveVertex deletes the aGlobalIndex-th vertex.
CIRCLE_POINTS
const wxSize & GetDrillSize() const
Definition: class_pad.h:226
wxPoint ShapePos() const
Definition: class_pad.cpp:491
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
static bool canAddCorner(const EDA_ITEM &aItem)
Determine if the tool can currently add a corner to the given item
int Size() const
Returns the number of selected parts.
Definition: selection.h:127
void Hatch()
Function Hatch computes the hatch lines depending on the hatch parameters and stores it in the zone's...
Definition: class_zone.cpp:981
const wxPoint & GetBezControl2() const
EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boards.
Definition: base_struct.h:159
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:79
void SetEndX(int x)
void updatePoints()
Updates edit points with item'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.
bool PadsLocked() const
Definition: class_module.h:333
EDIT_POINT m_original
Original position for the current drag point.
Definition: point_editor.h:78
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
BEZIER_CURVE_POINTS
const wxPoint & GetBezControl1() const
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Function Add() Adds a VIEW_ITEM to the view.
Definition: pcb_view.cpp:58
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Function Add() Adds a VIEW_ITEM to the view.
Definition: view.cpp:346
bool Init() override
Function Init() Init() is called once upon a registration of the tool.
void SetStartX(int x)
EDIT_POINT.
Definition: edit_points.h:46
PAD_SHAPE_T GetShape() const
Definition: class_pad.h:153
POLYGON & Polygon(int aIndex)
Returns the aIndex-th subpolygon in the set
KIGFX::VIEW_CONTROLS * getViewControls() const
Function getViewControls()
Definition: tool_base.cpp:42
class ZONE_CONTAINER, managed by a footprint
Definition: typeinfo.h:95
VIEW.
Definition: view.h:61
const VECTOR2I GetArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition: trigo.cpp:397
EDIT_POINT m_altConstrainer
Definition: point_editor.h:87
const wxSize & GetSize() const
Definition: class_pad.h:220
const wxPoint GetPosition() const override
Definition: class_pad.h:161
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:75
BOARD_ITEM_CONTAINER * GetParent() const
void SetOffset(const wxPoint &aOffset)
Definition: class_pad.h:228
wxPoint m_featureLineDO
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Function AddItem()
void Reset(RESET_REASON aReason) override
Function Reset() Brings the tool to a known, initial state.
static std::shared_ptr< EDIT_POINTS > Make(EDA_ITEM *aItem, SCH_BASE_FRAME *frame)
EC_SNAPLINE.
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)
DIMENSION.
const VECTOR2D Position() const
Returns mouse cursor position in world coordinates.
Definition: tool_event.h:274
virtual void ApplyConstraint()
Function ApplyConstraint()
Definition: edit_points.h:174
int addCorner(const TOOL_EVENT &aEvent)
TOOL_ACTION handlers.
int modifiedSelection(const TOOL_EVENT &aEvent)
VECTOR2D GetCursorPosition() const
Returns the current cursor position in world coordinates.
EDA_ITEM * Front() const
Definition: selection.h:184
void ClearFilledPolysList()
Function ClearFilledPolysList clears the list of filled polygons.
Definition: class_zone.h:588
static bool addCornerCondition(const SELECTION &aSelection)
Condition to display "Create corner" context menu entry.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193
Class GAL is the abstract interface for drawing on a 2D-surface.
virtual void UpdateMsgPanel()
Redraw the message panel.
BOARD_DESIGN_SETTINGS contains design settings for a BOARD object.
bool IsSelfIntersecting() const
Function IsSelfIntersecting Checks whether any of the polygons in the set is self intersecting.
void SetBezControl1(const wxPoint &aPoint)
static std::shared_ptr< EDIT_POINTS > Make(EDA_ITEM *aItem, KIGFX::GAL *aGal)
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:106
void SetNeedRefill(bool aNeedRefill)
Definition: class_zone.h:189