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