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
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
wxPoint GetArcStart() const
static const TOOL_EVENT SelectedEvent
Definition: actions.h:203
void setTransitions() override
Sets up handlers for various events.
int OutlineCount() const
Returns the number of outlines in the set
wxPoint GetArcEnd() const
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:164
void SetArcStart(const wxPoint &aArcStartPoint)
Initialize the start arc point.
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:204
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,...
wxPoint GetPosition() const override
Definition: class_pad.h:162
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)
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:156
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
wxPoint GetArcMid() const
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:288
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:207
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:201
static TOOL_ACTION pointEditorRemoveCorner
Removes a corner.
Definition: pcb_actions.h:204
Arcs (with rounded ends)
const wxPoint & GetOffset() const
Definition: class_pad.h:230
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:220
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
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:108
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:315
void RemoveVertex(int aGlobalIndex)
Function RemoveVertex deletes the aGlobalIndex-th vertex.
CIRCLE_POINTS
const wxSize & GetDrillSize() const
Definition: class_pad.h:227
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:154
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:221
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:229
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
wxPoint GetCenter() const override
Function GetCenter()
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