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