KiCad PCB EDA Suite
pcb_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-2021 CERN
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  * @author Maciej Suminski <maciej.suminski@cern.ch>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <functional>
27 #include <memory>
28 
29 using namespace std::placeholders;
30 #include <advanced_config.h>
31 #include <tool/tool_manager.h>
32 #include <view/view_controls.h>
33 #include <geometry/seg.h>
34 #include <confirm.h>
35 #include "pcb_actions.h"
36 #include "pcb_selection_tool.h"
37 #include "pcb_point_editor.h"
38 #include "pcb_grid_helper.h"
39 #include <board_commit.h>
40 #include <bitmaps.h>
41 #include <status_popup.h>
42 #include <pcb_edit_frame.h>
43 #include <fp_shape.h>
44 #include <pcb_dimension.h>
45 #include <pad.h>
46 #include <zone.h>
48 #include <progress_reporter.h>
49 
50 
51 // Few constants to avoid using bare numbers for point indices
53 {
55 };
56 
57 
59 {
61 };
62 
63 
65 {
67 };
68 
69 
71 {
73 };
74 
75 
77 {
79 };
80 
81 
83 {
88 };
89 
90 
92 {
98 };
99 
100 
102  PCB_TOOL_BASE( "pcbnew.PointEditor" ),
103  m_selectionTool( nullptr ),
104  m_editedPoint( nullptr ),
105  m_hoveredPoint( nullptr ),
106  m_original( VECTOR2I( 0, 0 ) ),
107  m_refill( false ),
108  m_altEditMethod( false ),
109  m_altConstrainer( VECTOR2I( 0, 0 ) )
110 {
111 }
112 
113 
115 {
116  m_refill = false;
117  m_editPoints.reset();
118  m_altConstraint.reset();
119  getViewControls()->SetAutoPan( false );
120 
121  m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( getEditFrame<PCB_BASE_EDIT_FRAME>() );
122  m_statusPopup->SetTextColor( wxColour( 255, 0, 0 ) );
123  m_statusPopup->SetText( _( "Self-intersecting polygons are not allowed." ) );
124 }
125 
126 
128 {
129  // Find the selection tool, so they can cooperate
131 
132  wxASSERT_MSG( m_selectionTool, "pcbnew.InteractiveSelection tool is not available" );
133 
134  auto& menu = m_selectionTool->GetToolMenu().GetMenu();
137  std::bind( &PCB_POINT_EDITOR::removeCornerCondition, this, _1 ) );
138 
139  return true;
140 }
141 
142 
143 void PCB_POINT_EDITOR::buildForPolyOutline( std::shared_ptr<EDIT_POINTS> points,
144  const SHAPE_POLY_SET* aOutline )
145 {
146  int cornersCount = aOutline->TotalVertices();
147 
148  for( auto iterator = aOutline->CIterateWithHoles(); iterator; iterator++ )
149  {
150  points->AddPoint( *iterator );
151 
152  if( iterator.IsEndContour() )
153  points->AddBreak();
154  }
155 
156  // Lines have to be added after creating edit points,
157  // as they use EDIT_POINT references
158  for( int i = 0; i < cornersCount - 1; ++i )
159  {
160  if( points->IsContourEnd( i ) )
161  points->AddLine( points->Point( i ), points->Point( points->GetContourStartIdx( i ) ) );
162  else
163  points->AddLine( points->Point( i ), points->Point( i + 1 ) );
164 
165  points->Line( i ).SetConstraint( new EC_PERPLINE( points->Line( i ) ) );
166  }
167 
168  // The last missing line, connecting the last and the first polygon point
169  points->AddLine( points->Point( cornersCount - 1 ),
170  points->Point( points->GetContourStartIdx( cornersCount - 1 ) ) );
171 
172  points->Line( points->LinesSize() - 1 )
173  .SetConstraint( new EC_PERPLINE( points->Line( points->LinesSize() - 1 ) ) );
174 }
175 
176 
177 std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
178 {
179  std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
180 
181  if( !aItem )
182  return points;
183 
184  // Generate list of edit points basing on the item type
185  switch( aItem->Type() )
186  {
187  case PCB_SHAPE_T:
188  case PCB_FP_SHAPE_T:
189  {
190  const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
191 
192  switch( shape->GetShape() )
193  {
194  case SHAPE_T::SEGMENT:
195  points->AddPoint( shape->GetStart() );
196  points->AddPoint( shape->GetEnd() );
197  break;
198 
199  case SHAPE_T::RECT:
200  points->AddPoint( shape->GetStart() );
201  points->AddPoint( wxPoint( shape->GetEnd().x, shape->GetStart().y ) );
202  points->AddPoint( shape->GetEnd() );
203  points->AddPoint( wxPoint( shape->GetStart().x, shape->GetEnd().y ) );
204 
205  points->AddLine( points->Point( RECT_TOP_LEFT ), points->Point( RECT_TOP_RIGHT ) );
206  points->Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( points->Line( RECT_TOP ) ) );
207  points->AddLine( points->Point( RECT_TOP_RIGHT ), points->Point( RECT_BOT_RIGHT ) );
208  points->Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_RIGHT ) ) );
209  points->AddLine( points->Point( RECT_BOT_RIGHT ), points->Point( RECT_BOT_LEFT ) );
210  points->Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_BOT ) ) );
211  points->AddLine( points->Point( RECT_BOT_LEFT ), points->Point( RECT_TOP_LEFT ) );
212  points->Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_LEFT ) ) );
213 
214  break;
215 
216  case SHAPE_T::ARC:
217  points->AddPoint( shape->GetCenter() );
218  points->AddPoint( shape->GetArcStart() );
219  points->AddPoint( shape->GetArcMid() );
220  points->AddPoint( shape->GetArcEnd() );
221 
222  points->Point( ARC_MID ).SetGridConstraint( IGNORE_GRID );
223  points->Point( ARC_START ).SetGridConstraint( SNAP_TO_GRID );
224  points->Point( ARC_CENTER ).SetGridConstraint( SNAP_BY_GRID );
225  points->Point( ARC_END ).SetGridConstraint( SNAP_TO_GRID );
226  break;
227 
228  case SHAPE_T::CIRCLE:
229  points->AddPoint( shape->GetCenter() );
230  points->AddPoint( shape->GetEnd() );
231  break;
232 
233  case SHAPE_T::POLY:
234  buildForPolyOutline( points, &shape->GetPolyShape() );
235  break;
236 
237  case SHAPE_T::BEZIER:
238  points->AddPoint( shape->GetStart() );
239  points->AddPoint( shape->GetBezierC1() );
240  points->AddPoint( shape->GetBezierC2() );
241  points->AddPoint( shape->GetEnd() );
242  break;
243 
244  default: // suppress warnings
245  break;
246  }
247 
248  break;
249  }
250 
251  case PCB_PAD_T:
252  {
253  const PAD* pad = static_cast<const PAD*>( aItem );
254  wxPoint shapePos = pad->ShapePos();
255  wxPoint halfSize( pad->GetSize().x / 2, pad->GetSize().y / 2 );
256 
257  if( !m_isFootprintEditor || pad->IsLocked() )
258  break;
259 
260  switch( pad->GetShape() )
261  {
262  case PAD_SHAPE::CIRCLE:
263  points->AddPoint( shapePos );
264  points->AddPoint( wxPoint( shapePos.x + halfSize.x, shapePos.y ) );
265  break;
266 
267  case PAD_SHAPE::OVAL:
269  case PAD_SHAPE::RECT:
272  {
273  if( (int) pad->GetOrientation() % 900 != 0 )
274  break;
275 
276  if( pad->GetOrientation() == 900 || pad->GetOrientation() == 2700 )
277  std::swap( halfSize.x, halfSize.y );
278 
279  points->AddPoint( shapePos - halfSize );
280  points->AddPoint( wxPoint( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
281  points->AddPoint( shapePos + halfSize );
282  points->AddPoint( wxPoint( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
283  }
284  break;
285 
286  default: // suppress warnings
287  break;
288  }
289  }
290  break;
291 
292  case PCB_FP_ZONE_T:
293  case PCB_ZONE_T:
294  {
295  const ZONE* zone = static_cast<const ZONE*>( aItem );
296  buildForPolyOutline( points, zone->Outline() );
297  }
298  break;
299 
300  case PCB_DIM_ALIGNED_T:
302  {
303  const PCB_DIM_ALIGNED* dimension = static_cast<const PCB_DIM_ALIGNED*>( aItem );
304 
305  points->AddPoint( dimension->GetStart() );
306  points->AddPoint( dimension->GetEnd() );
307  points->AddPoint( dimension->Text().GetPosition() );
308  points->AddPoint( dimension->GetCrossbarStart() );
309  points->AddPoint( dimension->GetCrossbarEnd() );
310 
311  if( aItem->Type() == PCB_DIM_ALIGNED_T )
312  {
313  // Dimension height setting - edit points should move only along the feature lines
314  points->Point( DIM_CROSSBARSTART )
315  .SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARSTART ),
316  points->Point( DIM_START ) ) );
317  points->Point( DIM_CROSSBAREND )
318  .SetConstraint( new EC_LINE( points->Point( DIM_CROSSBAREND ),
319  points->Point( DIM_END ) ) );
320  }
321 
322  break;
323  }
324 
325  case PCB_DIM_CENTER_T:
326  {
327  const PCB_DIM_CENTER* dimension = static_cast<const PCB_DIM_CENTER*>( aItem );
328 
329  points->AddPoint( dimension->GetStart() );
330  points->AddPoint( dimension->GetEnd() );
331 
332  points->Point( DIM_END ).SetConstraint( new EC_45DEGREE( points->Point( DIM_END ),
333  points->Point( DIM_START ) ) );
334 
335  break;
336  }
337 
338  case PCB_DIM_LEADER_T:
339  {
340  const PCB_DIM_LEADER* dimension = static_cast<const PCB_DIM_LEADER*>( aItem );
341 
342  points->AddPoint( dimension->GetStart() );
343  points->AddPoint( dimension->GetEnd() );
344  points->AddPoint( dimension->Text().GetPosition() );
345 
346  points->Point( DIM_TEXT ).SetConstraint( new EC_45DEGREE( points->Point( DIM_TEXT ),
347  points->Point( DIM_END ) ) );
348 
349  break;
350  }
351 
352  default:
353  points.reset();
354  break;
355  }
356 
357  return points;
358 }
359 
360 
362 {
363  EDIT_POINT* point;
364  EDIT_POINT* hovered = nullptr;
365 
366  if( aEvent.IsMotion() )
367  {
368  point = m_editPoints->FindPoint( aEvent.Position(), getView() );
369  hovered = point;
370  }
371  else if( aEvent.IsDrag( BUT_LEFT ) )
372  {
373  point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
374  }
375  else
376  {
377  point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
378  }
379 
380  if( hovered )
381  {
382  if( m_hoveredPoint != hovered )
383  {
384  if( m_hoveredPoint )
385  m_hoveredPoint->SetHover( false );
386 
387  m_hoveredPoint = hovered;
389  }
390  }
391  else if( m_hoveredPoint )
392  {
393  m_hoveredPoint->SetHover( false );
394  m_hoveredPoint = nullptr;
395  }
396 
397  if( m_editedPoint != point )
398  setEditedPoint( point );
399 }
400 
401 
403 {
405  return 0;
406 
407  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
409 
410  if( selection.Size() != 1 || selection.Front()->GetEditFlags() )
411  return 0;
412 
413  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
414 
415  if( !item || item->IsLocked() )
416  return 0;
417 
418  Activate();
419  // Must be done after Activate() so that it gets set into the correct context
420  getViewControls()->ShowCursor( true );
421 
423  m_editPoints = makePoints( item );
424 
425  if( !m_editPoints )
426  return 0;
427 
428  getView()->Add( m_editPoints.get() );
429  setEditedPoint( nullptr );
430  updateEditedPoint( aEvent );
431  m_refill = false;
432  bool inDrag = false;
433 
434  BOARD_COMMIT commit( editFrame );
435  LSET snapLayers = item->GetLayerSet();
436 
437  if( BaseType( item->Type() ) == PCB_DIMENSION_T )
438  snapLayers = LSET::AllLayersMask();
439 
440  // Main loop: keep receiving events
441  while( TOOL_EVENT* evt = Wait() )
442  {
443  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
444  grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
445 
446  if( !m_editPoints || evt->IsSelectionEvent() ||
447  evt->Matches( EVENTS::InhibitSelectionEditing ) )
448  {
449  break;
450  }
451 
452  EDIT_POINT* prevHover = m_hoveredPoint;
453 
454  if( !inDrag )
455  updateEditedPoint( *evt );
456 
457  if( prevHover != m_hoveredPoint )
458  getView()->Update( m_editPoints.get() );
459 
460  if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
461  {
462  if( !inDrag )
463  {
464  frame()->UndoRedoBlock( true );
465 
466  commit.StageItems( selection, CHT_MODIFY );
467 
469  m_original = *m_editedPoint; // Save the original position
470  getViewControls()->SetAutoPan( true );
471  inDrag = true;
472 
474  grid.SetAuxAxes( true, m_original.GetPosition() );
475 
476  setAltConstraint( true );
478  }
479 
480  VECTOR2I pos = evt->Position();
481 
482  //TODO: unify the constraints to solve simultaneously instead of sequentially
483  switch( m_editedPoint->GetGridConstraint() )
484  {
485  case IGNORE_GRID:
486  m_editedPoint->SetPosition( pos );
487  break;
488 
489  case SNAP_TO_GRID:
490  m_editedPoint->SetPosition( grid.BestSnapAnchor( pos, snapLayers, { item } ) );
491  break;
492 
493  case SNAP_BY_GRID:
494  {
495  if( grid.GetUseGrid() )
496  {
497  VECTOR2I gridPt = grid.BestSnapAnchor( pos, {}, { item } );
498 
500  VECTOR2I delta = pos - last;
501  VECTOR2I deltaGrid = gridPt - grid.BestSnapAnchor( last, {}, { item } );
502 
503  if( abs( delta.x ) > grid.GetGrid().x / 2 )
504  pos.x = last.x + deltaGrid.x;
505  else
506  pos.x = last.x;
507 
508  if( abs( delta.y ) > grid.GetGrid().y / 2 )
509  pos.y = last.y + deltaGrid.y;
510  else
511  pos.y = last.y;
512  }
513 
514  m_editedPoint->SetPosition( pos );
515  break;
516  }
517  }
518 
519  // The alternative constraint limits to 45 degrees
520  if( Is45Limited() )
521  m_altConstraint->Apply();
522  else
524 
526  {
528  snapLayers, { item } ) );
529  }
530 
531  updateItem();
533  updatePoints();
534  }
535  else if( m_editedPoint && evt->Action() == TA_MOUSE_DOWN && evt->Buttons() == BUT_LEFT )
536  {
538  getView()->Update( m_editPoints.get() );
539  }
540  else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
541  {
542  if( m_editedPoint )
543  {
544  m_editedPoint->SetActive( false );
545  getView()->Update( m_editPoints.get() );
546  }
547 
548  getViewControls()->SetAutoPan( false );
549  setAltConstraint( false );
550 
551  commit.Push( _( "Drag a corner" ) );
552  inDrag = false;
553  frame()->UndoRedoBlock( false );
554 
555  m_refill = true;
556  }
557  else if( evt->IsCancelInteractive() || evt->IsActivate() )
558  {
559  if( inDrag ) // Restore the last change
560  {
561  commit.Revert();
562  inDrag = false;
563  frame()->UndoRedoBlock( false );
564  }
565 
566  // Only cancel point editor when activating a new tool
567  // Otherwise, allow the points to persist when moving up the
568  // tool stack
569  if( evt->IsActivate() && !evt->IsMoveTool() )
570  break;
571  }
572  else if( evt->Action() == TA_UNDO_REDO_POST )
573  {
574  break;
575  }
576 
577  else
578  {
579  evt->SetPassEvent();
580  }
581  }
582 
583  if( m_editPoints )
584  {
585  getView()->Remove( m_editPoints.get() );
586 
587  finishItem();
588  m_editPoints.reset();
589  }
590 
591  frame()->UpdateMsgPanel();
592 
593  return 0;
594 }
595 
597  const VECTOR2I& aStart, const VECTOR2I& aMid,
598  const VECTOR2I& aEnd,
599  const VECTOR2I& aCursor ) const
600 {
601  VECTOR2I start = aStart;
602  VECTOR2I end = aEnd;
603  VECTOR2I center = aCenter;
604  VECTOR2D startLine = aStart - aCenter;
605  VECTOR2D endLine = aEnd - aCenter;
606  double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
607 
608  bool clockwise;
609  bool movingStart;
610  bool arcValid = true;
611 
612  VECTOR2I p1, p2, p3;
613  // p1 does not move, p2 does.
614 
615  if( aStart != aArc->GetArcStart() )
616  {
617  start = aCursor;
618  p1 = aEnd;
619  p2 = aStart;
620  p3 = aMid;
621  movingStart = true;
622  }
623  else if( aEnd != aArc->GetArcEnd() )
624  {
625  end = aCursor;
626  p1 = aStart;
627  p2 = aEnd;
628  p3 = aMid;
629  movingStart = false;
630  }
631  else
632  {
633  return;
634  }
635 
636  VECTOR2D v1, v2, v3, v4;
637 
638  // Move the coordinate system
639  v1 = p1 - aCenter;
640  v2 = p2 - aCenter;
641  v3 = p3 - aCenter;
642 
643  VECTOR2D u1, u2, u3;
644 
645  // A point cannot be both the center and on the arc.
646  if( ( v1.EuclideanNorm() == 0 ) || ( v2.EuclideanNorm() == 0 ) )
647  return;
648 
649  u1 = v1 / v1.EuclideanNorm();
650  u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
651  u2 = u2 / u2.EuclideanNorm();
652 
653  // [ u1, u3 ] is a base centered on the circle with:
654  // u1 : unit vector toward the point that does not move
655  // u2 : unit vector toward the mid point.
656 
657  // Get vectors v1, and v2 in that coordinate system.
658 
659  double det = u1.x * u2.y - u2.x * u1.y;
660 
661  // u1 and u2 are unit vectors, and perpendicular.
662  // det should not be 0. In case it is, do not change the arc.
663  if( det == 0 )
664  return;
665 
666  double tmpx = v1.x * u2.y - v1.y * u2.x;
667  double tmpy = -v1.x * u1.y + v1.y * u1.x;
668  v1.x = tmpx;
669  v1.y = tmpy;
670  v1 = v1 / det;
671 
672  tmpx = v2.x * u2.y - v2.y * u2.x;
673  tmpy = -v2.x * u1.y + v2.y * u1.x;
674  v2.x = tmpx;
675  v2.y = tmpy;
676  v2 = v2 / det;
677 
678  double R = v1.EuclideanNorm();
679  bool transformCircle = false;
680 
681  /* p2
682  * X***
683  * ** <---- This is the arc
684  * y ^ **
685  * | R *
686  * | <-----------> *
687  * x------x------>--------x p1
688  * C' <----> C x
689  * delta
690  *
691  * p1 does not move, and the tangent at p1 remains the same.
692  * => The new center, C', will be on the C-p1 axis.
693  * p2 moves
694  *
695  * The radius of the new circle is delta + R
696  *
697  * || C' p2 || = || C' P1 ||
698  * is the same as :
699  * ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
700  *
701  * delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
702  *
703  * We can use this equation for any point p2 with p2.x < R
704  */
705 
706  if( v2.x == R )
707  {
708  // Straight line, do nothing
709  }
710  else
711  {
712  if( v2.x > R )
713  {
714  // If we need to invert the curvature.
715  // We modify the input so we can use the same equation
716  transformCircle = true;
717  v2.x = 2 * R - v2.x;
718  }
719 
720  // We can keep the tangent constraint.
721  double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
722 
723  // This is just to limit the radius, so nothing overflows later when drawing.
724  if( abs( v2.y / ( R - v2.x ) ) > ADVANCED_CFG::GetCfg().m_DrawArcCenterMaxAngle )
725  {
726  arcValid = false;
727  }
728 
729  // Never recorded a problem, but still checking.
730  if( !std::isfinite( delta ) )
731  {
732  arcValid = false;
733  }
734 
735  // v4 is the new center
736  v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
737 
738  clockwise = aArc->GetAngle() > 0;
739 
740  if( transformCircle )
741  clockwise = !clockwise;
742 
743  tmpx = v4.x * u1.x + v4.y * u2.x;
744  tmpy = v4.x * u1.y + v4.y * u2.y;
745  v4.x = tmpx;
746  v4.y = tmpy;
747 
748  center = v4 + aCenter;
749 
750  startLine = start - center;
751  endLine = end - center;
752  newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
753 
754  if( clockwise && newAngle < 0.0 )
755  newAngle += 3600.0;
756  else if( !clockwise && newAngle > 0.0 )
757  newAngle -= 3600.0;
758 
759  if( arcValid )
760  {
761  aArc->SetAngle( newAngle, false );
762  aArc->SetCenter( ( wxPoint ) center );
763 
764  if( movingStart )
765  aArc->SetArcStart( ( wxPoint ) start );
766  else
767  aArc->SetArcEnd( ( wxPoint ) end );
768  }
769  }
770 }
771 
772 
786 static void pinEditedCorner( int aEditedPointIndex, int aMinWidth, int aMinHeight,
787  VECTOR2I& aTopLeft, VECTOR2I& aTopRight, VECTOR2I& aBotLeft,
788  VECTOR2I& aBotRight, VECTOR2I& aHole, VECTOR2I& aHoleSize )
789 {
790  switch( aEditedPointIndex )
791  {
792  case RECT_TOP_LEFT:
793  if( aHoleSize.x )
794  {
795  // pin edited point to the top/left of the hole
796  aTopLeft.x = std::min( aTopLeft.x, aHole.x - aHoleSize.x / 2 - aMinWidth );
797  aTopLeft.y = std::min( aTopLeft.y, aHole.y - aHoleSize.y / 2 - aMinHeight );
798  }
799  else
800  {
801  // pin edited point within opposite corner
802  aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - aMinWidth );
803  aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - aMinHeight );
804  }
805 
806  // push edited point edges to adjacent corners
807  aTopRight.y = aTopLeft.y;
808  aBotLeft.x = aTopLeft.x;
809 
810  break;
811 
812  case RECT_TOP_RIGHT:
813  if( aHoleSize.x )
814  {
815  // pin edited point to the top/right of the hole
816  aTopRight.x = std::max( aTopRight.x, aHole.x + aHoleSize.x / 2 + aMinWidth );
817  aTopRight.y = std::min( aTopRight.y, aHole.y - aHoleSize.y / 2 - aMinHeight );
818  }
819  else
820  {
821  // pin edited point within opposite corner
822  aTopRight.x = std::max( aTopRight.x, aBotLeft.x + aMinWidth );
823  aTopRight.y = std::min( aTopRight.y, aBotLeft.y - aMinHeight );
824  }
825 
826  // push edited point edges to adjacent corners
827  aTopLeft.y = aTopRight.y;
828  aBotRight.x = aTopRight.x;
829 
830  break;
831 
832  case RECT_BOT_LEFT:
833  if( aHoleSize.x )
834  {
835  // pin edited point to the bottom/left of the hole
836  aBotLeft.x = std::min( aBotLeft.x, aHole.x - aHoleSize.x / 2 - aMinWidth );
837  aBotLeft.y = std::max( aBotLeft.y, aHole.y + aHoleSize.y / 2 + aMinHeight );
838  }
839  else
840  {
841  // pin edited point within opposite corner
842  aBotLeft.x = std::min( aBotLeft.x, aTopRight.x - aMinWidth );
843  aBotLeft.y = std::max( aBotLeft.y, aTopRight.y + aMinHeight );
844  }
845 
846  // push edited point edges to adjacent corners
847  aBotRight.y = aBotLeft.y;
848  aTopLeft.x = aBotLeft.x;
849 
850  break;
851 
852  case RECT_BOT_RIGHT:
853  if( aHoleSize.x )
854  {
855  // pin edited point to the bottom/right of the hole
856  aBotRight.x = std::max( aBotRight.x, aHole.x + aHoleSize.x / 2 + aMinWidth );
857  aBotRight.y = std::max( aBotRight.y, aHole.y + aHoleSize.y / 2 + aMinHeight );
858  }
859  else
860  {
861  // pin edited point within opposite corner
862  aBotRight.x = std::max( aBotRight.x, aTopLeft.x + aMinWidth );
863  aBotRight.y = std::max( aBotRight.y, aTopLeft.y + aMinHeight );
864  }
865 
866  // push edited point edges to adjacent corners
867  aBotLeft.y = aBotRight.y;
868  aTopRight.x = aBotRight.x;
869 
870  break;
871  }
872 }
873 
874 
876  const VECTOR2I& aStart, const VECTOR2I& aMid,
877  const VECTOR2I& aEnd,
878  const VECTOR2I& aCursor ) const
879 {
880  bool clockwise;
881  bool movingStart;
882 
883  VECTOR2I p1, p2;
884  VECTOR2I target;
885 
886  // p1 does not move, p2 does.
887 
888  if( aStart != aArc->GetArcStart() )
889  {
890  p1 = aEnd;
891  p2 = aStart;
892  movingStart = true;
893  }
894  else
895  {
896  p1 = aStart;
897  p2 = aEnd;
898  movingStart = false;
899  }
900 
901  target = p2 - aCenter;
902 
903  double sqRadius = ( p1 - aCenter ).SquaredEuclideanNorm();
904 
905  p1 = p1 - aCenter;
906  p2 = p2 - aCenter;
907 
908  // Circle : x^2 + y^2 = R ^ 2
909  // In this coordinate system, the angular position of the cursor is (r, theta)
910  // The line coming from the center of the circle is y = start.y / start.x * x
911  // The intersection fulfills : x^2 = R^2 / ( 1 + ( start.y / start.x ) ^ 2 )
912 
913  if( target.x == 0 )
914  {
915  p2.x = 0;
916  p2.y = ( target.y > 0 ) ? sqrt( sqRadius ) : -sqrt( sqRadius );
917  }
918  else
919  {
920  double tan = target.y / static_cast<double>( target.x );
921 
922  // The divider is always greater than 1 ( cannot be 0 )
923  double tmp = sqrt( sqRadius / ( 1.0 + tan * tan ) );
924 
925  // Move to the correct quadrant
926  tmp = target.x > 0 ? tmp : -tmp;
927  p2.y = target.y / static_cast<double>( target.x ) * tmp;
928  p2.x = tmp;
929  }
930 
931  p1 = p1 + aCenter;
932  p2 = p2 + aCenter;
933 
934  clockwise = aArc->GetAngle() > 0;
935 
936  VECTOR2D startLine = aStart - aCenter;
937  VECTOR2D endLine = aEnd - aCenter;
938  double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
939 
940  if( clockwise && newAngle < 0.0 )
941  newAngle += 3600.0;
942  else if( !clockwise && newAngle > 0.0 )
943  newAngle -= 3600.0;
944 
945  aArc->SetAngle( newAngle, false );
946  aArc->SetCenter( (wxPoint) aCenter );
947 
948  if( movingStart )
949  aArc->SetArcStart( (wxPoint) aStart );
950  else
951  aArc->SetArcEnd( (wxPoint) aEnd );
952 }
953 
954 
956  const VECTOR2I& aStart, const VECTOR2I& aMid,
957  const VECTOR2I& aEnd, const VECTOR2I& aCursor ) const
958 {
959  // Now, update the edit point position
960  // Express the point in a circle-centered coordinate system.
961  VECTOR2I start = aStart - aCenter;
962  VECTOR2I end = aEnd - aCenter;
963 
964  double sqRadius = ( aCursor - aCenter ).SquaredEuclideanNorm();
965 
966  // Special case, because the tangent would lead to +/- infinity
967  if( start.x == 0 )
968  {
969  start.y = aCursor.y > 0 ? sqrt( sqRadius ) : -sqrt( sqRadius );
970  }
971  else
972  {
973  // Circle : x^2 + y^2 = R ^ 2
974  // In this coordinate system, the angular position of the cursor is (r, theta)
975  // The line coming from the center of the circle is y = start.y / start.x * x
976  // The intersection fulfills : x^2 = R^2 / ( 1 + ( start.y / start.x ) ^ 2 )
977 
978  double tan = aStart.y / static_cast<double>( start.x );
979  double tmp = sqrt( sqRadius / ( 1.0 + tan * tan ) );
980 
981  // Move to the correct quadrant
982  tmp = start.x > 0 ? tmp : -tmp;
983  start.y = start.y / static_cast<double>( start.x ) * tmp;
984  start.x = tmp;
985  }
986 
987  // Special case, because the tangent would lead to +/- infinity
988  if( end.x == 0 )
989  {
990  end.y = aMid.y > 0 ? sqrt( sqRadius ) : -sqrt( sqRadius );
991  }
992  else
993  {
994  // Circle : x^2 + y^2 = R ^ 2
995  // In this coordinate system, the angular position of the cursor is (r, theta)
996  // The line coming from the center of the circle is y = start.y / start.x * x
997  // The intersection fulfills : x^2 = R^2 / ( 1 + ( start.y / start.x ) ^ 2 )
998 
999  double tan = end.y / static_cast<double>( end.x );
1000  double tmp = sqrt( sqRadius / ( 1.0 + tan * tan ) );
1001 
1002  // Move to the correct quadrant
1003  tmp = end.x > 0 ? tmp : -tmp;
1004  end.y = end.y / static_cast<double>( end.x ) * tmp;
1005  end.x = tmp;
1006  }
1007 
1008  start = start + aCenter;
1009  end = end + aCenter;
1010 
1011  aArc->SetArcStart( (wxPoint) start );
1012  aArc->SetArcEnd( (wxPoint) end );
1013 }
1014 
1015 
1017  const VECTOR2I& aEnd,
1018  const VECTOR2I& aCursor ) const
1019 {
1020  // Let 'm' be the middle point of the chord between the start and end points
1021  VECTOR2I m = ( aStart + aEnd ) / 2;
1022 
1023  // Legal midpoints lie on a vector starting just off the chord midpoint and extending out
1024  // past the existing midpoint. We do not allow arc inflection while point editing.
1025  const int JUST_OFF = ( aStart - aEnd ).EuclideanNorm() / 100;
1026  VECTOR2I v = (VECTOR2I) aArc->GetArcMid() - m;
1027  SEG legal( m + v.Resize( JUST_OFF ), m + v.Resize( INT_MAX / 2 ) );
1028  VECTOR2I mid = legal.NearestPoint( aCursor );
1029 
1030  aArc->SetArcGeometry( (wxPoint) aStart, (wxPoint) mid, (wxPoint) aEnd );
1031 }
1032 
1033 
1035 {
1036  EDA_ITEM* item = m_editPoints->GetParent();
1037 
1038  if( !item )
1039  return;
1040 
1041  switch( item->Type() )
1042  {
1043  case PCB_SHAPE_T:
1044  case PCB_FP_SHAPE_T:
1045  {
1046  PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1047 
1048  switch( shape->GetShape() )
1049  {
1050  case SHAPE_T::SEGMENT:
1051  if( isModified( m_editPoints->Point( SEG_START ) ) )
1052  shape->SetStart( (wxPoint) m_editPoints->Point( SEG_START ).GetPosition() );
1053  else if( isModified( m_editPoints->Point( SEG_END ) ) )
1054  shape->SetEnd( (wxPoint) m_editPoints->Point( SEG_END ).GetPosition() );
1055 
1056  break;
1057 
1058  case SHAPE_T::RECT:
1059  {
1060  if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) ) )
1061  {
1062  shape->SetStart( (wxPoint) m_editPoints->Point( RECT_TOP_LEFT ).GetPosition() );
1063  }
1064  else if( isModified( m_editPoints->Point( RECT_TOP_RIGHT ) ) )
1065  {
1066  shape->SetStartY( m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().y );
1067  shape->SetEndX( m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().x );
1068  }
1069  else if( isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1070  {
1071  shape->SetEnd( (wxPoint) m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition() );
1072  }
1073  else if( isModified( m_editPoints->Point( RECT_BOT_LEFT ) ) )
1074  {
1075  shape->SetStartX( m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().x );
1076  shape->SetEndY( m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().y );
1077  }
1078  else if( isModified( m_editPoints->Line( RECT_TOP ) ) )
1079  {
1080  shape->SetStartY( m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().y );
1081  }
1082  else if( isModified( m_editPoints->Line( RECT_LEFT ) ) )
1083  {
1084  shape->SetStartX( m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().x );
1085  }
1086  else if( isModified( m_editPoints->Line( RECT_BOT ) ) )
1087  {
1088  shape->SetEndY( m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().y );
1089  }
1090  else if( isModified( m_editPoints->Line( RECT_RIGHT ) ) )
1091  {
1092  shape->SetEndX( m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().x );
1093  }
1094 
1095  for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1096  {
1097  if( !isModified( m_editPoints->Line( i ) ) )
1098  m_editPoints->Line( i ).SetConstraint(
1099  new EC_PERPLINE( m_editPoints->Line( i ) ) );
1100  }
1101  }
1102  break;
1103 
1104  case SHAPE_T::ARC:
1105  {
1106  VECTOR2I center = m_editPoints->Point( ARC_CENTER ).GetPosition();
1107  VECTOR2I mid = m_editPoints->Point( ARC_MID ).GetPosition();
1108  VECTOR2I start = m_editPoints->Point( ARC_START ).GetPosition();
1109  VECTOR2I end = m_editPoints->Point( ARC_END ).GetPosition();
1110 
1111  if( isModified( m_editPoints->Point( ARC_CENTER ) ) )
1112  {
1113  wxPoint moveVector = wxPoint( center.x, center.y ) - shape->GetCenter();
1114  shape->Move( moveVector );
1115  }
1116  else if( isModified( m_editPoints->Point( ARC_MID ) ) )
1117  {
1118  const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition( false );
1119 
1120  if( m_altEditMethod )
1121  editArcMidKeepCenter( shape, center, start, mid, end, cursorPos );
1122  else
1123  editArcMidKeepEndpoints( shape, start, end, cursorPos );
1124  }
1125  else if( isModified( m_editPoints->Point( ARC_START ) )
1126  || isModified( m_editPoints->Point( ARC_END ) ) )
1127  {
1128  const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
1129 
1130  if( m_altEditMethod )
1131  editArcEndpointKeepCenter( shape, center, start, mid, end, cursorPos );
1132  else
1133  editArcEndpointKeepTangent( shape, center, start, mid, end, cursorPos );
1134  }
1135  }
1136  break;
1137 
1138  case SHAPE_T::CIRCLE:
1139  {
1140  const VECTOR2I& center = m_editPoints->Point( CIRC_CENTER ).GetPosition();
1141  const VECTOR2I& end = m_editPoints->Point( CIRC_END ).GetPosition();
1142 
1143  if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
1144  {
1145  wxPoint moveVector = wxPoint( center.x, center.y ) - shape->GetCenter();
1146  shape->Move( moveVector );
1147  }
1148  else
1149  {
1150  shape->SetEnd( wxPoint( end.x, end.y ) );
1151  }
1152  }
1153  break;
1154 
1155  case SHAPE_T::POLY:
1156  {
1157  SHAPE_POLY_SET& outline = shape->GetPolyShape();
1158 
1159  for( int i = 0; i < outline.TotalVertices(); ++i )
1160  outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
1161 
1162  for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1163  {
1164  if( !isModified( m_editPoints->Line( i ) ) )
1165  m_editPoints->Line( i ).SetConstraint(
1166  new EC_PERPLINE( m_editPoints->Line( i ) ) );
1167  }
1168 
1169  validatePolygon( outline );
1170  }
1171  break;
1172 
1173  case SHAPE_T::BEZIER:
1174  if( isModified( m_editPoints->Point( BEZIER_CURVE_START ) ) )
1175  shape->SetStart( (wxPoint) m_editPoints->Point( BEZIER_CURVE_START ).
1176  GetPosition() );
1177  else if( isModified( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ) ) )
1178  shape->SetBezierC1( (wxPoint) m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).
1179  GetPosition() );
1180  else if( isModified( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ) ) )
1181  shape->SetBezierC2( (wxPoint) m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).
1182  GetPosition() );
1183  else if( isModified( m_editPoints->Point( BEZIER_CURVE_END ) ) )
1184  shape->SetEnd( (wxPoint) m_editPoints->Point( BEZIER_CURVE_END ).GetPosition() );
1185 
1186  shape->RebuildBezierToSegmentsPointsList( shape->GetWidth() );
1187  break;
1188 
1189  default: // suppress warnings
1190  break;
1191  }
1192 
1193  // Update relative coordinates for footprint shapes
1194  if( FP_SHAPE* fpShape = dyn_cast<FP_SHAPE*>( item ) )
1195  fpShape->SetLocalCoord();
1196 
1197  break;
1198  }
1199 
1200  case PCB_PAD_T:
1201  {
1202  PAD* pad = static_cast<PAD*>( item );
1203 
1204  switch( pad->GetShape() )
1205  {
1206  case PAD_SHAPE::CIRCLE:
1207  {
1208  wxPoint center = (wxPoint) m_editPoints->Point( CIRC_CENTER ).GetPosition();
1209  wxPoint end = (wxPoint) m_editPoints->Point( CIRC_END ).GetPosition();
1210 
1211  if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
1212  {
1213  wxPoint moveVector = center - pad->ShapePos();
1214  pad->SetOffset( pad->GetOffset() + moveVector );
1215  }
1216  else
1217  {
1218  int diameter = (int) EuclideanNorm( end - center ) * 2;
1219  pad->SetSize( wxSize( diameter, diameter ) );
1220  }
1221  }
1222  break;
1223 
1224  case PAD_SHAPE::OVAL:
1225  case PAD_SHAPE::TRAPEZOID:
1226  case PAD_SHAPE::RECT:
1227  case PAD_SHAPE::ROUNDRECT:
1229  {
1230  VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
1231  VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
1232  VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
1233  VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
1234  VECTOR2I holeCenter = pad->GetPosition();
1235  VECTOR2I holeSize = pad->GetDrillSize();
1236 
1237  pinEditedCorner( getEditedPointIndex(), Mils2iu( 1 ), Mils2iu( 1 ), topLeft, topRight,
1238  botLeft, botRight, holeCenter, holeSize );
1239 
1240  if( ( pad->GetOffset().x || pad->GetOffset().y )
1241  || ( pad->GetDrillSize().x && pad->GetDrillSize().y ) )
1242  {
1243  // Keep hole pinned at the current location; adjust the pad around the hole
1244 
1245  wxPoint center = pad->GetPosition();
1246  int dist[4];
1247 
1248  if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1249  || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1250  {
1251  dist[0] = center.x - topLeft.x;
1252  dist[1] = center.y - topLeft.y;
1253  dist[2] = botRight.x - center.x;
1254  dist[3] = botRight.y - center.y;
1255  }
1256  else
1257  {
1258  dist[0] = center.x - botLeft.x;
1259  dist[1] = center.y - topRight.y;
1260  dist[2] = topRight.x - center.x;
1261  dist[3] = botLeft.y - center.y;
1262  }
1263 
1264  wxSize padSize( dist[0] + dist[2], dist[1] + dist[3] );
1265  wxPoint deltaOffset( padSize.x / 2 - dist[2], padSize.y / 2 - dist[3] );
1266 
1267  if( pad->GetOrientation() == 900 || pad->GetOrientation() == 2700 )
1268  std::swap( padSize.x, padSize.y );
1269 
1270  RotatePoint( &deltaOffset, -pad->GetOrientation() );
1271 
1272  pad->SetSize( padSize );
1273  pad->SetOffset( -deltaOffset );
1274  }
1275  else
1276  {
1277  // Keep pad position at the center of the pad shape
1278 
1279  int left, top, right, bottom;
1280 
1281  if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1282  || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1283  {
1284  left = topLeft.x;
1285  top = topLeft.y;
1286  right = botRight.x;
1287  bottom = botRight.y;
1288  }
1289  else
1290  {
1291  left = botLeft.x;
1292  top = topRight.y;
1293  right = topRight.x;
1294  bottom = botLeft.y;
1295  }
1296 
1297  wxSize padSize( abs( right - left ), abs( bottom - top ) );
1298 
1299  if( pad->GetOrientation() == 900 || pad->GetOrientation() == 2700 )
1300  std::swap( padSize.x, padSize.y );
1301 
1302  pad->SetSize( padSize );
1303  pad->SetPosition( wxPoint( ( left + right ) / 2, ( top + bottom ) / 2 ) );
1304  }
1305  }
1306  break;
1307 
1308  default: // suppress warnings
1309  break;
1310  }
1311 
1312  break;
1313  }
1314 
1315  case PCB_FP_ZONE_T:
1316  case PCB_ZONE_T:
1317  {
1318  ZONE* zone = static_cast<ZONE*>( item );
1319  zone->UnFill();
1320  SHAPE_POLY_SET& outline = *zone->Outline();
1321 
1322  for( int i = 0; i < outline.TotalVertices(); ++i )
1323  {
1324  if( outline.CVertex( i ) != m_editPoints->Point( i ).GetPosition() )
1325  zone->SetNeedRefill( true );
1326 
1327  outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
1328  }
1329 
1330  for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1331  {
1332  if( !isModified( m_editPoints->Line( i ) ) )
1333  m_editPoints->Line( i ).SetConstraint( new EC_PERPLINE( m_editPoints->Line( i ) ) );
1334  }
1335 
1336  validatePolygon( outline );
1337  zone->HatchBorder();
1338 
1339  // TODO Refill zone when KiCad supports auto re-fill
1340  break;
1341  }
1342 
1343  case PCB_DIM_ALIGNED_T:
1344  {
1345  PCB_DIM_ALIGNED* dimension = static_cast<PCB_DIM_ALIGNED*>( item );
1346 
1347  // Check which point is currently modified and updated dimension's points respectively
1348  if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) )
1349  {
1350  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetStart() );
1351  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1352 
1353  if( featureLine.Cross( crossBar ) > 0 )
1354  dimension->SetHeight( -featureLine.EuclideanNorm() );
1355  else
1356  dimension->SetHeight( featureLine.EuclideanNorm() );
1357 
1358  dimension->Update();
1359  }
1360  else if( isModified( m_editPoints->Point( DIM_CROSSBAREND ) ) )
1361  {
1362  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
1363  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1364 
1365  if( featureLine.Cross( crossBar ) > 0 )
1366  dimension->SetHeight( -featureLine.EuclideanNorm() );
1367  else
1368  dimension->SetHeight( featureLine.EuclideanNorm() );
1369 
1370  dimension->Update();
1371  }
1372  else if( isModified( m_editPoints->Point( DIM_START ) ) )
1373  {
1374  dimension->SetStart( (wxPoint) m_editedPoint->GetPosition() );
1375  dimension->Update();
1376 
1377  m_editPoints->Point( DIM_CROSSBARSTART ).
1378  SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1379  m_editPoints->Point( DIM_START ) ) );
1380  m_editPoints->Point( DIM_CROSSBAREND ).
1381  SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1382  m_editPoints->Point( DIM_END ) ) );
1383  }
1384  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1385  {
1386  dimension->SetEnd( (wxPoint) m_editedPoint->GetPosition() );
1387  dimension->Update();
1388 
1389  m_editPoints->Point( DIM_CROSSBARSTART ).
1390  SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1391  m_editPoints->Point( DIM_START ) ) );
1392  m_editPoints->Point( DIM_CROSSBAREND ).
1393  SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1394  m_editPoints->Point( DIM_END ) ) );
1395  }
1396  else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1397  {
1398  // Force manual mode if we weren't already in it
1400  dimension->Text().SetPosition( wxPoint( m_editedPoint->GetPosition() ) );
1401  dimension->Update();
1402  }
1403 
1404  break;
1405  }
1406 
1407  case PCB_DIM_ORTHOGONAL_T:
1408  {
1409  PCB_DIM_ORTHOGONAL* dimension = static_cast<PCB_DIM_ORTHOGONAL*>( item );
1410 
1411  if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) ||
1412  isModified( m_editPoints->Point( DIM_CROSSBAREND ) ) )
1413  {
1414  BOX2I bounds( dimension->GetStart(), dimension->GetEnd() - dimension->GetStart() );
1415 
1416  const VECTOR2I& cursorPos = m_editedPoint->GetPosition();
1417 
1418  // Find vector from nearest dimension point to edit position
1419  VECTOR2I directionA( cursorPos - dimension->GetStart() );
1420  VECTOR2I directionB( cursorPos - dimension->GetEnd() );
1421  VECTOR2I direction = ( directionA < directionB ) ? directionA : directionB;
1422 
1423  bool vert;
1424  VECTOR2D featureLine( cursorPos - dimension->GetStart() );
1425 
1426  // Only change the orientation when we move outside the bounds
1427  if( !bounds.Contains( cursorPos ) )
1428  {
1429  // If the dimension is horizontal or vertical, set correct orientation
1430  // otherwise, test if we're left/right of the bounding box or above/below it
1431  if( bounds.GetWidth() == 0 )
1432  vert = true;
1433  else if( bounds.GetHeight() == 0 )
1434  vert = false;
1435  else if( cursorPos.x > bounds.GetLeft() && cursorPos.x < bounds.GetRight() )
1436  vert = false;
1437  else if( cursorPos.y > bounds.GetTop() && cursorPos.y < bounds.GetBottom() )
1438  vert = true;
1439  else
1440  vert = std::abs( direction.y ) < std::abs( direction.x );
1441 
1444  }
1445  else
1446  {
1447  vert = dimension->GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1448  }
1449 
1450  dimension->SetHeight( vert ? featureLine.x : featureLine.y );
1451  }
1452  else if( isModified( m_editPoints->Point( DIM_START ) ) )
1453  {
1454  dimension->SetStart( (wxPoint) m_editedPoint->GetPosition() );
1455  }
1456  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1457  {
1458  dimension->SetEnd( (wxPoint) m_editedPoint->GetPosition() );
1459  }
1460  else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1461  {
1462  // Force manual mode if we weren't already in it
1464  dimension->Text().SetPosition( wxPoint( m_editedPoint->GetPosition() ) );
1465  }
1466 
1467  dimension->Update();
1468 
1469  break;
1470  }
1471 
1472  case PCB_DIM_CENTER_T:
1473  {
1474  PCB_DIM_CENTER* dimension = static_cast<PCB_DIM_CENTER*>( item );
1475 
1476  if( isModified( m_editPoints->Point( DIM_START ) ) )
1477  dimension->SetStart( (wxPoint) m_editedPoint->GetPosition() );
1478  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1479  dimension->SetEnd( (wxPoint) m_editedPoint->GetPosition() );
1480 
1481  dimension->Update();
1482 
1483  break;
1484  }
1485 
1486  case PCB_DIM_LEADER_T:
1487  {
1488  PCB_DIM_LEADER* dimension = static_cast<PCB_DIM_LEADER*>( item );
1489 
1490  if( isModified( m_editPoints->Point( DIM_START ) ) )
1491  {
1492  dimension->SetStart( (wxPoint) m_editedPoint->GetPosition() );
1493  }
1494  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1495  {
1496  wxPoint newPoint( m_editedPoint->GetPosition() );
1497  wxPoint delta = newPoint - dimension->GetEnd();
1498 
1499  dimension->SetEnd( newPoint );
1500  dimension->Text().SetPosition( dimension->Text().GetPosition() + delta );
1501  }
1502  else if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
1503  {
1504  dimension->Text().SetPosition( (wxPoint) m_editedPoint->GetPosition() );
1505  }
1506 
1507  dimension->Update();
1508 
1509  break;
1510  }
1511 
1512  default:
1513  break;
1514  }
1515 
1516  getView()->Update( item );
1517 }
1518 
1519 
1521 {
1522  auto item = m_editPoints->GetParent();
1523 
1524  if( !item )
1525  return;
1526 
1527  // TODO Refill edited zones when KiCad supports auto re-fill
1528 }
1529 
1530 
1532 {
1533  bool valid = !aPoly.IsSelfIntersecting();
1534 
1535  if( m_statusPopup )
1536  {
1537  if( valid )
1538  {
1539  m_statusPopup->Hide();
1540  }
1541  else
1542  {
1543  wxPoint p = wxGetMousePosition() + wxPoint( 20, 20 );
1544  m_statusPopup->Move( p );
1545  m_statusPopup->PopupFor( 1500 );
1546  }
1547  }
1548 
1549  return valid;
1550 }
1551 
1552 
1554 {
1555  if( !m_editPoints )
1556  return;
1557 
1558  EDA_ITEM* item = m_editPoints->GetParent();
1559 
1560  if( !item )
1561  return;
1562 
1563  switch( item->Type() )
1564  {
1565  case PCB_SHAPE_T:
1566  case PCB_FP_SHAPE_T:
1567  {
1568  const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
1569 
1570  switch( shape->GetShape() )
1571  {
1572  case SHAPE_T::SEGMENT:
1573  m_editPoints->Point( SEG_START ).SetPosition( shape->GetStart() );
1574  m_editPoints->Point( SEG_END ).SetPosition( shape->GetEnd() );
1575  break;
1576 
1577  case SHAPE_T::RECT:
1578  m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shape->GetStart() );
1579  m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( shape->GetEnd().x,
1580  shape->GetStart().y );
1581  m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shape->GetEnd() );
1582  m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( shape->GetStart().x,
1583  shape->GetEnd().y );
1584  break;
1585 
1586  case SHAPE_T::ARC:
1587  m_editPoints->Point( ARC_CENTER ).SetPosition( shape->GetCenter() );
1588  m_editPoints->Point( ARC_START ).SetPosition( shape->GetArcStart() );
1589  m_editPoints->Point( ARC_MID ).SetPosition( shape->GetArcMid() );
1590  m_editPoints->Point( ARC_END ).SetPosition( shape->GetArcEnd() );
1591  break;
1592 
1593  case SHAPE_T::CIRCLE:
1594  m_editPoints->Point( CIRC_CENTER ).SetPosition( shape->GetCenter() );
1595  m_editPoints->Point( CIRC_END ).SetPosition( shape->GetEnd() );
1596  break;
1597 
1598  case SHAPE_T::POLY:
1599  {
1600  std::vector<wxPoint> points;
1601  shape->DupPolyPointsList( points );
1602 
1603  if( m_editPoints->PointsSize() != (unsigned) points.size() )
1604  {
1605  getView()->Remove( m_editPoints.get() );
1606  m_editedPoint = nullptr;
1607 
1608  m_editPoints = makePoints( item );
1609 
1610  if( m_editPoints )
1611  getView()->Add( m_editPoints.get() );
1612  }
1613  else
1614  {
1615  for( unsigned i = 0; i < points.size(); i++ )
1616  m_editPoints->Point( i ).SetPosition( points[i] );
1617  }
1618 
1619  break;
1620  }
1621 
1622  case SHAPE_T::BEZIER:
1623  m_editPoints->Point( BEZIER_CURVE_START ).SetPosition( shape->GetStart() );
1624  m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).SetPosition( shape->GetBezierC1() );
1625  m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).SetPosition( shape->GetBezierC2() );
1626  m_editPoints->Point( BEZIER_CURVE_END ).SetPosition( shape->GetEnd() );
1627  break;
1628 
1629  default: // suppress warnings
1630  break;
1631  }
1632 
1633  break;
1634  }
1635 
1636  case PCB_PAD_T:
1637  {
1638  const PAD* pad = static_cast<const PAD*>( item );
1639  bool locked = pad->GetParent() && pad->IsLocked();
1640  wxPoint shapePos = pad->ShapePos();
1641  wxPoint halfSize( pad->GetSize().x / 2, pad->GetSize().y / 2 );
1642 
1643  switch( pad->GetShape() )
1644  {
1645  case PAD_SHAPE::CIRCLE:
1646  {
1647  int target = locked ? 0 : 2;
1648 
1649  // Careful; pad shape is mutable...
1650  if( int( m_editPoints->PointsSize() ) != target )
1651  {
1652  getView()->Remove( m_editPoints.get() );
1653  m_editedPoint = nullptr;
1654 
1655  m_editPoints = makePoints( item );
1656 
1657  if( m_editPoints )
1658  getView()->Add( m_editPoints.get() );
1659  }
1660  else if( target == 2 )
1661  {
1662  VECTOR2I vec = m_editPoints->Point( CIRC_END ).GetPosition()
1663  - m_editPoints->Point( CIRC_CENTER ).GetPosition();
1664  vec.Resize( halfSize.x );
1665 
1666  m_editPoints->Point( CIRC_CENTER ).SetPosition( shapePos );
1667  m_editPoints->Point( CIRC_END ).SetPosition( vec + shapePos );
1668  }
1669  }
1670  break;
1671 
1672  case PAD_SHAPE::OVAL:
1673  case PAD_SHAPE::TRAPEZOID:
1674  case PAD_SHAPE::RECT:
1675  case PAD_SHAPE::ROUNDRECT:
1677  {
1678  // Careful; pad shape and orientation are mutable...
1679  int target = locked || (int) pad->GetOrientation() % 900 > 0 ? 0 : 4;
1680 
1681  if( int( m_editPoints->PointsSize() ) != target )
1682  {
1683  getView()->Remove( m_editPoints.get() );
1684  m_editedPoint = nullptr;
1685 
1686  m_editPoints = makePoints( item );
1687 
1688  if( m_editPoints )
1689  getView()->Add( m_editPoints.get() );
1690  }
1691  else if( target == 4 )
1692  {
1693  if( pad->GetOrientation() == 900 || pad->GetOrientation() == 2700 )
1694  std::swap( halfSize.x, halfSize.y );
1695 
1696  m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shapePos - halfSize );
1697  m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( wxPoint( shapePos.x + halfSize.x,
1698  shapePos.y - halfSize.y ) );
1699  m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shapePos + halfSize );
1700  m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( wxPoint( shapePos.x - halfSize.x,
1701  shapePos.y + halfSize.y ) );
1702  }
1703 
1704  break;
1705  }
1706 
1707  default: // suppress warnings
1708  break;
1709  }
1710  }
1711  break;
1712 
1713  case PCB_FP_ZONE_T:
1714  case PCB_ZONE_T:
1715  {
1716  ZONE* zone = static_cast<ZONE*>( item );
1717  const SHAPE_POLY_SET* outline = zone->Outline();
1718 
1719  if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
1720  {
1721  getView()->Remove( m_editPoints.get() );
1722  m_editedPoint = nullptr;
1723 
1724  m_editPoints = makePoints( item );
1725 
1726  if( m_editPoints )
1727  getView()->Add( m_editPoints.get() );
1728  }
1729  else
1730  {
1731  for( int i = 0; i < outline->TotalVertices(); ++i )
1732  m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
1733  }
1734 
1735  break;
1736  }
1737 
1738  case PCB_DIM_ALIGNED_T:
1739  case PCB_DIM_ORTHOGONAL_T:
1740  {
1741  const PCB_DIM_ALIGNED* dimension = static_cast<const PCB_DIM_ALIGNED*>( item );
1742 
1743  m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1744  m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1745  m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->Text().GetPosition() );
1746  m_editPoints->Point( DIM_CROSSBARSTART ).SetPosition( dimension->GetCrossbarStart() );
1747  m_editPoints->Point( DIM_CROSSBAREND ).SetPosition( dimension->GetCrossbarEnd() );
1748  break;
1749  }
1750 
1751  case PCB_DIM_CENTER_T:
1752  {
1753  const PCB_DIM_CENTER* dimension = static_cast<const PCB_DIM_CENTER*>( item );
1754 
1755  m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1756  m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1757  break;
1758  }
1759 
1760  case PCB_DIM_LEADER_T:
1761  {
1762  const PCB_DIM_LEADER* dimension = static_cast<const PCB_DIM_LEADER*>( item );
1763 
1764  m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1765  m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1766  m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->Text().GetPosition() );
1767  break;
1768  }
1769 
1770  default:
1771  break;
1772  }
1773 
1774  getView()->Update( m_editPoints.get() );
1775 }
1776 
1777 
1779 {
1781 
1782  if( aPoint )
1783  {
1785  controls->ForceCursorPosition( true, aPoint->GetPosition() );
1786  controls->ShowCursor( true );
1787  }
1788  else
1789  {
1790  if( frame()->ToolStackIsEmpty() )
1791  controls->ShowCursor( false );
1792 
1793  controls->ForceCursorPosition( false );
1794  }
1795 
1796  m_editedPoint = aPoint;
1797 }
1798 
1799 
1801 {
1802  if( aEnabled )
1803  {
1804  EDA_ITEM* parent = m_editPoints->GetParent();
1805  EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
1806  bool isPoly;
1807 
1808  switch( parent->Type() )
1809  {
1810  case PCB_ZONE_T:
1811  case PCB_FP_ZONE_T:
1812  isPoly = true;
1813  break;
1814 
1815  case PCB_SHAPE_T:
1816  case PCB_FP_SHAPE_T:
1817  isPoly = static_cast<PCB_SHAPE*>( parent )->GetShape() == SHAPE_T::POLY;
1818  break;
1819 
1820  default:
1821  isPoly = false;
1822  break;
1823  }
1824 
1825  if( line && isPoly )
1826  {
1827  EC_CONVERGING* altConstraint = new EC_CONVERGING( *line, *m_editPoints );
1828  m_altConstraint.reset( (EDIT_CONSTRAINT<EDIT_POINT>*) altConstraint );
1829  }
1830  else
1831  {
1832  // Find a proper constraining point for 45 degrees mode
1835  }
1836  }
1837  else
1838  {
1839  m_altConstraint.reset();
1840  }
1841 }
1842 
1843 
1845 {
1846  EDA_ITEM* item = m_editPoints->GetParent();
1847 
1848  switch( item->Type() )
1849  {
1850  case PCB_SHAPE_T:
1851  case PCB_FP_SHAPE_T:
1852  switch( static_cast<const PCB_SHAPE*>( item )->GetShape() )
1853  {
1854  case SHAPE_T::SEGMENT:
1855  return *( m_editPoints->Next( *m_editedPoint ) ); // select the other end of line
1856 
1857  case SHAPE_T::ARC:
1858  case SHAPE_T::CIRCLE:
1859  return m_editPoints->Point( CIRC_CENTER );
1860 
1861  default: // suppress warnings
1862  break;
1863  }
1864 
1865  break;
1866 
1867  case PCB_DIM_ALIGNED_T:
1868  {
1869  // Constraint for crossbar
1870  if( isModified( m_editPoints->Point( DIM_START ) ) )
1871  return m_editPoints->Point( DIM_END );
1872 
1873  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1874  return m_editPoints->Point( DIM_START );
1875 
1876  else
1877  return EDIT_POINT( m_editedPoint->GetPosition() ); // no constraint
1878 
1879  break;
1880  }
1881 
1882  case PCB_DIM_CENTER_T:
1883  {
1884  if( isModified( m_editPoints->Point( DIM_END ) ) )
1885  return m_editPoints->Point( DIM_START );
1886 
1887  break;
1888  }
1889 
1890  default:
1891  break;
1892  }
1893 
1894  // In any other case we may align item to its original position
1895  return m_original;
1896 }
1897 
1898 
1900 {
1901  const auto type = aItem.Type();
1902 
1903  // Works only for zones and line segments
1904  if( type == PCB_ZONE_T || type == PCB_FP_ZONE_T )
1905  return true;
1906 
1907  if( type == PCB_SHAPE_T || type == PCB_FP_SHAPE_T )
1908  {
1909  const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
1910  return shape.GetShape() == SHAPE_T::SEGMENT || shape.GetShape() == SHAPE_T::POLY;
1911  }
1912 
1913  return false;
1914 }
1915 
1916 
1918 {
1919  if( aSelection.Size() != 1 )
1920  return false;
1921 
1922  const EDA_ITEM* item = aSelection.Front();
1923 
1924  return ( item != nullptr ) && canAddCorner( *item );
1925 }
1926 
1927 
1928 // Finds a corresponding vertex in a polygon set
1929 static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
1930 findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
1931 {
1932  for( auto it = aPolySet.IterateWithHoles(); it; ++it )
1933  {
1934  auto vertexIdx = it.GetIndex();
1935 
1936  if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
1937  return std::make_pair( true, vertexIdx );
1938  }
1939 
1940  return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
1941 }
1942 
1943 
1945 {
1946  if( !m_editPoints || !m_editedPoint )
1947  return false;
1948 
1949  EDA_ITEM* item = m_editPoints->GetParent();
1950  SHAPE_POLY_SET* polyset = nullptr;
1951 
1952  if( !item )
1953  return false;
1954 
1955  switch( item->Type() )
1956  {
1957  case PCB_ZONE_T:
1958  case PCB_FP_ZONE_T:
1959  polyset = static_cast<ZONE*>( item )->Outline();
1960  break;
1961 
1962  case PCB_SHAPE_T:
1963  case PCB_FP_SHAPE_T:
1964  if( static_cast<PCB_SHAPE*>( item )->GetShape() == SHAPE_T::POLY )
1965  polyset = &static_cast<PCB_SHAPE*>( item )->GetPolyShape();
1966  else
1967  return false;
1968 
1969  break;
1970 
1971  default:
1972  return false;
1973  }
1974 
1975  auto vertex = findVertex( *polyset, *m_editedPoint );
1976 
1977  if( !vertex.first )
1978  return false;
1979 
1980  const auto& vertexIdx = vertex.second;
1981 
1982  // Check if there are enough vertices so one can be removed without
1983  // degenerating the polygon.
1984  // The first condition allows one to remove all corners from holes (when
1985  // there are only 2 vertices left, a hole is removed).
1986  if( vertexIdx.m_contour == 0 &&
1987  polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
1988  return false;
1989 
1990  // Remove corner does not work with lines
1991  if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
1992  return false;
1993 
1994  return m_editedPoint != nullptr;
1995 }
1996 
1997 
1999 {
2000  if( !m_editPoints )
2001  return 0;
2002 
2003  EDA_ITEM* item = m_editPoints->GetParent();
2004  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2005  const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
2006 
2007  // called without an active edited polygon
2008  if( !item || !canAddCorner( *item ) )
2009  return 0;
2010 
2011  PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
2012  BOARD_COMMIT commit( frame );
2013 
2014  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T
2015  || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
2016  {
2017  unsigned int nearestIdx = 0;
2018  unsigned int nextNearestIdx = 0;
2019  unsigned int nearestDist = INT_MAX;
2020  unsigned int firstPointInContour = 0;
2021  SHAPE_POLY_SET* zoneOutline;
2022 
2023  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2024  {
2025  ZONE* zone = static_cast<ZONE*>( item );
2026  zoneOutline = zone->Outline();
2027  zone->SetNeedRefill( true );
2028  }
2029  else
2030  zoneOutline = &( graphicItem->GetPolyShape() );
2031 
2032  commit.Modify( item );
2033 
2034  // Search the best outline segment to add a new corner
2035  // and therefore break this segment into two segments
2036 
2037  // Object to iterate through the corners of the outlines (main contour and its holes)
2038  SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0, zoneOutline->OutlineCount()-1,
2039  /* IterateHoles */ true );
2040  int curr_idx = 0;
2041 
2042  // Iterate through all the corners of the outlines and search the best segment
2043  for( ; iterator; iterator++, curr_idx++ )
2044  {
2045  int jj = curr_idx+1;
2046 
2047  if( iterator.IsEndContour() )
2048  { // We reach the last point of the current contour (main or hole)
2049  jj = firstPointInContour;
2050  firstPointInContour = curr_idx+1; // Prepare next contour analysis
2051  }
2052 
2053  SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
2054 
2055  unsigned int distance = curr_segment.Distance( cursorPos );
2056 
2057  if( distance < nearestDist )
2058  {
2059  nearestDist = distance;
2060  nearestIdx = curr_idx;
2061  nextNearestIdx = jj;
2062  }
2063  }
2064 
2065  // Find the point on the closest segment
2066  auto& sideOrigin = zoneOutline->CVertex( nearestIdx );
2067  auto& sideEnd = zoneOutline->CVertex( nextNearestIdx );
2068  SEG nearestSide( sideOrigin, sideEnd );
2069  VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
2070 
2071  // Do not add points that have the same coordinates as ones that already belong to polygon
2072  // instead, add a point in the middle of the side
2073  if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
2074  nearestPoint = ( sideOrigin + sideEnd ) / 2;
2075 
2076  zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
2077 
2078  // We re-hatch the filled zones but not polygons
2079  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2080  static_cast<ZONE*>( item )->HatchBorder();
2081 
2082 
2083  commit.Push( _( "Add a zone corner" ) );
2084  }
2085  else if( graphicItem && graphicItem->GetShape() == SHAPE_T::SEGMENT )
2086  {
2087  commit.Modify( graphicItem );
2088 
2089  SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
2090  VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
2091 
2092  // Move the end of the line to the break point..
2093  graphicItem->SetEnd( wxPoint( nearestPoint.x, nearestPoint.y ) );
2094 
2095  if( graphicItem->Type() == PCB_FP_SHAPE_T )
2096  static_cast<FP_SHAPE*>( graphicItem )->SetLocalCoord();
2097 
2098  // and add another one starting from the break point
2099  PCB_SHAPE* newSegment;
2100 
2101  if( item->Type() == PCB_FP_SHAPE_T )
2102  {
2103  FP_SHAPE* edge = static_cast<FP_SHAPE*>( graphicItem );
2104  assert( edge->GetParent()->Type() == PCB_FOOTPRINT_T );
2105  newSegment = new FP_SHAPE( *edge );
2106  }
2107  else
2108  {
2109  newSegment = new PCB_SHAPE( *graphicItem );
2110  }
2111 
2112  newSegment->ClearSelected();
2113  newSegment->SetStart( wxPoint( nearestPoint.x, nearestPoint.y ) );
2114  newSegment->SetEnd( wxPoint( seg.B.x, seg.B.y ) );
2115 
2116  if( newSegment->Type() == PCB_FP_SHAPE_T )
2117  static_cast<FP_SHAPE*>( newSegment )->SetLocalCoord();
2118 
2119  commit.Add( newSegment );
2120  commit.Push( _( "Split segment" ) );
2121  }
2122 
2123  updatePoints();
2124  return 0;
2125 }
2126 
2127 
2129 {
2130  if( !m_editPoints || !m_editedPoint )
2131  return 0;
2132 
2133  EDA_ITEM* item = m_editPoints->GetParent();
2134 
2135  if( !item )
2136  return 0;
2137 
2138  SHAPE_POLY_SET* polygon = nullptr;
2139 
2140  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2141  {
2142  ZONE* zone = static_cast<ZONE*>( item );
2143  polygon = zone->Outline();
2144  zone->SetNeedRefill( true );
2145  }
2146  else if( item->Type() == PCB_FP_SHAPE_T || item->Type() == PCB_SHAPE_T )
2147  {
2148  PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2149 
2150  if( shape->GetShape() == SHAPE_T::POLY )
2151  polygon = &shape->GetPolyShape();
2152  }
2153 
2154  if( !polygon )
2155  return 0;
2156 
2157  PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
2158  BOARD_COMMIT commit( frame );
2159  auto vertex = findVertex( *polygon, *m_editedPoint );
2160 
2161  if( vertex.first )
2162  {
2163  const auto& vertexIdx = vertex.second;
2164  auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
2165 
2166  if( outline.PointCount() > 3 )
2167  {
2168  // the usual case: remove just the corner when there are >3 vertices
2169  commit.Modify( item );
2170  polygon->RemoveVertex( vertexIdx );
2171  validatePolygon( *polygon );
2172  }
2173  else
2174  {
2175  // either remove a hole or the polygon when there are <= 3 corners
2176  if( vertexIdx.m_contour > 0 )
2177  {
2178  // remove hole
2179  commit.Modify( item );
2180  polygon->RemoveContour( vertexIdx.m_contour );
2181  }
2182  else
2183  {
2185  commit.Remove( item );
2186  }
2187  }
2188 
2189  setEditedPoint( nullptr );
2190 
2191  commit.Push( _( "Remove a zone/polygon corner" ) );
2192 
2193  // Refresh zone hatching
2194  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2195  static_cast<ZONE*>( item )->HatchBorder();
2196 
2197  updatePoints();
2198  }
2199 
2200  return 0;
2201 }
2202 
2203 
2205 {
2206  updatePoints();
2207  return 0;
2208 }
2209 
2210 
2212 {
2214  return 0;
2215 }
2216 
2217 
2219 {
2229 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:146
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:59
int removeCorner(const TOOL_EVENT &aEvent)
int TotalVertices() const
Delete aIdx-th polygon from the set.
void SetStartX(int x)
Definition: pcb_shape.h:129
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
EDIT_CONSTRAINT for 3 segments: dragged and two adjacent ones, enforcing to keep their slopes and all...
wxPoint GetArcEnd() const
Definition: pcb_shape.cpp:417
void editArcEndpointKeepTangent(PCB_SHAPE *aArc, const VECTOR2I &aCenter, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, const VECTOR2I &aCursor) const
Move an end point of the arc, while keeping the tangent at the other endpoint.
void editArcMidKeepEndpoints(PCB_SHAPE *aArc, const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCursor) const
Move the mid point of the arc, while keeping the two endpoints.
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
Arcs (with rounded ends)
static const TOOL_EVENT SelectedEvent
Definition: actions.h:199
Bezier Curve.
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:100
VECTOR2I v2(1, 0)
Test suite for KiCad math code.
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:101
int OutlineCount() const
Return the number of vertices in a given outline/hole.
void setTransitions() override
< Set up handlers for various events.
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
const wxPoint & GetCrossbarStart() const
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
polygon (not yet used for tracks, but could be in microwave apps)
EDIT_POINT get45DegConstrainer() const
Condition to display "Create corner" context menu entry.
static TOOL_ACTION activatePointEditor
Definition: actions.h:168
void updatePoints()
Update which point is being edited.
SHAPE_POLY_SET & GetPolyShape()
Definition: pcb_shape.h:240
virtual void SetPosition(const wxPoint &aPos) override
Definition: pcb_text.h:81
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:200
const wxPoint & GetEnd() const
Return the ending point of the graphic.
Definition: pcb_shape.h:134
void updateEditedPoint(const TOOL_EVENT &aEvent)
Set the current point being edited. NULL means none.
int addCorner(const TOOL_EVENT &aEvent)
TOOL_ACTION handlers.
This file is part of the common library.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:80
SHAPE_T GetShape() const
Definition: pcb_shape.h:110
void ClearSelected()
Definition: eda_item.h:132
bool removeCornerCondition(const SELECTION &aSelection)
COMMIT & Add(EDA_ITEM *aItem)
Notify observers that aItem has been added.
Definition: commit.h:78
PCB_TEXT & Text()
void buildForPolyOutline(std::shared_ptr< EDIT_POINTS > points, const SHAPE_POLY_SET *aOutline)
void setEditedPoint(EDIT_POINT *aPoint)
constexpr KICAD_T BaseType(const KICAD_T aType)
Return the underlying type of the given type.
Definition: typeinfo.h:235
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the aGlobalIndex-th vertex in the poly set.
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:102
SHAPE_POLY_SET * Outline()
Definition: zone.h:320
static TOOL_ACTION changeEditMethod
Definition: actions.h:169
virtual void Revert() override
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
std::unique_ptr< STATUS_TEXT_POPUP > m_statusPopup
Represent a line connecting two EDIT_POINTs.
Definition: edit_points.h:215
int GetWidth() const
Definition: pcb_shape.h:97
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:353
const wxPoint & GetBezierC2() const
Definition: pcb_shape.h:116
EDIT_CONSTRAINT for a EDIT_LINE, that constrains the line to move perpendicular to the line itself.
usual segment : line with rounded ends
const wxPoint & GetCrossbarEnd() const
CONST_ITERATOR CIterateWithHoles(int aOutline) const
TOOL_MENU & GetToolMenu()
bool IsMotion() const
Definition: tool_event.h:300
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:143
double RAD2DECIDEG(double rad)
Definition: trigo.h:234
Structure to hold the necessary information in order to index a vertex on a SHAPE_POLY_SET object: th...
double m_DrawArcCenterMaxAngle
When drawing an arc, the angle ( center - start ) - ( start - end ) can be limited to avoid extremely...
void InsertVertex(int aGlobalIndex, const VECTOR2I &aNewVertex)
Adds a vertex in the globally indexed position aGlobalIndex.
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
class PAD, a pad in a footprint
Definition: typeinfo.h:89
static std::pair< bool, SHAPE_POLY_SET::VERTEX_INDEX > findVertex(SHAPE_POLY_SET &aPolySet, const EDIT_POINT &aPoint)
void UndoRedoBlock(bool aBlock=true)
Enable/disable undo and redo operations.
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
ITERATOR Iterate(int aFirst, int aLast, bool aIterateHoles=false)
Return an object to iterate through the points of the polygons between aFirst and aLast.
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
void DupPolyPointsList(std::vector< wxPoint > &aBuffer) const
Duplicate the list of corners in a std::vector<wxPoint>
Definition: pcb_shape.cpp:1277
virtual bool IsLocked() const
Definition: board_item.cpp:78
std::shared_ptr< EDIT_POINTS > m_editPoints
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
GRID_CONSTRAINT_TYPE GetGridConstraint() const
Definition: edit_points.h:177
EDIT_CONSTRAINT that imposes a constraint that a point has to lie on a line (determined by 2 points).
EDIT_CONSTRAINT that imposes a constraint that two points have to be located at angle of 45 degree mu...
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:204
int modifiedSelection(const TOOL_EVENT &aEvent)
wxPoint GetArcStart() const
Definition: pcb_shape.h:156
EDIT_POINT * m_editedPoint
For better understanding of the points that make a dimension:
EDIT_POINT m_altConstrainer
std::shared_ptr< EDIT_POINTS > makePoints(EDA_ITEM *aItem)
Update item's points with edit points.
PCB_BASE_EDIT_FRAME * frame() const
int getEditedPointIndex() const
Return true if aPoint is the currently modified point.
void SetVertex(const VERTEX_INDEX &aIndex, const VECTOR2I &aPos)
Accessor function to set the position of a specific point.
static bool canAddCorner(const EDA_ITEM &aItem)
Condition to display "Remove corner" context menu entry.
void SetArcGeometry(const wxPoint &aStart, const wxPoint &aMid, const wxPoint &aEnd)
Set the three controlling points for an arc.
Definition: pcb_shape.cpp:496
PCB_SELECTION & GetSelection()
Return the set of currently selected items.
virtual void Move(const wxPoint &aMoveVector) override
Move this object.
Definition: pcb_shape.cpp:156
virtual const wxPoint & GetStart() const
The dimension's origin is the first feature point for the dimension.
void SetActive(bool aActive=true)
Definition: edit_points.h:172
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:285
void editArcEndpointKeepCenter(PCB_SHAPE *aArc, const VECTOR2I &aCenter, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, const VECTOR2I &aCursor) const
Move an end point of the arc around the circumference.
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:502
DIR GetOrientation() const
const PCB_SELECTION & selection() const
void SetEndY(int y)
Definition: pcb_shape.h:138
virtual VECTOR2I GetPosition() const
Return coordinates of an EDIT_POINT.
Definition: edit_points.h:70
VECTOR2< double > VECTOR2D
Definition: vector2d.h:622
int OnSelectionChange(const TOOL_EVENT &aEvent)
Change selection event handler.
virtual wxPoint GetPosition() const override
Definition: pcb_text.h:76
DIMENSION_POINTS
PCB_SELECTION_TOOL * m_selectionTool
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:927
ITERATOR IterateWithHoles(int aOutline)
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
SEG_POINTS
virtual void SetEnd(const wxPoint &aPoint)
static TOOL_ACTION pointEditorAddCorner
Break outline (insert additional points to an edge)
Definition: pcb_actions.h:209
static TOOL_ACTION pointEditorRemoveCorner
Removes a corner.
Definition: pcb_actions.h:212
void setAltConstraint(bool aEnabled)
Return a point that should be used as a constrainer for 45 degrees mode.
Generic, UI-independent tool event.
Definition: tool_event.h:152
wxPoint GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.cpp:385
int changeEditMethod(const TOOL_EVENT &aEvent)
const wxPoint & GetStart() const
Return the starting point of the graphic.
Definition: pcb_shape.h:124
static void pinEditedCorner(int aEditedPointIndex, int aMinWidth, int aMinHeight, VECTOR2I &aTopLeft, VECTOR2I &aTopRight, VECTOR2I &aBotLeft, VECTOR2I &aBotRight, VECTOR2I &aHole, VECTOR2I &aHoleSize)
Update the coordinates of 4 corners of a rectangle, according to pad constraints and the moved corner...
Mark the center of a circle or arc with a cross shape.
COMMIT & StageItems(const Range &aRange, CHANGE_TYPE aChangeType)
Add a change of the item aItem of type aChangeType to the change list.
Definition: commit.h:117
const wxPoint & GetBezierC1() const
Definition: pcb_shape.h:113
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition: actions.h:211
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition: seg.cpp:227
An interface for classes handling user events controlling the view behavior such as zooming,...
#define _(s)
const VECTOR2D DragOrigin() const
Returns information about mouse buttons state.
Definition: tool_event.h:269
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
static LSET AllLayersMask()
Definition: lset.cpp:787
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
class ZONE, a copper pour area
Definition: typeinfo.h:105
COMMIT & Remove(EDA_ITEM *aItem)
Notify observers that aItem has been removed.
Definition: commit.h:90
std::shared_ptr< EDIT_CONSTRAINT< EDIT_POINT > > m_altConstraint
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition: tool_event.h:362
void SetCenter(const wxPoint &aCenterPoint)
Definition: pcb_shape.h:198
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:158
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
double Angle() const
Compute the angle of the vector.
Definition: vector2d.h:307
bool Is45Limited() const
Should the tool use its 45° mode option?
void SetOrientation(DIR aOrientation)
Set the orientation of the dimension line (so, perpendicular to the feature lines).
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition: typeinfo.h:99
bool m_isFootprintEditor
void SetEndX(int x)
Definition: pcb_shape.h:139
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
Definition: seg.h:40
EDIT_POINT m_original
Original position for the current drag point.
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:404
ARC_POINTS
bool Init() override
Init() is called once upon a registration of the tool.
void editArcMidKeepCenter(PCB_SHAPE *aArc, const VECTOR2I &aCenter, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, const VECTOR2I &aCursor) const
Move the mid point of the arc, while keeping the angle.
KIGFX::VIEW_CONTROLS * controls() const
Common, abstract interface for edit frames.
RECT_POINTS
void SetStart(const wxPoint &aStart)
Definition: pcb_shape.h:127
void RemoveVertex(int aGlobalIndex)
Delete the aGlobalIndex-th vertex.
CIRCLE_POINTS
Base class for iterating over all vertices in a given SHAPE_POLY_SET.
class ZONE, managed by a footprint
Definition: typeinfo.h:94
void SetHover(bool aHover=true)
Definition: edit_points.h:175
double GetAngle() const
Definition: pcb_shape.h:107
virtual void SetStart(const wxPoint &aPoint)
int Size() const
Returns the number of selected parts.
Definition: selection.h:103
The selection tool: currently supports:
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:100
static const TOOL_EVENT InhibitSelectionEditing
Definition: actions.h:210
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true) override
Revert the commit by restoring the modified items state.
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:77
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_BezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: pcb_shape.cpp:359
void SetArcEnd(const wxPoint &aArcEndPoint)
Initialize the end arc point.
Definition: pcb_shape.h:193
bool isModified(const EDIT_POINT &aPoint) const
Set up an alternative constraint (typically enabled upon a modifier key being pressed).
void Activate()
Run the tool.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
static bool addCornerCondition(const SELECTION &aSelection)
Determine if the tool can currently add a corner to the given item.
A leader is a dimension-like object pointing to a specific point.
constexpr int delta
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
void SetStartY(int y)
Definition: pcb_shape.h:128
EDIT_POINT * m_hoveredPoint
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:103
wxPoint GetArcMid() const
Definition: pcb_shape.cpp:435
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:293
virtual const wxPoint & GetEnd() const
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:323
void SetBezierC2(const wxPoint &aPoint)
Definition: pcb_shape.h:115
void SetNeedRefill(bool aNeedRefill)
Definition: zone.h:238
Represent a single point that can be used for modifying items.
Definition: edit_points.h:47
BEZIER_CURVE_POINTS
POLYGON & Polygon(int aIndex)
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
segment with non rounded ends
RECT_LINES
Definition: pad.h:57
Text placement is manually set by the user.
bool UnFill()
Removes the zone filling.
Definition: zone.cpp:188
bool validatePolygon(SHAPE_POLY_SET &aModified) const
Validate a polygon and displays a popup warning if invalid.
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:166
virtual void SetAngle(double aAngle, bool aUpdateEnd=true)
Set the angle for arcs, and normalizes it within the range 0 - 360 degrees.
Definition: pcb_shape.cpp:519
void updateItem() const
Apply the last changes to the edited item.
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
const VECTOR2D Position() const
Returns the point where dragging has started.
Definition: tool_event.h:263
virtual void ApplyConstraint()
Correct coordinates of an EDIT_POINT by applying previously set constraint.
Definition: edit_points.h:165
void SetEnd(const wxPoint &aEnd)
Definition: pcb_shape.h:137
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:176
void SetTextPositionMode(DIM_TEXT_POSITION aMode)
EDA_ITEM * Front() const
Definition: selection.h:144
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: view.cpp:1518
void SetBezierC1(const wxPoint &aPoint)
Definition: pcb_shape.h:112
void SetArcStart(const wxPoint &aArcStartPoint)
Initialize the start arc point.
Definition: pcb_shape.h:183
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
virtual void UpdateMsgPanel()
Redraw the message panel.
bool IsSelfIntersecting() const
Check whether any of the polygons in the set is self intersecting.
void RemoveContour(int aContourIdx, int aPolygonIdx=-1)
Delete the aContourIdx-th contour of the aPolygonIdx-th polygon in the set.
virtual void SetPosition(const VECTOR2I &aPosition)
Set new coordinates for an EDIT_POINT.
Definition: edit_points.h:106
void Update()
Update the dimension's cached text and geometry.