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 <advanced_config.h>
29 #include <tool/tool_manager.h>
30 #include <view/view_controls.h>
32 #include <geometry/seg.h>
33 #include <confirm.h>
34 #include "pcb_actions.h"
35 #include "selection_tool.h"
36 #include "point_editor.h"
37 #include "grid_helper.h"
38 #include <board_commit.h>
39 #include <bitmaps.h>
40 #include <status_popup.h>
41 #include <pcb_edit_frame.h>
42 #include <class_edge_mod.h>
43 #include <class_dimension.h>
44 #include <class_zone.h>
47 
48 // Few constants to avoid using bare numbers for point indices
50 {
52 };
53 
55 {
57 };
58 
60 {
62 };
63 
65 {
67 };
68 
70 {
75 };
76 
78 {
84 };
85 
87 {
88 private:
89 
90  static void buildForPolyOutline( std::shared_ptr<EDIT_POINTS> points,
91  const SHAPE_POLY_SET* aOutline, KIGFX::GAL* aGal )
92  {
93 
94  int cornersCount = aOutline->TotalVertices();
95 
96  for( auto iterator = aOutline->CIterateWithHoles(); iterator; iterator++ )
97  {
98  points->AddPoint( *iterator );
99 
100  if( iterator.IsEndContour() )
101  points->AddBreak();
102  }
103 
104  // Lines have to be added after creating edit points,
105  // as they use EDIT_POINT references
106  for( int i = 0; i < cornersCount - 1; ++i )
107  {
108  if( points->IsContourEnd( i ) )
109  {
110  points->AddLine( points->Point( i ),
111  points->Point( points->GetContourStartIdx( i ) ) );
112  }
113  else
114  {
115  points->AddLine( points->Point( i ), points->Point( i + 1 ) );
116  }
117 
118  points->Line( i ).SetConstraint( new EC_PERPLINE( points->Line( i ) ) );
119  }
120 
121  // The last missing line, connecting the last and the first polygon point
122  points->AddLine( points->Point( cornersCount - 1 ),
123  points->Point( points->GetContourStartIdx( cornersCount - 1 ) ) );
124 
125  points->Line( points->LinesSize() - 1 ).SetConstraint(
126  new EC_PERPLINE( points->Line( points->LinesSize() - 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_DIM_ALIGNED_T:
249  {
250  const ALIGNED_DIMENSION* dimension = static_cast<const ALIGNED_DIMENSION*>( aItem );
251 
252  points->AddPoint( dimension->GetStart() );
253  points->AddPoint( dimension->GetEnd() );
254  points->AddPoint( dimension->Text().GetPosition() );
255  points->AddPoint( dimension->GetCrossbarStart() );
256  points->AddPoint( dimension->GetCrossbarEnd() );
257 
258  if( aItem->Type() == PCB_DIM_ALIGNED_T )
259  {
260  // Dimension height setting - edit points should move only along the feature lines
261  points->Point( DIM_CROSSBARSTART )
262  .SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARSTART ),
263  points->Point( DIM_START ) ) );
264  points->Point( DIM_CROSSBAREND )
265  .SetConstraint( new EC_LINE( points->Point( DIM_CROSSBAREND ),
266  points->Point( DIM_END ) ) );
267  }
268 
269  break;
270  }
271 
272  case PCB_DIM_CENTER_T:
273  {
274  const CENTER_DIMENSION* dimension = static_cast<const CENTER_DIMENSION*>( aItem );
275 
276  points->AddPoint( dimension->GetStart() );
277  points->AddPoint( dimension->GetEnd() );
278 
279  points->Point( DIM_END ).SetConstraint( new EC_45DEGREE( points->Point( DIM_END ),
280  points->Point( DIM_START ) ) );
281 
282  break;
283  }
284 
285  case PCB_DIM_LEADER_T:
286  {
287  const LEADER* dimension = static_cast<const LEADER*>( aItem );
288 
289  points->AddPoint( dimension->GetStart() );
290  points->AddPoint( dimension->GetEnd() );
291  points->AddPoint( dimension->Text().GetPosition() );
292 
293  break;
294  }
295 
296  default:
297  points.reset();
298  break;
299  }
300 
301  return points;
302  }
303 
304 private:
306 };
307 
308 
310  PCB_TOOL_BASE( "pcbnew.PointEditor" ),
311  m_selectionTool( nullptr ),
312  m_editedPoint( nullptr ),
313  m_hoveredPoint( nullptr ),
314  m_original( VECTOR2I( 0, 0 ) ),
315  m_altConstrainer( VECTOR2I( 0, 0 ) ),
316  m_refill( false ),
317  m_altEditMethod( false )
318 {
319 }
320 
321 
323 {
324  m_refill = false;
325  m_editPoints.reset();
326  m_altConstraint.reset();
327  getViewControls()->SetAutoPan( false );
328 
329  m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( getEditFrame<PCB_BASE_EDIT_FRAME>() );
330  m_statusPopup->SetTextColor( wxColour( 255, 0, 0 ) );
331  m_statusPopup->SetText( _( "Self-intersecting polygons are not allowed." ) );
332 }
333 
334 
336 {
337  // Find the selection tool, so they can cooperate
339 
340  wxASSERT_MSG( m_selectionTool, _( "pcbnew.InteractiveSelection tool is not available" ) );
341 
342  auto& menu = m_selectionTool->GetToolMenu().GetMenu();
345  std::bind( &POINT_EDITOR::removeCornerCondition, this, _1 ) );
346 
347  return true;
348 }
349 
350 
352 {
353  EDIT_POINT* point;
354  EDIT_POINT* hovered = nullptr;
355 
356  if( aEvent.IsMotion() )
357  {
358  point = m_editPoints->FindPoint( aEvent.Position(), getView() );
359  hovered = point;
360  }
361  else if( aEvent.IsDrag( BUT_LEFT ) )
362  {
363  point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
364  }
365  else
366  {
367  point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
368  }
369 
370  if( hovered )
371  {
372  if( m_hoveredPoint != hovered )
373  {
374  if( m_hoveredPoint )
375  m_hoveredPoint->SetHover( false );
376 
377  m_hoveredPoint = hovered;
379  }
380  }
381  else if( m_hoveredPoint )
382  {
383  m_hoveredPoint->SetHover( false );
384  m_hoveredPoint = nullptr;
385  }
386 
387  if( m_editedPoint != point )
388  setEditedPoint( point );
389 }
390 
391 
393 {
394  if( !m_selectionTool )
395  return 0;
396 
398 
399  if( selection.Size() != 1 || selection.Front()->GetEditFlags() )
400  return 0;
401 
402  Activate();
403 
405  KIGFX::VIEW* view = getView();
406  PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
407 
408  controls->ShowCursor( true );
409 
410  GRID_HELPER grid( m_toolMgr, editFrame->GetMagneticItemsSettings() );
411  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
412 
413  if( !item )
414  return 0;
415 
416  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
417 
418  if( !m_editPoints )
419  return 0;
420 
421  view->Add( m_editPoints.get() );
422  setEditedPoint( nullptr );
423  updateEditedPoint( aEvent );
424  m_refill = false;
425  bool inDrag = false;
426 
427  frame()->UndoRedoBlock( true );
428 
429  BOARD_COMMIT commit( editFrame );
430  LSET snapLayers = item->GetLayerSet();
431 
432  if( BaseType( item->Type() ) == PCB_DIMENSION_T )
433  snapLayers = LSET::AllLayersMask();
434 
435  // Main loop: keep receiving events
436  while( TOOL_EVENT* evt = Wait() )
437  {
438  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
439  grid.SetUseGrid( editFrame->IsGridVisible() );
440 
441  if( !m_editPoints || evt->IsSelectionEvent() )
442  break;
443 
444  EDIT_POINT* prevHover = m_hoveredPoint;
445 
446  if( !inDrag )
447  updateEditedPoint( *evt );
448 
449  if( prevHover != m_hoveredPoint )
450  getView()->Update( m_editPoints.get() );
451 
452  if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
453  {
454  if( !inDrag )
455  {
456  commit.StageItems( selection, CHT_MODIFY );
457 
458  controls->ForceCursorPosition( false );
459  m_original = *m_editedPoint; // Save the original position
460  controls->SetAutoPan( true );
461  inDrag = true;
462  grid.SetAuxAxes( true, m_original.GetPosition() );
463  setAltConstraint( true );
465  }
466 
467  //TODO: unify the constraints to solve simultaneously instead of sequentially
468  m_editedPoint->SetPosition( grid.BestSnapAnchor( evt->Position(),
469  snapLayers, { item } ) );
470 
471  // The alternative constraint limits to 45°
472  bool enableAltConstraint = !!evt->Modifier( MD_CTRL );
473 
474  if( enableAltConstraint )
475  m_altConstraint->Apply();
476  else
478 
479  // Don't snap the line center to the grid
480  if( !dynamic_cast<EDIT_LINE*>( m_editedPoint) )
481  m_editedPoint->SetPosition( grid.BestSnapAnchor( m_editedPoint->GetPosition(),
482  snapLayers, { item } ) );
483 
484  updateItem();
485  updatePoints();
486  }
487  else if( m_editedPoint && evt->Action() == TA_MOUSE_DOWN && evt->Buttons() == BUT_LEFT )
488  {
490  getView()->Update( m_editPoints.get() );
491  }
492  else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
493  {
494  if( m_editedPoint )
495  {
496  m_editedPoint->SetActive( false );
497  getView()->Update( m_editPoints.get() );
498  }
499 
500  controls->SetAutoPan( false );
501  setAltConstraint( false );
502 
503  commit.Push( _( "Drag a corner" ) );
504  inDrag = false;
505  m_refill = true;
506  }
507 
508  else if( evt->IsCancelInteractive() || evt->IsActivate() )
509  {
510  if( inDrag ) // Restore the last change
511  commit.Revert();
512  else if( evt->IsCancelInteractive() )
513  break;
514 
515  if( evt->IsActivate() && !evt->IsMoveTool() )
516  break;
517  }
518 
519  else
520  evt->SetPassEvent();
521  }
522 
523  if( m_editPoints )
524  {
525  view->Remove( m_editPoints.get() );
526 
527  finishItem();
528  m_editPoints.reset();
529  }
530 
531  frame()->UndoRedoBlock( false );
532  frame()->UpdateMsgPanel();
533 
534  return 0;
535 }
536 
538  VECTOR2I aMid, VECTOR2I aEnd,
539  const VECTOR2I aCursor ) const
540 {
541  VECTOR2D startLine = aStart - aCenter;
542  VECTOR2D endLine = aEnd - aCenter;
543  double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
544 
545  bool clockwise;
546  bool movingStart;
547  bool arcValid = true;
548 
549  VECTOR2I *p1, *p2, *p3;
550  // p1 does not move, p2 does.
551 
552  if( aStart != aArc->GetArcStart() )
553  {
554  aStart = aCursor;
555  p1 = &aEnd;
556  p2 = &aStart;
557  p3 = &aMid;
558  movingStart = true;
559  }
560  else
561  {
562  aEnd = aCursor;
563  p1 = &aStart;
564  p2 = &aEnd;
565  p3 = &aMid;
566  movingStart = false;
567  }
568 
569  VECTOR2D v1, v2, v3, v4;
570 
571  // Move the coordinate system
572  v1 = *p1 - aCenter;
573  v2 = *p2 - aCenter;
574  v3 = *p3 - aCenter;
575 
576  VECTOR2D u1, u2, u3;
577 
578  // A point cannot be both the center and on the arc.
579  if( ( v1.EuclideanNorm() == 0 ) || ( v2.EuclideanNorm() == 0 ) )
580  return;
581 
582  u1 = v1 / v1.EuclideanNorm();
583  u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
584  u2 = u2 / u2.EuclideanNorm();
585 
586  // [ u1, u3 ] is a base centered on the circle with:
587  // u1 : unit vector toward the point that does not move
588  // u2 : unit vector toward the mid point.
589 
590  // Get vectors v1, and v2 in that coordinate system.
591 
592  double det = u1.x * u2.y - u2.x * u1.y;
593 
594  // u1 and u2 are unit vectors, and perpendicular.
595  // det should not be 0. In case it is, do not change the arc.
596  if( det == 0 )
597  return;
598 
599  double tmpx = v1.x * u2.y - v1.y * u2.x;
600  double tmpy = -v1.x * u1.y + v1.y * u1.x;
601  v1.x = tmpx;
602  v1.y = tmpy;
603  v1 = v1 / det;
604 
605  tmpx = v2.x * u2.y - v2.y * u2.x;
606  tmpy = -v2.x * u1.y + v2.y * u1.x;
607  v2.x = tmpx;
608  v2.y = tmpy;
609  v2 = v2 / det;
610 
611  double R = v1.EuclideanNorm();
612  bool transformCircle = false;
613 
614  /* p2
615  * X***
616  * ** <---- This is the arc
617  * y ^ **
618  * | R *
619  * | <-----------> *
620  * x------x------>--------x p1
621  * C' <----> C x
622  * delta
623  *
624  * p1 does not move, and the tangent at p1 remains the same.
625  * => The new center, C', will be on the C-p1 axis.
626  * p2 moves
627  *
628  * The radius of the new circle is delta + R
629  *
630  * || C' p2 || = || C' P1 ||
631  * is the same as :
632  * ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
633  *
634  * delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
635  *
636  * We can use this equation for any point p2 with p2.x < R
637  */
638 
639  if( v2.x == R )
640  {
641  // Straight line, do nothing
642  }
643  else
644  {
645  if( v2.x > R )
646  {
647  // If we need to invert the curvature.
648  // We modify the input so we can use the same equation
649  transformCircle = true;
650  v2.x = 2 * R - v2.x;
651  }
652  // We can keep the tangent constraint.
653  double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
654 
655  // This is just to limit the radius, so nothing overflows later when drawing.
656  if( abs( v2.y / ( R - v2.x ) ) > ADVANCED_CFG::GetCfg().m_drawArcCenterMaxAngle )
657  {
658  arcValid = false;
659  }
660  // Never recorded a problem, but still checking.
661  if( !std::isfinite( delta ) )
662  {
663  arcValid = false;
664  }
665  // v4 is the new center
666  v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
667 
668  clockwise = aArc->GetAngle() > 0;
669 
670  if( transformCircle )
671  clockwise = !clockwise;
672 
673  tmpx = v4.x * u1.x + v4.y * u2.x;
674  tmpy = v4.x * u1.y + v4.y * u2.y;
675  v4.x = tmpx;
676  v4.y = tmpy;
677 
678  aCenter = v4 + aCenter;
679 
680  startLine = aStart - aCenter;
681  endLine = aEnd - aCenter;
682  newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
683 
684  if( clockwise && newAngle < 0.0 )
685  newAngle += 3600.0;
686  else if( !clockwise && newAngle > 0.0 )
687  newAngle -= 3600.0;
688 
689  if( arcValid )
690  {
691  aArc->SetAngle( newAngle );
692  aArc->SetCenter( wxPoint( aCenter.x, aCenter.y ) );
693 
694  if( movingStart )
695  {
696  aArc->SetArcStart( wxPoint( aStart.x, aStart.y ) );
697  // Set angle computes the end point, so re-force it now.
698  aArc->SetArcEnd( wxPoint( aEnd.x, aEnd.y ) );
699  }
700  else
701  {
702  aArc->SetArcEnd( wxPoint( aEnd.x, aEnd.y ) );
703  }
704  }
705  }
706 }
707 
708 
722 static void pinEditedCorner( int aEditedPointIndex, int aMinWidth, int aMinHeight,
723  VECTOR2I& aTopLeft, VECTOR2I& aTopRight, VECTOR2I& aBotLeft,
724  VECTOR2I& aBotRight, VECTOR2I aHole, VECTOR2I aHoleSize )
725 {
726  switch( aEditedPointIndex )
727  {
728  case RECT_TOP_LEFT:
729  if( aHoleSize.x )
730  {
731  // pin edited point to the top/left of the hole
732  aTopLeft.x = std::min( aTopLeft.x, aHole.x - aHoleSize.x / 2 - aMinWidth );
733  aTopLeft.y = std::min( aTopLeft.y, aHole.y - aHoleSize.y / 2 - aMinHeight );
734  }
735  else
736  {
737  // pin edited point within opposite corner
738  aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - aMinWidth );
739  aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - aMinHeight );
740  }
741 
742  // push edited point edges to adjacent corners
743  aTopRight.y = aTopLeft.y;
744  aBotLeft.x = aTopLeft.x;
745 
746  break;
747 
748  case RECT_TOP_RIGHT:
749  if( aHoleSize.x )
750  {
751  // pin edited point to the top/right of the hole
752  aTopRight.x = std::max( aTopRight.x, aHole.x + aHoleSize.x / 2 + aMinWidth );
753  aTopRight.y = std::min( aTopRight.y, aHole.y - aHoleSize.y / 2 - aMinHeight );
754  }
755  else
756  {
757  // pin edited point within opposite corner
758  aTopRight.x = std::max( aTopRight.x, aBotLeft.x + aMinWidth );
759  aTopRight.y = std::min( aTopRight.y, aBotLeft.y - aMinHeight );
760  }
761 
762  // push edited point edges to adjacent corners
763  aTopLeft.y = aTopRight.y;
764  aBotRight.x = aTopRight.x;
765 
766  break;
767 
768  case RECT_BOT_LEFT:
769  if( aHoleSize.x )
770  {
771  // pin edited point to the bottom/left of the hole
772  aBotLeft.x = std::min( aBotLeft.x, aHole.x - aHoleSize.x / 2 - aMinWidth );
773  aBotLeft.y = std::max( aBotLeft.y, aHole.y + aHoleSize.y / 2 + aMinHeight );
774  }
775  else
776  {
777  // pin edited point within opposite corner
778  aBotLeft.x = std::min( aBotLeft.x, aTopRight.x - aMinWidth );
779  aBotLeft.y = std::max( aBotLeft.y, aTopRight.y + aMinHeight );
780  }
781 
782  // push edited point edges to adjacent corners
783  aBotRight.y = aBotLeft.y;
784  aTopLeft.x = aBotLeft.x;
785 
786  break;
787 
788  case RECT_BOT_RIGHT:
789  if( aHoleSize.x )
790  {
791  // pin edited point to the bottom/right of the hole
792  aBotRight.x = std::max( aBotRight.x, aHole.x + aHoleSize.x / 2 + aMinWidth );
793  aBotRight.y = std::max( aBotRight.y, aHole.y + aHoleSize.y / 2 + aMinHeight );
794  }
795  else
796  {
797  // pin edited point within opposite corner
798  aBotRight.x = std::max( aBotRight.x, aTopLeft.x + aMinWidth );
799  aBotRight.y = std::max( aBotRight.y, aTopLeft.y + aMinHeight );
800  }
801 
802  // push edited point edges to adjacent corners
803  aBotLeft.y = aBotRight.y;
804  aTopRight.x = aBotRight.x;
805 
806  break;
807  }
808 }
809 
810 
812  VECTOR2I aMid, VECTOR2I aEnd,
813  const VECTOR2I aCursor ) const
814 {
815  bool clockwise;
816  bool movingStart;
817 
818  VECTOR2I *p1, *p2;
819  VECTOR2I target;
820 
821  // p1 does not move, p2 does.
822 
823  if( aStart != aArc->GetArcStart() )
824  {
825  p1 = &aEnd;
826  p2 = &aStart;
827  movingStart = true;
828  }
829  else
830  {
831  p1 = &aStart;
832  p2 = &aEnd;
833  movingStart = false;
834  }
835 
836  target = *p2 - aCenter;
837 
838  double sqRadius = ( *p1 - aCenter ).SquaredEuclideanNorm();
839 
840  *p1 = *p1 - aCenter;
841  *p2 = *p2 - aCenter;
842 
843  // Circle : x^2 + y^2 = R ^ 2
844  // In this coordinate system, the angular position of the cursor is (r, theta)
845  // The line coming from the center of the circle is y = start.y / start.x * x
846  // The intersection fulfills : x^2 = R^2 / ( 1 + ( start.y / start.x ) ^ 2 )
847 
848  if( target.x == 0 )
849  {
850  p2->x = 0;
851  p2->y = ( target.y > 0 ) ? sqrt( sqRadius ) : -sqrt( sqRadius );
852  }
853  else
854  {
855  double tan = target.y / static_cast<double>( target.x );
856  // The divider is always greater than 1 ( cannot be 0 )
857  double tmp = sqrt( sqRadius / ( 1.0 + tan * tan ) );
858  // Move to the correct quadrant
859  tmp = target.x > 0 ? tmp : -tmp;
860  p2->y = target.y / static_cast<double>( target.x ) * tmp;
861  p2->x = tmp;
862  }
863 
864  *p1 = *p1 + aCenter;
865  *p2 = *p2 + aCenter;
866 
867  clockwise = aArc->GetAngle() > 0;
868 
869  VECTOR2D startLine = aStart - aCenter;
870  VECTOR2D endLine = aEnd - aCenter;
871  double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
872 
873  if( clockwise && newAngle < 0.0 )
874  newAngle += 3600.0;
875  else if( !clockwise && newAngle > 0.0 )
876  newAngle -= 3600.0;
877 
878  aArc->SetAngle( newAngle );
879  aArc->SetCenter( wxPoint( aCenter.x, aCenter.y ) );
880 
881  if( movingStart )
882  {
883  aArc->SetArcStart( wxPoint( aStart.x, aStart.y ) );
884  // Set angle computes the end point, so re-force it now.
885  aArc->SetArcEnd( wxPoint( aEnd.x, aEnd.y ) );
886  }
887  else
888  {
889  aArc->SetArcEnd( wxPoint( aEnd.x, aEnd.y ) );
890  }
891 }
892 
893 
895  VECTOR2I aMid, VECTOR2I aEnd, const VECTOR2I aCursor ) const
896 {
897  // Now, update the edit point position
898  // Express the point in a cercle-centered coordinate system.
899  aStart = aStart - aCenter;
900  aEnd = aEnd - aCenter;
901 
902  double sqRadius = ( aCursor - aCenter ).SquaredEuclideanNorm();
903 
904  // Special case, because the tangent would lead to +/- infinity
905  if( aStart.x == 0 )
906  {
907  aStart.y = aCursor.y > 0 ? sqrt( sqRadius ) : -sqrt( sqRadius );
908  }
909  else
910  {
911  // Circle : x^2 + y^2 = R ^ 2
912  // In this coordinate system, the angular position of the cursor is (r, theta)
913  // The line coming from the center of the circle is y = start.y / start.x * x
914  // The intersection fulfills : x^2 = R^2 / ( 1 + ( start.y / start.x ) ^ 2 )
915 
916  double tan = aStart.y / static_cast<double>( aStart.x );
917  double tmp = sqrt( sqRadius / ( 1.0 + tan * tan ) );
918  // Move to the correct quadrant
919  tmp = aStart.x > 0 ? tmp : -tmp;
920  aStart.y = aStart.y / static_cast<double>( aStart.x ) * tmp;
921  aStart.x = tmp;
922  }
923 
924  // Special case, because the tangent would lead to +/- infinity
925  if( aEnd.x == 0 )
926  {
927  aEnd.y = aMid.y > 0 ? sqrt( sqRadius ) : -sqrt( sqRadius );
928  }
929  else
930  {
931  // Circle : x^2 + y^2 = R ^ 2
932  // In this coordinate system, the angular position of the cursor is (r, theta)
933  // The line coming from the center of the circle is y = start.y / start.x * x
934  // The intersection fulfills : x^2 = R^2 / ( 1 + ( start.y / start.x ) ^ 2 )
935 
936  double tan = aEnd.y / static_cast<double>( aEnd.x );
937  double tmp = sqrt( sqRadius / ( 1.0 + tan * tan ) );
938  // Move to the correct quadrant
939  tmp = aEnd.x > 0 ? tmp : -tmp;
940  aEnd.y = aEnd.y / static_cast<double>( aEnd.x ) * tmp;
941  aEnd.x = tmp;
942  }
943 
944  aStart = aStart + aCenter;
945  aEnd = aEnd + aCenter;
946 
947  aArc->SetArcStart( wxPoint( aStart.x, aStart.y ) );
948  aArc->SetArcEnd( wxPoint( aEnd.x, aEnd.y ) );
949 }
950 
951 
953  VECTOR2I aMid, VECTOR2I aEnd, const VECTOR2I aCursor ) const
954 {
955  bool clockwise;
956  VECTOR2I oldCenter = aArc->GetCenter();
957 
958 
959  // This allows the user to go on the sides of the arc
960  aMid = aCursor;
961  // Find the new center
962  aCenter = GetArcCenter( aStart, aMid, aEnd );
963 
964  aArc->SetCenter( wxPoint( aCenter.x, aCenter.y ) );
965 
966  // Check if the new arc is CW or CCW
967  VECTOR2D startLine = aStart - aCenter;
968  VECTOR2D endLine = aEnd - aCenter;
969  double newAngle = RAD2DECIDEG( endLine.Angle() - startLine.Angle() );
970  VECTOR2D v1, v2;
971 
972  v1 = aStart - aMid;
973  v2 = aEnd - aMid;
974  double theta = RAD2DECIDEG( v1.Angle() );
975  RotatePoint( &( v1.x ), &( v1.y ), theta );
976  RotatePoint( &( v2.x ), &( v2.y ), theta );
977  clockwise = ( ( v1.Angle() - v2.Angle() ) > 0 );
978 
979  // Normalize the angle
980  if( clockwise && newAngle < 0.0 )
981  newAngle += 3600.0;
982  else if( !clockwise && newAngle > 0.0 )
983  newAngle -= 3600.0;
984 
985  // Accuracy test
986  // First, get the angle
987  VECTOR2I endTest = aStart;
988  RotatePoint( &( endTest.x ), &( endTest.y ), aCenter.x, aCenter.y, -newAngle );
989  double distance = ( endTest - aEnd ).SquaredEuclideanNorm();
990 
992  {
993  // Cancel Everything
994  // If the accuracy is low, we can't draw precisely the arc.
995  // It may happen when the radius is *high*
996  aArc->SetCenter( wxPoint( oldCenter.x, oldCenter.y ) );
997  }
998  else
999  {
1000  aArc->SetAngle( newAngle );
1001  aArc->SetArcEnd( wxPoint( aEnd.x, aEnd.y ) );
1002  }
1003 
1004  // Now, update the edit point position
1005  // Express the point in a cercle-centered coordinate system.
1006  aMid = aCursor - aCenter;
1007 
1008  double sqRadius = ( aEnd - aCenter ).SquaredEuclideanNorm();
1009 
1010  // Special case, because the tangent would lead to +/- infinity
1011  if( aMid.x == 0 )
1012  {
1013  aMid.y = aMid.y > 0 ? sqrt( sqRadius ) : -sqrt( sqRadius );
1014  }
1015  else
1016  {
1017  // Circle : x^2 + y^2 = R ^ 2
1018  // In this coordinate system, the angular position of the cursor is (r, theta)
1019  // The line coming from the center of the circle is y = start.y / start.x * x
1020  // The intersection fulfills : x^2 = R^2 / ( 1 + ( start.y / start.x ) ^ 2 )
1021 
1022  double tan = aMid.y / static_cast<double>( aMid.x );
1023  double tmp = sqrt( sqRadius / ( 1.0 + tan * tan ) );
1024  // Move to the correct quadrant
1025  tmp = aMid.x > 0 ? tmp : -tmp;
1026  aMid.y = aMid.y / static_cast<double>( aMid.x ) * tmp;
1027  aMid.x = tmp;
1028  }
1029 }
1030 
1031 
1033 {
1034  EDA_ITEM* item = m_editPoints->GetParent();
1035 
1036  if( !item )
1037  return;
1038 
1039  switch( item->Type() )
1040  {
1041  case PCB_LINE_T:
1042  case PCB_MODULE_EDGE_T:
1043  {
1044  DRAWSEGMENT* segment = static_cast<DRAWSEGMENT*>( item );
1045 
1046  switch( segment->GetShape() )
1047  {
1048  case S_SEGMENT:
1049  if( isModified( m_editPoints->Point( SEG_START ) ) )
1050  segment->SetStart( wxPoint( m_editPoints->Point( SEG_START ).GetPosition().x,
1051  m_editPoints->Point( SEG_START ).GetPosition().y ) );
1052 
1053  else if( isModified( m_editPoints->Point( SEG_END ) ) )
1054  segment->SetEnd( wxPoint( m_editPoints->Point( SEG_END ).GetPosition().x,
1055  m_editPoints->Point( SEG_END ).GetPosition().y ) );
1056 
1057  break;
1058 
1059  case S_RECT:
1060  {
1061  if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) ) )
1062  {
1063  segment->SetStart( (wxPoint) m_editPoints->Point( RECT_TOP_LEFT ).GetPosition() );
1064  }
1065  else if( isModified( m_editPoints->Point( RECT_TOP_RIGHT ) ) )
1066  {
1067  segment->SetStartY( m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().y );
1068  segment->SetEndX( m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().x );
1069  }
1070  else if( isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1071  {
1072  segment->SetEnd( (wxPoint) m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition() );
1073  }
1074  else if( isModified( m_editPoints->Point( RECT_BOT_LEFT ) ) )
1075  {
1076  segment->SetStartX( m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().x );
1077  segment->SetEndY( m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().y );
1078  }
1079  }
1080  break;
1081 
1082  case S_ARC:
1083  {
1084  VECTOR2I center = m_editPoints->Point( ARC_CENTER ).GetPosition();
1085  VECTOR2I mid = m_editPoints->Point( ARC_MID ).GetPosition();
1086  VECTOR2I start = m_editPoints->Point( ARC_START ).GetPosition();
1087  VECTOR2I end = m_editPoints->Point( ARC_END ).GetPosition();
1088 
1089  const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
1090 
1091  if( isModified( m_editPoints->Point( ARC_CENTER ) ) )
1092  {
1093  wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
1094  segment->Move( moveVector );
1095  }
1096  else if( isModified( m_editPoints->Point( ARC_MID ) ) )
1097  {
1098  if( m_altEditMethod )
1099  {
1100  editArcMidKeepCenter( segment, center, start, mid, end, cursorPos );
1101  }
1102  else
1103  {
1104  editArcMidKeepEnpoints( segment, center, start, mid, end, cursorPos );
1105  }
1106  }
1107  else if( isModified( m_editPoints->Point( ARC_START ) )
1108  || isModified( m_editPoints->Point( ARC_END ) ) )
1109  {
1110  if( m_altEditMethod )
1111  {
1112  editArcEndpointKeepCenter( segment, center, start, mid, end, cursorPos );
1113  }
1114  else
1115  {
1116  editArcEndpointKeepTangent( segment, center, start, mid, end, cursorPos );
1117  }
1118  }
1119  }
1120  break;
1121 
1122  case S_CIRCLE:
1123  {
1124  const VECTOR2I& center = m_editPoints->Point( CIRC_CENTER ).GetPosition();
1125  const VECTOR2I& end = m_editPoints->Point( CIRC_END ).GetPosition();
1126 
1127  if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
1128  {
1129  wxPoint moveVector = wxPoint( center.x, center.y ) - segment->GetCenter();
1130  segment->Move( moveVector );
1131  }
1132  else
1133  {
1134  segment->SetEnd( wxPoint( end.x, end.y ) );
1135  }
1136  }
1137  break;
1138 
1139  case S_POLYGON:
1140  {
1141  SHAPE_POLY_SET& outline = segment->GetPolyShape();
1142 
1143  for( int i = 0; i < outline.TotalVertices(); ++i )
1144  outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
1145 
1146  for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1147  {
1148  if( !isModified( m_editPoints->Line( i ) ) )
1149  m_editPoints->Line( i ).SetConstraint( new EC_PERPLINE( m_editPoints->Line( i ) ) );
1150  }
1151 
1152  validatePolygon( outline );
1153  }
1154  break;
1155 
1156  case S_CURVE:
1157  if( isModified( m_editPoints->Point( BEZIER_CURVE_START ) ) )
1158  segment->SetStart( wxPoint( m_editPoints->Point( BEZIER_CURVE_START ).GetPosition().x,
1159  m_editPoints->Point( BEZIER_CURVE_START ).GetPosition().y ) );
1160  else if( isModified( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ) ) )
1161  segment->SetBezControl1( wxPoint( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).GetPosition().x,
1162  m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).GetPosition().y ) );
1163  else if( isModified( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ) ) )
1164  segment->SetBezControl2( wxPoint( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).GetPosition().x,
1165  m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).GetPosition().y ) );
1166  else if( isModified( m_editPoints->Point( BEZIER_CURVE_END ) ) )
1167  segment->SetEnd( wxPoint( m_editPoints->Point( BEZIER_CURVE_END ).GetPosition().x,
1168  m_editPoints->Point( BEZIER_CURVE_END ).GetPosition().y ) );
1169 
1170  segment->RebuildBezierToSegmentsPointsList( segment->GetWidth() );
1171  break;
1172 
1173  default: // suppress warnings
1174  break;
1175  }
1176 
1177  // Update relative coordinates for module edges
1178  if( EDGE_MODULE* edge = dyn_cast<EDGE_MODULE*>( item ) )
1179  edge->SetLocalCoord();
1180 
1181  break;
1182  }
1183 
1184  case PCB_PAD_T:
1185  {
1186  D_PAD* pad = static_cast<D_PAD*>( item );
1187 
1188  switch( pad->GetShape() )
1189  {
1190  case PAD_SHAPE_CIRCLE:
1191  {
1192  wxPoint center = (wxPoint) m_editPoints->Point( CIRC_CENTER ).GetPosition();
1193  wxPoint end = (wxPoint) m_editPoints->Point( CIRC_END ).GetPosition();
1194 
1195  if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
1196  {
1197  wxPoint moveVector = center - pad->ShapePos();
1198  pad->SetOffset( pad->GetOffset() + moveVector );
1199  }
1200  else
1201  {
1202  int diameter = (int) EuclideanNorm( end - center ) * 2;
1203  pad->SetSize( wxSize( diameter, diameter ) );
1204  }
1205  }
1206  break;
1207 
1208  case PAD_SHAPE_OVAL:
1209  case PAD_SHAPE_TRAPEZOID:
1210  case PAD_SHAPE_RECT:
1211  case PAD_SHAPE_ROUNDRECT:
1213  {
1214  VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
1215  VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
1216  VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
1217  VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
1218 
1219  pinEditedCorner( getEditedPointIndex(), Mils2iu( 1 ), Mils2iu( 1 ), topLeft, topRight,
1220  botLeft, botRight,pad->GetPosition(), pad->GetDrillSize() );
1221 
1222  if( ( pad->GetOffset().x || pad->GetOffset().y )
1223  || ( pad->GetDrillSize().x && pad->GetDrillSize().y ) )
1224  {
1225  // Keep hole pinned at the current location; adjust the pad around the hole
1226 
1227  wxPoint center = pad->GetPosition();
1228  int dist[4];
1229 
1230  if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1231  || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1232  {
1233  dist[0] = center.x - m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().x;
1234  dist[1] = center.y - m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().y;
1235  dist[2] = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().x - center.x;
1236  dist[3] = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().y - center.y;
1237  }
1238  else
1239  {
1240  dist[0] = center.x - m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().x;
1241  dist[1] = center.y - m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().y;
1242  dist[2] = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().x - center.x;
1243  dist[3] = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().y - center.y;
1244  }
1245 
1246  wxSize padSize( dist[0] + dist[2], dist[1] + dist[3] );
1247  wxPoint deltaOffset( padSize.x / 2 - dist[2], padSize.y / 2 - dist[3] );
1248 
1249  if( pad->GetOrientation() == 900 || pad->GetOrientation() == 2700 )
1250  std::swap( padSize.x, padSize.y );
1251 
1252  RotatePoint( &deltaOffset, -pad->GetOrientation() );
1253 
1254  pad->SetSize( padSize );
1255  pad->SetOffset( -deltaOffset );
1256  }
1257  else
1258  {
1259  // Keep pad position at the center of the pad shape
1260 
1261  int left, top, right, bottom;
1262 
1263  if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1264  || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1265  {
1266  left = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().x;
1267  top = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition().y;
1268  right = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().x;
1269  bottom = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition().y;
1270  }
1271  else
1272  {
1273  left = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().x;
1274  top = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().y;
1275  right = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition().x;
1276  bottom = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition().y;
1277  }
1278 
1279  wxSize padSize( abs( right - left ), abs( bottom - top ) );
1280 
1281  if( pad->GetOrientation() == 900 || pad->GetOrientation() == 2700 )
1282  std::swap( padSize.x, padSize.y );
1283 
1284  pad->SetSize( padSize );
1285  pad->SetPosition( wxPoint( ( left + right ) / 2, ( top + bottom ) / 2 ) );
1286  }
1287  }
1288  break;
1289 
1290  default: // suppress warnings
1291  break;
1292  }
1293  }
1294  break;
1295 
1297  case PCB_ZONE_AREA_T:
1298  {
1299  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
1300  zone->ClearFilledPolysList();
1301  SHAPE_POLY_SET& outline = *zone->Outline();
1302 
1303  for( int i = 0; i < outline.TotalVertices(); ++i )
1304  {
1305  if( outline.CVertex( i ) != m_editPoints->Point( i ).GetPosition() )
1306  zone->SetNeedRefill( true );
1307 
1308  outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
1309  }
1310 
1311  validatePolygon( outline );
1312  zone->HatchBorder();
1313  break;
1314  }
1315 
1316  case PCB_DIM_ALIGNED_T:
1317  {
1318  ALIGNED_DIMENSION* dimension = static_cast<ALIGNED_DIMENSION*>( item );
1319 
1320  // Check which point is currently modified and updated dimension's points respectively
1321  if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) )
1322  {
1323  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetStart() );
1324  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1325 
1326  if( featureLine.Cross( crossBar ) > 0 )
1327  dimension->SetHeight( -featureLine.EuclideanNorm() );
1328  else
1329  dimension->SetHeight( featureLine.EuclideanNorm() );
1330  }
1331 
1332  else if( isModified( m_editPoints->Point( DIM_CROSSBAREND ) ) )
1333  {
1334  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
1335  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1336 
1337  if( featureLine.Cross( crossBar ) > 0 )
1338  dimension->SetHeight( -featureLine.EuclideanNorm() );
1339  else
1340  dimension->SetHeight( featureLine.EuclideanNorm() );
1341  }
1342 
1343  else if( isModified( m_editPoints->Point( DIM_START ) ) )
1344  {
1345  dimension->SetStart( wxPoint( m_editedPoint->GetPosition().x,
1346  m_editedPoint->GetPosition().y ) );
1347  m_editPoints->Point( DIM_CROSSBARSTART ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1348  m_editPoints->Point( DIM_START ) ) );
1349  m_editPoints->Point( DIM_CROSSBAREND ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1350  m_editPoints->Point( DIM_END ) ) );
1351  }
1352 
1353  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1354  {
1355  dimension->SetEnd( wxPoint( m_editedPoint->GetPosition().x,
1356  m_editedPoint->GetPosition().y ) );
1357  m_editPoints->Point( DIM_CROSSBARSTART ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1358  m_editPoints->Point( DIM_START ) ) );
1359  m_editPoints->Point( DIM_CROSSBAREND ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1360  m_editPoints->Point( DIM_END ) ) );
1361  }
1362 
1363  else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1364  {
1365  // Force manual mode if we weren't already in it
1367  dimension->Text().SetPosition( wxPoint( m_editedPoint->GetPosition() ) );
1368  dimension->Update();
1369  }
1370 
1371  break;
1372  }
1373 
1374  case PCB_DIM_ORTHOGONAL_T:
1375  {
1376  ORTHOGONAL_DIMENSION* dimension = static_cast<ORTHOGONAL_DIMENSION*>( item );
1377 
1378  BOX2I bounds( dimension->GetStart(),
1379  dimension->GetEnd() - dimension->GetStart() );
1380 
1381  VECTOR2I direction( m_editedPoint->GetPosition() - bounds.Centre() );
1382  bool vert = std::abs( direction.y ) < std::abs( direction.x );
1383  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetStart() );
1384 
1385  if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) ||
1386  isModified( m_editPoints->Point( DIM_CROSSBAREND ) ) )
1387  {
1388  // Only change the orientation when we move outside the bounds
1389  if( !bounds.Contains( m_editedPoint->GetPosition() ) )
1390  {
1393  }
1394 
1395  vert = dimension->GetOrientation() == ORTHOGONAL_DIMENSION::DIR::VERTICAL;
1396 
1397  dimension->SetHeight( vert ? featureLine.x : featureLine.y );
1398  }
1399  else if( isModified( m_editPoints->Point( DIM_START ) ) )
1400  {
1401  dimension->SetStart( wxPoint( m_editedPoint->GetPosition().x,
1402  m_editedPoint->GetPosition().y ) );
1403  }
1404  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1405  {
1406  dimension->SetEnd( wxPoint( m_editedPoint->GetPosition().x,
1407  m_editedPoint->GetPosition().y ) );
1408  }
1409  else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1410  {
1411  // Force manual mode if we weren't already in it
1413  dimension->Text().SetPosition( wxPoint( m_editedPoint->GetPosition() ) );
1414  dimension->Update();
1415  }
1416 
1417  break;
1418  }
1419 
1420  case PCB_DIM_CENTER_T:
1421  {
1422  CENTER_DIMENSION* dimension = static_cast<CENTER_DIMENSION*>( item );
1423 
1424  if( isModified( m_editPoints->Point( DIM_START ) ) )
1425  {
1426  dimension->SetStart( wxPoint( m_editedPoint->GetPosition().x,
1427  m_editedPoint->GetPosition().y ) );
1428  }
1429  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1430  {
1431  dimension->SetEnd( wxPoint( m_editedPoint->GetPosition().x,
1432  m_editedPoint->GetPosition().y ) );
1433  }
1434 
1435  dimension->Update();
1436 
1437  break;
1438  }
1439 
1440  case PCB_DIM_LEADER_T:
1441  {
1442  LEADER* dimension = static_cast<LEADER*>( item );
1443 
1444  if( isModified( m_editPoints->Point( DIM_START ) ) )
1445  {
1446  dimension->SetStart( wxPoint( m_editedPoint->GetPosition().x,
1447  m_editedPoint->GetPosition().y ) );
1448  }
1449  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1450  {
1452  wxPoint delta = newPoint - dimension->GetEnd();
1453 
1454  dimension->SetEnd( newPoint );
1455  dimension->Text().SetPosition( dimension->Text().GetPosition() + delta );
1456  }
1457  else if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
1458  {
1459  dimension->Text().SetPosition( wxPoint( m_editedPoint->GetPosition() ) );
1460  }
1461 
1462  dimension->Update();
1463 
1464  break;
1465  }
1466 
1467  default:
1468  break;
1469  }
1470 
1471  getView()->Update( item );
1472 }
1473 
1474 
1476 {
1477  auto item = m_editPoints->GetParent();
1478 
1479  if( !item )
1480  return;
1481 
1482  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T )
1483  {
1484  auto zone = static_cast<ZONE_CONTAINER*>( item );
1485 
1486  if( zone->IsFilled() && m_refill && zone->NeedRefill() )
1487  m_toolMgr->RunAction( PCB_ACTIONS::zoneFill, true, zone );
1488  }
1489 }
1490 
1491 
1493 {
1494  bool valid = !aPoly.IsSelfIntersecting();
1495 
1496  if( m_statusPopup )
1497  {
1498  if( valid )
1499  {
1500  m_statusPopup->Hide();
1501  }
1502  else
1503  {
1504  wxPoint p = wxGetMousePosition() + wxPoint( 20, 20 );
1505  m_statusPopup->Move( p );
1506  m_statusPopup->PopupFor( 1500 );
1507  }
1508  }
1509 
1510  return valid;
1511 }
1512 
1513 
1515 {
1516  if( !m_editPoints )
1517  return;
1518 
1519  EDA_ITEM* item = m_editPoints->GetParent();
1520 
1521  if( !item )
1522  return;
1523 
1524  switch( item->Type() )
1525  {
1526  case PCB_LINE_T:
1527  case PCB_MODULE_EDGE_T:
1528  {
1529  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( item );
1530 
1531  switch( segment->GetShape() )
1532  {
1533  case S_SEGMENT:
1534  m_editPoints->Point( SEG_START ).SetPosition( segment->GetStart() );
1535  m_editPoints->Point( SEG_END ).SetPosition( segment->GetEnd() );
1536  break;
1537 
1538  case S_RECT:
1539  m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( segment->GetStart() );
1540  m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( segment->GetEnd().x,
1541  segment->GetStart().y );
1542  m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( segment->GetEnd() );
1543  m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( segment->GetStart().x,
1544  segment->GetEnd().y );
1545  break;
1546 
1547  case S_ARC:
1548  m_editPoints->Point( ARC_CENTER ).SetPosition( segment->GetCenter() );
1549  m_editPoints->Point( ARC_START ).SetPosition( segment->GetArcStart() );
1550  m_editPoints->Point( ARC_MID ).SetPosition( segment->GetArcMid() );
1551  m_editPoints->Point( ARC_END ).SetPosition( segment->GetArcEnd() );
1552  break;
1553 
1554  case S_CIRCLE:
1555  m_editPoints->Point( CIRC_CENTER ).SetPosition( segment->GetCenter() );
1556  m_editPoints->Point( CIRC_END ).SetPosition( segment->GetEnd() );
1557  break;
1558 
1559  case S_POLYGON:
1560  {
1561  const auto& points = segment->BuildPolyPointsList();
1562 
1563  if( m_editPoints->PointsSize() != (unsigned) points.size() )
1564  {
1565  getView()->Remove( m_editPoints.get() );
1566  m_editedPoint = nullptr;
1567  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
1568  getView()->Add( m_editPoints.get() );
1569  }
1570  else
1571  {
1572  for( unsigned i = 0; i < points.size(); i++ )
1573  m_editPoints->Point( i ).SetPosition( points[i] );
1574  }
1575  break;
1576  }
1577 
1578  case S_CURVE:
1579  m_editPoints->Point( BEZIER_CURVE_START ).SetPosition( segment->GetStart() );
1580  m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).SetPosition( segment->GetBezControl1() );
1581  m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).SetPosition( segment->GetBezControl2() );
1582  m_editPoints->Point( BEZIER_CURVE_END ).SetPosition( segment->GetEnd() );
1583  break;
1584 
1585  default: // suppress warnings
1586  break;
1587  }
1588 
1589  break;
1590  }
1591 
1592  case PCB_PAD_T:
1593  {
1594  const D_PAD* pad = static_cast<const D_PAD*>( item );
1595  bool locked = pad->GetParent() && pad->GetParent()->PadsLocked();
1596  wxPoint shapePos = pad->ShapePos();
1597  wxPoint halfSize( pad->GetSize().x / 2, pad->GetSize().y / 2 );
1598 
1599  switch( pad->GetShape() )
1600  {
1601  case PAD_SHAPE_CIRCLE:
1602  {
1603  int target = locked ? 0 : 2;
1604 
1605  // Careful; pad shape is mutable...
1606  if( int( m_editPoints->PointsSize() ) != target )
1607  {
1608  getView()->Remove( m_editPoints.get() );
1609  m_editedPoint = nullptr;
1610  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
1611  getView()->Add( m_editPoints.get() );
1612  }
1613  else if( target == 2 )
1614  {
1615  VECTOR2I vec = m_editPoints->Point( CIRC_END ).GetPosition()
1616  - m_editPoints->Point( CIRC_CENTER ).GetPosition();
1617  vec.Resize( halfSize.x );
1618 
1619  m_editPoints->Point( CIRC_CENTER ).SetPosition( shapePos );
1620  m_editPoints->Point( CIRC_END ).SetPosition( vec + shapePos );
1621  }
1622  }
1623  break;
1624 
1625  case PAD_SHAPE_OVAL:
1626  case PAD_SHAPE_TRAPEZOID:
1627  case PAD_SHAPE_RECT:
1628  case PAD_SHAPE_ROUNDRECT:
1630  {
1631  // Careful; pad shape and orientation are mutable...
1632  int target = locked || (int) pad->GetOrientation() % 900 > 0 ? 0 : 4;
1633 
1634  if( int( m_editPoints->PointsSize() ) != target )
1635  {
1636  getView()->Remove( m_editPoints.get() );
1637  m_editedPoint = nullptr;
1638  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
1639  getView()->Add( m_editPoints.get() );
1640  }
1641  else if( target == 4 )
1642  {
1643  if( pad->GetOrientation() == 900 || pad->GetOrientation() == 2700 )
1644  std::swap( halfSize.x, halfSize.y );
1645 
1646  m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shapePos - halfSize );
1647  m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( wxPoint( shapePos.x + halfSize.x,
1648  shapePos.y - halfSize.y ) );
1649  m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shapePos + halfSize );
1650  m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( wxPoint( shapePos.x - halfSize.x,
1651  shapePos.y + halfSize.y ) );
1652  }
1653  }
1654  break;
1655 
1656  default: // suppress warnings
1657  break;
1658  }
1659  }
1660  break;
1661 
1663  case PCB_ZONE_AREA_T:
1664  {
1665  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
1666  const SHAPE_POLY_SET* outline = zone->Outline();
1667 
1668  if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
1669  {
1670  getView()->Remove( m_editPoints.get() );
1671  m_editedPoint = nullptr;
1672  m_editPoints = EDIT_POINTS_FACTORY::Make( item, getView()->GetGAL() );
1673  getView()->Add( m_editPoints.get() );
1674  }
1675  else
1676  {
1677  for( int i = 0; i < outline->TotalVertices(); ++i )
1678  m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
1679  }
1680 
1681  break;
1682  }
1683 
1684  case PCB_DIM_ALIGNED_T:
1685  case PCB_DIM_ORTHOGONAL_T:
1686  {
1687  const ALIGNED_DIMENSION* dimension = static_cast<const ALIGNED_DIMENSION*>( item );
1688 
1689  m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1690  m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1691  m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->Text().GetPosition() );
1692  m_editPoints->Point( DIM_CROSSBARSTART ).SetPosition( dimension->GetCrossbarStart() );
1693  m_editPoints->Point( DIM_CROSSBAREND ).SetPosition( dimension->GetCrossbarEnd() );
1694  break;
1695  }
1696 
1697  case PCB_DIM_CENTER_T:
1698  {
1699  const CENTER_DIMENSION* dimension = static_cast<const CENTER_DIMENSION*>( item );
1700 
1701  m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1702  m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1703  break;
1704  }
1705 
1706  case PCB_DIM_LEADER_T:
1707  {
1708  const LEADER* dimension = static_cast<const LEADER*>( item );
1709 
1710  m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1711  m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1712  m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->Text().GetPosition() );
1713  break;
1714  }
1715 
1716  default:
1717  break;
1718  }
1719 
1720  getView()->Update( m_editPoints.get() );
1721 }
1722 
1723 
1725 {
1727 
1728  if( aPoint )
1729  {
1730  frame()->GetCanvas()->SetCurrentCursor( wxCURSOR_ARROW );
1731  controls->ForceCursorPosition( true, aPoint->GetPosition() );
1732  controls->ShowCursor( true );
1733  }
1734  else
1735  {
1736  if( frame()->ToolStackIsEmpty() )
1737  controls->ShowCursor( false );
1738 
1739  controls->ForceCursorPosition( false );
1740  }
1741 
1742  m_editedPoint = aPoint;
1743 }
1744 
1745 
1746 void POINT_EDITOR::setAltConstraint( bool aEnabled )
1747 {
1748  if( aEnabled )
1749  {
1750  EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
1751  bool isPoly = false;
1752 
1753  if( m_editPoints->GetParent()->Type() == PCB_ZONE_AREA_T
1754  || m_editPoints->GetParent()->Type() == PCB_MODULE_ZONE_AREA_T )
1755  isPoly = true;
1756 
1757  else if( m_editPoints->GetParent()->Type() == PCB_LINE_T
1758  || m_editPoints->GetParent()->Type() == PCB_MODULE_EDGE_T )
1759  {
1760  DRAWSEGMENT* seg = static_cast<DRAWSEGMENT*>( m_editPoints->GetParent() );
1761  isPoly = seg->GetShape() == S_POLYGON;
1762  }
1763 
1764  if( line && isPoly )
1765  {
1767  }
1768  else
1769  {
1770  // Find a proper constraining point for 45 degrees mode
1773  }
1774  }
1775  else
1776  {
1777  m_altConstraint.reset();
1778  }
1779 }
1780 
1781 
1783 {
1784  EDA_ITEM* item = m_editPoints->GetParent();
1785 
1786  switch( item->Type() )
1787  {
1788  case PCB_LINE_T:
1789  case PCB_MODULE_EDGE_T:
1790  {
1791  const DRAWSEGMENT* segment = static_cast<const DRAWSEGMENT*>( item );
1792  {
1793  switch( segment->GetShape() )
1794  {
1795  case S_SEGMENT:
1796  return *( m_editPoints->Next( *m_editedPoint ) ); // select the other end of line
1797 
1798  case S_ARC:
1799  case S_CIRCLE:
1800  return m_editPoints->Point( CIRC_CENTER );
1801 
1802  default: // suppress warnings
1803  break;
1804  }
1805  }
1806 
1807  break;
1808  }
1809 
1810  case PCB_DIM_ALIGNED_T:
1811  {
1812  // Constraint for crossbar
1813  if( isModified( m_editPoints->Point( DIM_START ) ) )
1814  return m_editPoints->Point( DIM_END );
1815 
1816  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1817  return m_editPoints->Point( DIM_START );
1818 
1819  else
1820  return EDIT_POINT( m_editedPoint->GetPosition() ); // no constraint
1821 
1822  break;
1823  }
1824 
1825  case PCB_DIM_CENTER_T:
1826  {
1827  if( isModified( m_editPoints->Point( DIM_END ) ) )
1828  return m_editPoints->Point( DIM_START );
1829 
1830  break;
1831  }
1832 
1833  default:
1834  break;
1835  }
1836 
1837  // In any other case we may align item to its original position
1838  return m_original;
1839 }
1840 
1841 
1843 {
1844  const auto type = aItem.Type();
1845 
1846  // Works only for zones and line segments
1847  return type == PCB_ZONE_AREA_T || type == PCB_MODULE_ZONE_AREA_T ||
1848  ( ( type == PCB_LINE_T || type == PCB_MODULE_EDGE_T ) &&
1849  ( static_cast<const DRAWSEGMENT&>( aItem ).GetShape() == S_SEGMENT ||
1850  static_cast<const DRAWSEGMENT&>( aItem ).GetShape() == S_POLYGON ) );
1851 }
1852 
1853 
1855 {
1856  if( aSelection.Size() != 1 )
1857  return false;
1858 
1859  const EDA_ITEM* item = aSelection.Front();
1860 
1861  return ( item != nullptr ) && canAddCorner( *item );
1862 }
1863 
1864 
1865 // Finds a corresponding vertex in a polygon set
1866 static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
1867 findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
1868 {
1869  for( auto it = aPolySet.IterateWithHoles(); it; ++it )
1870  {
1871  auto vertexIdx = it.GetIndex();
1872 
1873  if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
1874  return std::make_pair( true, vertexIdx );
1875  }
1876 
1877  return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
1878 }
1879 
1880 
1882 {
1883  if( !m_editPoints || !m_editedPoint )
1884  return false;
1885 
1886  EDA_ITEM* item = m_editPoints->GetParent();
1887 
1888  if( !item || !( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T ||
1889  ( ( item->Type() == PCB_MODULE_EDGE_T || item->Type() == PCB_LINE_T ) &&
1890  static_cast<DRAWSEGMENT*>( item )->GetShape() == S_POLYGON ) ) )
1891  return false;
1892 
1893  SHAPE_POLY_SET *polyset;
1894 
1895  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T )
1896  polyset = static_cast<ZONE_CONTAINER*>( item )->Outline();
1897  else
1898  polyset = &static_cast<DRAWSEGMENT*>( item )->GetPolyShape();
1899 
1900  auto vertex = findVertex( *polyset, *m_editedPoint );
1901 
1902  if( !vertex.first )
1903  return false;
1904 
1905  const auto& vertexIdx = vertex.second;
1906 
1907  // Check if there are enough vertices so one can be removed without
1908  // degenerating the polygon.
1909  // The first condition allows one to remove all corners from holes (when
1910  // there are only 2 vertices left, a hole is removed).
1911  if( vertexIdx.m_contour == 0 && polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
1912  return false;
1913 
1914  // Remove corner does not work with lines
1915  if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
1916  return false;
1917 
1918  return m_editedPoint != NULL;
1919 }
1920 
1921 
1923 {
1924  if( !m_editPoints )
1925  return 0;
1926 
1927  EDA_ITEM* item = m_editPoints->GetParent();
1928  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1929  const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
1930 
1931  // called without an active edited polygon
1932  if( !item || !canAddCorner( *item ) )
1933  return 0;
1934 
1935  DRAWSEGMENT* graphicItem = dynamic_cast<DRAWSEGMENT*>( item );
1936  BOARD_COMMIT commit( frame );
1937 
1938  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T ||
1939  ( graphicItem && graphicItem->GetShape() == S_POLYGON ) )
1940  {
1941  unsigned int nearestIdx = 0;
1942  unsigned int nextNearestIdx = 0;
1943  unsigned int nearestDist = INT_MAX;
1944  unsigned int firstPointInContour = 0;
1945  SHAPE_POLY_SET* zoneOutline;
1946 
1947  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T )
1948  {
1949  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item );
1950  zoneOutline = zone->Outline();
1951  zone->SetNeedRefill( true );
1952  }
1953  else
1954  zoneOutline = &( graphicItem->GetPolyShape() );
1955 
1956  commit.Modify( item );
1957 
1958  // Search the best outline segment to add a new corner
1959  // and therefore break this segment into two segments
1960 
1961  // Object to iterate through the corners of the outlines (main contour and its holes)
1962  SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0,
1963  zoneOutline->OutlineCount()-1, /* IterateHoles */ true );
1964  int curr_idx = 0;
1965 
1966  // Iterate through all the corners of the outlines and search the best segment
1967  for( ; iterator; iterator++, curr_idx++ )
1968  {
1969  int jj = curr_idx+1;
1970 
1971  if( iterator.IsEndContour() )
1972  { // We reach the last point of the current contour (main or hole)
1973  jj = firstPointInContour;
1974  firstPointInContour = curr_idx+1; // Prepare next contour analysis
1975  }
1976 
1977  SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
1978 
1979  unsigned int distance = curr_segment.Distance( cursorPos );
1980 
1981  if( distance < nearestDist )
1982  {
1983  nearestDist = distance;
1984  nearestIdx = curr_idx;
1985  nextNearestIdx = jj;
1986  }
1987  }
1988 
1989  // Find the point on the closest segment
1990  auto& sideOrigin = zoneOutline->CVertex( nearestIdx );
1991  auto& sideEnd = zoneOutline->CVertex( nextNearestIdx );
1992  SEG nearestSide( sideOrigin, sideEnd );
1993  VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
1994 
1995  // Do not add points that have the same coordinates as ones that already belong to polygon
1996  // instead, add a point in the middle of the side
1997  if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
1998  nearestPoint = ( sideOrigin + sideEnd ) / 2;
1999 
2000  zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
2001 
2002  // We re-hatch the filled zones but not polygons
2003  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T )
2004  static_cast<ZONE_CONTAINER*>( item )->HatchBorder();
2005 
2006 
2007  commit.Push( _( "Add a zone corner" ) );
2008  }
2009 
2010  else if( graphicItem && graphicItem->GetShape() == S_SEGMENT )
2011  {
2012  commit.Modify( graphicItem );
2013 
2014  SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
2015  VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
2016 
2017  // Move the end of the line to the break point..
2018  graphicItem->SetEnd( wxPoint( nearestPoint.x, nearestPoint.y ) );
2019 
2020  if( graphicItem->Type() == PCB_MODULE_EDGE_T )
2021  static_cast<EDGE_MODULE*>( graphicItem )->SetLocalCoord();
2022 
2023  // and add another one starting from the break point
2024  DRAWSEGMENT* newSegment;
2025 
2026  if( item->Type() == PCB_MODULE_EDGE_T )
2027  {
2028  EDGE_MODULE* edge = static_cast<EDGE_MODULE*>( graphicItem );
2029  assert( edge->GetParent()->Type() == PCB_MODULE_T );
2030  newSegment = new EDGE_MODULE( *edge );
2031  }
2032  else
2033  {
2034  newSegment = new DRAWSEGMENT( *graphicItem );
2035  }
2036 
2037  newSegment->ClearSelected();
2038  newSegment->SetStart( wxPoint( nearestPoint.x, nearestPoint.y ) );
2039  newSegment->SetEnd( wxPoint( seg.B.x, seg.B.y ) );
2040 
2041  if( newSegment->Type() == PCB_MODULE_EDGE_T )
2042  static_cast<EDGE_MODULE*>( newSegment )->SetLocalCoord();
2043 
2044  commit.Add( newSegment );
2045  commit.Push( _( "Split segment" ) );
2046  }
2047 
2048  updatePoints();
2049  return 0;
2050 }
2051 
2052 
2054 {
2055  if( !m_editPoints || !m_editedPoint )
2056  return 0;
2057 
2058  EDA_ITEM* item = m_editPoints->GetParent();
2059 
2060  if( !item )
2061  return 0;
2062 
2063  SHAPE_POLY_SET* polygon = nullptr;
2064 
2065  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T )
2066  {
2067  auto zone = static_cast<ZONE_CONTAINER*>( item );
2068  polygon = zone->Outline();
2069  zone->SetNeedRefill( true );
2070  }
2071  else if( (item->Type() == PCB_MODULE_EDGE_T ) || ( item->Type() == PCB_LINE_T ) )
2072  {
2073  auto ds = static_cast<DRAWSEGMENT*>( item );
2074 
2075  if( ds->GetShape() == S_POLYGON )
2076  polygon = &ds->GetPolyShape();
2077  }
2078 
2079  if( !polygon )
2080  return 0;
2081 
2082  PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
2083  BOARD_COMMIT commit( frame );
2084  auto vertex = findVertex( *polygon, *m_editedPoint );
2085 
2086  if( vertex.first )
2087  {
2088  const auto& vertexIdx = vertex.second;
2089  auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
2090 
2091  if( outline.PointCount() > 3 )
2092  {
2093  // the usual case: remove just the corner when there are >3 vertices
2094  commit.Modify( item );
2095  polygon->RemoveVertex( vertexIdx );
2096  validatePolygon( *polygon );
2097  }
2098  else
2099  {
2100  // either remove a hole or the polygon when there are <= 3 corners
2101  if( vertexIdx.m_contour > 0 )
2102  {
2103  // remove hole
2104  commit.Modify( item );
2105  polygon->RemoveContour( vertexIdx.m_contour );
2106  }
2107  else
2108  {
2110  commit.Remove( item );
2111  }
2112  }
2113 
2114  setEditedPoint( nullptr );
2115 
2116  commit.Push( _( "Remove a zone/polygon corner" ) );
2117 
2118  // Refresh zone hatching
2119  if( item->Type() == PCB_ZONE_AREA_T || item->Type() == PCB_MODULE_ZONE_AREA_T )
2120  static_cast<ZONE_CONTAINER*>( item )->HatchBorder();
2121 
2122  updatePoints();
2123  }
2124 
2125  return 0;
2126 }
2127 
2128 
2130 {
2131  updatePoints();
2132  return 0;
2133 }
2134 
2136 {
2138  return 0;
2139 }
2140 
2142 {
2150 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:133
static TOOL_ACTION selectionClear
Clears the current selection.
Definition: pcb_actions.h:62
Text placement is manually set by the user.
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
void HatchBorder()
Function HatchBorder computes the hatch lines depending on the hatch parameters and stores it in the ...
Definition: class_zone.cpp:914
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:206
class ALIGNED_DIMENSION, a linear dimension (graphic item)
Definition: typeinfo.h:101
void setTransitions() override
Sets up handlers for various events.
class LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:102
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()
static TOOL_ACTION activatePointEditor
Definition: actions.h:166
void SetArcStart(const wxPoint &aArcStartPoint)
Initialize the start arc point.
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:207
This file is part of the common library.
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:165
void editArcEndpointKeepCenter(DRAWSEGMENT *aArc, VECTOR2I aCenter, VECTOR2I aStart, VECTOR2I aMid, VECTOR2I aEnd, const VECTOR2I aCursor) const
Move an end point of the arc, while keeping radius, and the other point position.
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)
virtual void SetStart(const wxPoint &aPoint)
constexpr KICAD_T BaseType(const KICAD_T aType)
Returns the underlying type of the given type.
Definition: typeinfo.h:233
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
class CENTER_DIMENSION, a center point marking (graphic item)
Definition: typeinfo.h:103
static TOOL_ACTION changeEditMethod
Definition: actions.h:167
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:227
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...
void Update()
Updates the dimension's cached text and geometry.
class ZONE_CONTAINER, a zone area
Definition: typeinfo.h:106
virtual void Remove(VIEW_ITEM *aItem)
Function Remove() Removes a VIEW_ITEM from the view.
Definition: view.cpp:375
EC_PERPLINE.
SHAPE_POLY_SET * Outline()
Definition: class_zone.h:300
CONST_ITERATOR CIterateWithHoles(int aOutline) const
TOOL_MENU & GetToolMenu()
bool IsMotion() const
Definition: tool_event.h:306
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:224
Struct VERTEX_INDEX.
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:159
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
virtual const wxPoint & GetEnd() const
bool isModified(const EDIT_POINT &aPoint) const
Returns true if aPoint is the currently modified point.
Definition: point_editor.h:133
void UndoRedoBlock(bool aBlock=true)
Function UndoRedoBlock Enables/disable undo and redo operations.
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
std::unique_ptr< STATUS_TEXT_POPUP > m_statusPopup
Definition: point_editor.h:97
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.
const wxPoint & GetCrossbarStart() const
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:296
EC_LINE.
virtual void Remove(VIEW_ITEM *aItem) override
Function Remove() Removes a VIEW_ITEM from the view.
Definition: pcb_view.cpp:76
EC_45DEGREE.
void updateItem() const
Updates item's points with edit points.
static const TOOL_EVENT SelectedItemsModified
Definition: actions.h:211
bool removeCornerCondition(const SELECTION &aSelection)
Condition to display "Remove corner" context menu entry.
const wxPoint & GetCrossbarEnd() const
PCBNEW_SELECTION & GetSelection()
Function GetSelection()
EC_CIRCLE.
PCB_BASE_EDIT_FRAME * frame() const
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:83
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:182
void SetArcEnd(const wxPoint &aArcEndPoint)
Initialize the end arc point.
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:296
void SetHeight(int aHeight)
Sets the distance from the feature points to the crossbar line.
LSET is a set of PCB_LAYER_IDs.
const PCBNEW_SELECTION & selection() const
virtual VECTOR2I GetPosition() const
Function GetPosition()
Definition: edit_points.h:70
#define NULL
VECTOR2< double > VECTOR2D
Definition: vector2d.h:593
double m_drawArcAccuracy
For drawsegments - arcs.
A leader is a dimension-like object pointing to a specific point.
bool IsGridVisible() const
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:203
static TOOL_ACTION pointEditorRemoveCorner
Removes a corner.
Definition: pcb_actions.h:206
Arcs (with rounded ends)
virtual wxPoint GetPosition() const override
const wxPoint & GetOffset() const
Definition: class_pad.h:233
TOOL_EVENT.
Definition: tool_event.h:171
std::shared_ptr< EDIT_CONSTRAINT< EDIT_POINT > > m_altConstraint
Definition: point_editor.h:86
DIR GetOrientation() const
COMMIT & StageItems(const Range &aRange, CHANGE_TYPE aChangeType)
Definition: commit.h:116
bool m_altEditMethod
Definition: point_editor.h:95
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
KIGFX::PCB_VIEW * view() const
DIMENSION_POINTS
void SetSize(const wxSize &aSize)
Definition: class_pad.h:223
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:786
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.
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:1531
class DIMENSION: abstract dimension meta-type
Definition: typeinfo.h:100
void SetOrientation(DIR aOrientation)
Sets the orientation of the dimension line (so, perpendicular to the feature lines)
void editArcEndpointKeepTangent(DRAWSEGMENT *aArc, VECTOR2I aCenter, VECTOR2I aStart, VECTOR2I aMid, VECTOR2I aEnd, const VECTOR2I aCursor) const
Move an end point of the arc, while keeping the tangent at the other endpoint.
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:111
Definition: seg.h:39
int getEditedPointIndex() const
Definition: point_editor.h:121
void editArcMidKeepEnpoints(DRAWSEGMENT *aArc, VECTOR2I aCenter, VECTOR2I aStart, VECTOR2I aMid, VECTOR2I aEnd, const VECTOR2I aCursor) const
Move the mid point of the arc, while keeping the two endpoints.
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
virtual void SetPosition(const wxPoint &aPos) override
double GetAngle() const
RECT_POINTS
bool validatePolygon(SHAPE_POLY_SET &aModified) const
Validates a polygon and displays a popup warning if invalid.
virtual const wxPoint & GetStart() const
The dimension's origin is the first feature point for the dimension.
ARC_POINTS
int changeEditMethod(const TOOL_EVENT &aEvent)
Change the edit method to an alternative method ( currently, arcs only )
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 a variety of units (the basic call r...
Definition: class_pad.h:321
EDIT_POINT * m_hoveredPoint
Definition: point_editor.h:77
void RemoveVertex(int aGlobalIndex)
Function RemoveVertex deletes the aGlobalIndex-th vertex.
CIRCLE_POINTS
TEXTE_PCB & Text()
const wxSize & GetDrillSize() const
Definition: class_pad.h:230
void SetHover(bool aHover=true)
Definition: edit_points.h:185
wxPoint ShapePos() const
Definition: class_pad.cpp:609
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 SetTextPositionMode(DIM_TEXT_POSITION aMode)
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)
virtual 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:347
EDIT_POINT m_original
Original position for the current drag point.
Definition: point_editor.h:80
void Activate()
Function Activate() Runs the tool.
void InsertVertex(int aGlobalIndex, VECTOR2I aNewVertex)
Function InsertVertex Adds a vertex in the globally indexed position aGlobalIndex.
BEZIER_CURVE_POINTS
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers of advanced config.
class ORTHOGONAL_DIMENSION, a linear dimension constrained to x/y
Definition: typeinfo.h:104
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:59
T EuclideanNorm() const
Destructor.
Definition: vector2d.h:299
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:345
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:157
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:405
void editArcMidKeepCenter(DRAWSEGMENT *aArc, VECTOR2I aCenter, VECTOR2I aStart, VECTOR2I aMid, VECTOR2I aEnd, const VECTOR2I aCursor) const
Move the mid point of the arc, while keeping the angle.
EDIT_POINT m_altConstrainer
Definition: point_editor.h:89
const wxSize & GetSize() const
Definition: class_pad.h:224
EDGE_MODULE class definition.
For better understanding of the points that make a dimension:
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
double m_drawArcCenterMaxAngle
For drawsegments - arcs.
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:232
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Adds a menu entry to run a TOOL_ACTION on selected items.
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)
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)
const VECTOR2D Position() const
Returns mouse cursor position in world coordinates.
Definition: tool_event.h:274
virtual void SetEnd(const wxPoint &aPoint)
virtual void ApplyConstraint()
Function ApplyConstraint()
Definition: edit_points.h:175
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:608
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.
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...
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:107
void SetNeedRefill(bool aNeedRefill)
Definition: class_zone.h:218
Marks the center of a circle or arc with a cross shape The size and orientation of the cross is adjus...