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, "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  }
1283  }
1284  break;
1285 
1286  default: // suppress warnings
1287  break;
1288  }
1289 
1290  break;
1291  }
1292 
1293  case PCB_FP_ZONE_T:
1294  case PCB_ZONE_T:
1295  {
1296  ZONE* zone = static_cast<ZONE*>( item );
1297  zone->UnFill();
1298  SHAPE_POLY_SET& outline = *zone->Outline();
1299 
1300  for( int i = 0; i < outline.TotalVertices(); ++i )
1301  {
1302  if( outline.CVertex( i ) != m_editPoints->Point( i ).GetPosition() )
1303  zone->SetNeedRefill( true );
1304 
1305  outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
1306  }
1307 
1308  for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1309  {
1310  if( !isModified( m_editPoints->Line( i ) ) )
1311  m_editPoints->Line( i ).SetConstraint( new EC_PERPLINE( m_editPoints->Line( i ) ) );
1312  }
1313 
1314  validatePolygon( outline );
1315  zone->HatchBorder();
1316 
1317  // TODO Refill zone when KiCad supports auto re-fill
1318  break;
1319  }
1320 
1321  case PCB_DIM_ALIGNED_T:
1322  {
1323  PCB_DIM_ALIGNED* dimension = static_cast<PCB_DIM_ALIGNED*>( item );
1324 
1325  // Check which point is currently modified and updated dimension's points respectively
1326  if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) )
1327  {
1328  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetStart() );
1329  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1330 
1331  if( featureLine.Cross( crossBar ) > 0 )
1332  dimension->SetHeight( -featureLine.EuclideanNorm() );
1333  else
1334  dimension->SetHeight( featureLine.EuclideanNorm() );
1335 
1336  dimension->Update();
1337  }
1338  else if( isModified( m_editPoints->Point( DIM_CROSSBAREND ) ) )
1339  {
1340  VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
1341  VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1342 
1343  if( featureLine.Cross( crossBar ) > 0 )
1344  dimension->SetHeight( -featureLine.EuclideanNorm() );
1345  else
1346  dimension->SetHeight( featureLine.EuclideanNorm() );
1347 
1348  dimension->Update();
1349  }
1350  else if( isModified( m_editPoints->Point( DIM_START ) ) )
1351  {
1352  dimension->SetStart( (wxPoint) m_editedPoint->GetPosition() );
1353  dimension->Update();
1354 
1355  m_editPoints->Point( DIM_CROSSBARSTART ).
1356  SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1357  m_editPoints->Point( DIM_START ) ) );
1358  m_editPoints->Point( DIM_CROSSBAREND ).
1359  SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1360  m_editPoints->Point( DIM_END ) ) );
1361  }
1362  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1363  {
1364  dimension->SetEnd( (wxPoint) m_editedPoint->GetPosition() );
1365  dimension->Update();
1366 
1367  m_editPoints->Point( DIM_CROSSBARSTART ).
1368  SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1369  m_editPoints->Point( DIM_START ) ) );
1370  m_editPoints->Point( DIM_CROSSBAREND ).
1371  SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1372  m_editPoints->Point( DIM_END ) ) );
1373  }
1374  else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1375  {
1376  // Force manual mode if we weren't already in it
1378  dimension->Text().SetPosition( wxPoint( m_editedPoint->GetPosition() ) );
1379  dimension->Update();
1380  }
1381 
1382  break;
1383  }
1384 
1385  case PCB_DIM_ORTHOGONAL_T:
1386  {
1387  PCB_DIM_ORTHOGONAL* dimension = static_cast<PCB_DIM_ORTHOGONAL*>( item );
1388 
1389  if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) ||
1390  isModified( m_editPoints->Point( DIM_CROSSBAREND ) ) )
1391  {
1392  BOX2I bounds( dimension->GetStart(), dimension->GetEnd() - dimension->GetStart() );
1393 
1394  const VECTOR2I& cursorPos = m_editedPoint->GetPosition();
1395 
1396  // Find vector from nearest dimension point to edit position
1397  VECTOR2I directionA( cursorPos - dimension->GetStart() );
1398  VECTOR2I directionB( cursorPos - dimension->GetEnd() );
1399  VECTOR2I direction = ( directionA < directionB ) ? directionA : directionB;
1400 
1401  bool vert;
1402  VECTOR2D featureLine( cursorPos - dimension->GetStart() );
1403 
1404  // Only change the orientation when we move outside the bounds
1405  if( !bounds.Contains( cursorPos ) )
1406  {
1407  // If the dimension is horizontal or vertical, set correct orientation
1408  // otherwise, test if we're left/right of the bounding box or above/below it
1409  if( bounds.GetWidth() == 0 )
1410  vert = true;
1411  else if( bounds.GetHeight() == 0 )
1412  vert = false;
1413  else if( cursorPos.x > bounds.GetLeft() && cursorPos.x < bounds.GetRight() )
1414  vert = false;
1415  else if( cursorPos.y > bounds.GetTop() && cursorPos.y < bounds.GetBottom() )
1416  vert = true;
1417  else
1418  vert = std::abs( direction.y ) < std::abs( direction.x );
1419 
1422  }
1423  else
1424  {
1425  vert = dimension->GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1426  }
1427 
1428  dimension->SetHeight( vert ? featureLine.x : featureLine.y );
1429  }
1430  else if( isModified( m_editPoints->Point( DIM_START ) ) )
1431  {
1432  dimension->SetStart( (wxPoint) m_editedPoint->GetPosition() );
1433  }
1434  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1435  {
1436  dimension->SetEnd( (wxPoint) m_editedPoint->GetPosition() );
1437  }
1438  else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1439  {
1440  // Force manual mode if we weren't already in it
1442  dimension->Text().SetPosition( wxPoint( m_editedPoint->GetPosition() ) );
1443  }
1444 
1445  dimension->Update();
1446 
1447  break;
1448  }
1449 
1450  case PCB_DIM_CENTER_T:
1451  {
1452  PCB_DIM_CENTER* dimension = static_cast<PCB_DIM_CENTER*>( item );
1453 
1454  if( isModified( m_editPoints->Point( DIM_START ) ) )
1455  dimension->SetStart( (wxPoint) m_editedPoint->GetPosition() );
1456  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1457  dimension->SetEnd( (wxPoint) m_editedPoint->GetPosition() );
1458 
1459  dimension->Update();
1460 
1461  break;
1462  }
1463 
1464  case PCB_DIM_LEADER_T:
1465  {
1466  PCB_DIM_LEADER* dimension = static_cast<PCB_DIM_LEADER*>( item );
1467 
1468  if( isModified( m_editPoints->Point( DIM_START ) ) )
1469  {
1470  dimension->SetStart( (wxPoint) m_editedPoint->GetPosition() );
1471  }
1472  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1473  {
1474  wxPoint newPoint( m_editedPoint->GetPosition() );
1475  wxPoint delta = newPoint - dimension->GetEnd();
1476 
1477  dimension->SetEnd( newPoint );
1478  dimension->Text().SetPosition( dimension->Text().GetPosition() + delta );
1479  }
1480  else if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
1481  {
1482  dimension->Text().SetPosition( (wxPoint) m_editedPoint->GetPosition() );
1483  }
1484 
1485  dimension->Update();
1486 
1487  break;
1488  }
1489 
1490  default:
1491  break;
1492  }
1493 
1494  getView()->Update( item );
1495 }
1496 
1497 
1499 {
1500  auto item = m_editPoints->GetParent();
1501 
1502  if( !item )
1503  return;
1504 
1505  // TODO Refill edited zones when KiCad supports auto re-fill
1506 }
1507 
1508 
1510 {
1511  bool valid = !aPoly.IsSelfIntersecting();
1512 
1513  if( m_statusPopup )
1514  {
1515  if( valid )
1516  {
1517  m_statusPopup->Hide();
1518  }
1519  else
1520  {
1521  wxPoint p = wxGetMousePosition() + wxPoint( 20, 20 );
1522  m_statusPopup->Move( p );
1523  m_statusPopup->PopupFor( 1500 );
1524  }
1525  }
1526 
1527  return valid;
1528 }
1529 
1530 
1532 {
1533  if( !m_editPoints )
1534  return;
1535 
1536  EDA_ITEM* item = m_editPoints->GetParent();
1537 
1538  if( !item )
1539  return;
1540 
1541  switch( item->Type() )
1542  {
1543  case PCB_SHAPE_T:
1544  case PCB_FP_SHAPE_T:
1545  {
1546  const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
1547 
1548  switch( shape->GetShape() )
1549  {
1550  case SHAPE_T::SEGMENT:
1551  m_editPoints->Point( SEG_START ).SetPosition( shape->GetStart() );
1552  m_editPoints->Point( SEG_END ).SetPosition( shape->GetEnd() );
1553  break;
1554 
1555  case SHAPE_T::RECT:
1556  m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shape->GetStart() );
1557  m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( shape->GetEnd().x,
1558  shape->GetStart().y );
1559  m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shape->GetEnd() );
1560  m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( shape->GetStart().x,
1561  shape->GetEnd().y );
1562  break;
1563 
1564  case SHAPE_T::ARC:
1565  m_editPoints->Point( ARC_CENTER ).SetPosition( shape->GetCenter() );
1566  m_editPoints->Point( ARC_START ).SetPosition( shape->GetStart() );
1567  m_editPoints->Point( ARC_MID ).SetPosition( shape->GetArcMid() );
1568  m_editPoints->Point( ARC_END ).SetPosition( shape->GetEnd() );
1569  break;
1570 
1571  case SHAPE_T::CIRCLE:
1572  m_editPoints->Point( CIRC_CENTER ).SetPosition( shape->GetCenter() );
1573  m_editPoints->Point( CIRC_END ).SetPosition( shape->GetEnd() );
1574  break;
1575 
1576  case SHAPE_T::POLY:
1577  {
1578  std::vector<wxPoint> points;
1579  shape->DupPolyPointsList( points );
1580 
1581  if( m_editPoints->PointsSize() != (unsigned) points.size() )
1582  {
1583  getView()->Remove( m_editPoints.get() );
1584  m_editedPoint = nullptr;
1585 
1586  m_editPoints = makePoints( item );
1587 
1588  if( m_editPoints )
1589  getView()->Add( m_editPoints.get() );
1590  }
1591  else
1592  {
1593  for( unsigned i = 0; i < points.size(); i++ )
1594  m_editPoints->Point( i ).SetPosition( points[i] );
1595  }
1596 
1597  break;
1598  }
1599 
1600  case SHAPE_T::BEZIER:
1601  m_editPoints->Point( BEZIER_CURVE_START ).SetPosition( shape->GetStart() );
1602  m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).SetPosition( shape->GetBezierC1() );
1603  m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).SetPosition( shape->GetBezierC2() );
1604  m_editPoints->Point( BEZIER_CURVE_END ).SetPosition( shape->GetEnd() );
1605  break;
1606 
1607  default: // suppress warnings
1608  break;
1609  }
1610 
1611  break;
1612  }
1613 
1614  case PCB_PAD_T:
1615  {
1616  const PAD* pad = static_cast<const PAD*>( item );
1617  bool locked = pad->GetParent() && pad->IsLocked();
1618  wxPoint shapePos = pad->ShapePos();
1619  wxPoint halfSize( pad->GetSize().x / 2, pad->GetSize().y / 2 );
1620 
1621  switch( pad->GetShape() )
1622  {
1623  case PAD_SHAPE::CIRCLE:
1624  {
1625  int target = locked ? 0 : 1;
1626 
1627  // Careful; pad shape is mutable...
1628  if( int( m_editPoints->PointsSize() ) != target )
1629  {
1630  getView()->Remove( m_editPoints.get() );
1631  m_editedPoint = nullptr;
1632 
1633  m_editPoints = makePoints( item );
1634 
1635  if( m_editPoints )
1636  getView()->Add( m_editPoints.get() );
1637  }
1638  else if( target == 1 )
1639  {
1640  shapePos.x += halfSize.x;
1641  m_editPoints->Point( 0 ).SetPosition( shapePos );
1642  }
1643  }
1644  break;
1645 
1646  case PAD_SHAPE::OVAL:
1647  case PAD_SHAPE::TRAPEZOID:
1648  case PAD_SHAPE::RECT:
1649  case PAD_SHAPE::ROUNDRECT:
1651  {
1652  // Careful; pad shape and orientation are mutable...
1653  int target = locked || (int) pad->GetOrientation() % 900 > 0 ? 0 : 4;
1654 
1655  if( int( m_editPoints->PointsSize() ) != target )
1656  {
1657  getView()->Remove( m_editPoints.get() );
1658  m_editedPoint = nullptr;
1659 
1660  m_editPoints = makePoints( item );
1661 
1662  if( m_editPoints )
1663  getView()->Add( m_editPoints.get() );
1664  }
1665  else if( target == 4 )
1666  {
1667  if( pad->GetOrientation() == 900 || pad->GetOrientation() == 2700 )
1668  std::swap( halfSize.x, halfSize.y );
1669 
1670  m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shapePos - halfSize );
1671  m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( wxPoint( shapePos.x + halfSize.x,
1672  shapePos.y - halfSize.y ) );
1673  m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shapePos + halfSize );
1674  m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( wxPoint( shapePos.x - halfSize.x,
1675  shapePos.y + halfSize.y ) );
1676  }
1677 
1678  break;
1679  }
1680 
1681  default: // suppress warnings
1682  break;
1683  }
1684  }
1685  break;
1686 
1687  case PCB_FP_ZONE_T:
1688  case PCB_ZONE_T:
1689  {
1690  ZONE* zone = static_cast<ZONE*>( item );
1691  const SHAPE_POLY_SET* outline = zone->Outline();
1692 
1693  if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
1694  {
1695  getView()->Remove( m_editPoints.get() );
1696  m_editedPoint = nullptr;
1697 
1698  m_editPoints = makePoints( item );
1699 
1700  if( m_editPoints )
1701  getView()->Add( m_editPoints.get() );
1702  }
1703  else
1704  {
1705  for( int i = 0; i < outline->TotalVertices(); ++i )
1706  m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
1707  }
1708 
1709  break;
1710  }
1711 
1712  case PCB_DIM_ALIGNED_T:
1713  case PCB_DIM_ORTHOGONAL_T:
1714  {
1715  const PCB_DIM_ALIGNED* dimension = static_cast<const PCB_DIM_ALIGNED*>( item );
1716 
1717  m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1718  m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1719  m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->Text().GetPosition() );
1720  m_editPoints->Point( DIM_CROSSBARSTART ).SetPosition( dimension->GetCrossbarStart() );
1721  m_editPoints->Point( DIM_CROSSBAREND ).SetPosition( dimension->GetCrossbarEnd() );
1722  break;
1723  }
1724 
1725  case PCB_DIM_CENTER_T:
1726  {
1727  const PCB_DIM_CENTER* dimension = static_cast<const PCB_DIM_CENTER*>( item );
1728 
1729  m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1730  m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1731  break;
1732  }
1733 
1734  case PCB_DIM_LEADER_T:
1735  {
1736  const PCB_DIM_LEADER* dimension = static_cast<const PCB_DIM_LEADER*>( item );
1737 
1738  m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1739  m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1740  m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->Text().GetPosition() );
1741  break;
1742  }
1743 
1744  default:
1745  break;
1746  }
1747 
1748  getView()->Update( m_editPoints.get() );
1749 }
1750 
1751 
1753 {
1755 
1756  if( aPoint )
1757  {
1759  controls->ForceCursorPosition( true, aPoint->GetPosition() );
1760  controls->ShowCursor( true );
1761  }
1762  else
1763  {
1764  if( frame()->ToolStackIsEmpty() )
1765  controls->ShowCursor( false );
1766 
1767  controls->ForceCursorPosition( false );
1768  }
1769 
1770  m_editedPoint = aPoint;
1771 }
1772 
1773 
1775 {
1776  if( aEnabled )
1777  {
1778  EDA_ITEM* parent = m_editPoints->GetParent();
1779  EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
1780  bool isPoly;
1781 
1782  switch( parent->Type() )
1783  {
1784  case PCB_ZONE_T:
1785  case PCB_FP_ZONE_T:
1786  isPoly = true;
1787  break;
1788 
1789  case PCB_SHAPE_T:
1790  case PCB_FP_SHAPE_T:
1791  isPoly = static_cast<PCB_SHAPE*>( parent )->GetShape() == SHAPE_T::POLY;
1792  break;
1793 
1794  default:
1795  isPoly = false;
1796  break;
1797  }
1798 
1799  if( line && isPoly )
1800  {
1801  EC_CONVERGING* altConstraint = new EC_CONVERGING( *line, *m_editPoints );
1802  m_altConstraint.reset( (EDIT_CONSTRAINT<EDIT_POINT>*) altConstraint );
1803  }
1804  else
1805  {
1806  // Find a proper constraining point for 45 degrees mode
1809  }
1810  }
1811  else
1812  {
1813  m_altConstraint.reset();
1814  }
1815 }
1816 
1817 
1819 {
1820  EDA_ITEM* item = m_editPoints->GetParent();
1821 
1822  switch( item->Type() )
1823  {
1824  case PCB_SHAPE_T:
1825  case PCB_FP_SHAPE_T:
1826  switch( static_cast<const PCB_SHAPE*>( item )->GetShape() )
1827  {
1828  case SHAPE_T::SEGMENT:
1829  return *( m_editPoints->Next( *m_editedPoint ) ); // select the other end of line
1830 
1831  case SHAPE_T::ARC:
1832  case SHAPE_T::CIRCLE:
1833  return m_editPoints->Point( CIRC_CENTER );
1834 
1835  default: // suppress warnings
1836  break;
1837  }
1838 
1839  break;
1840 
1841  case PCB_DIM_ALIGNED_T:
1842  {
1843  // Constraint for crossbar
1844  if( isModified( m_editPoints->Point( DIM_START ) ) )
1845  return m_editPoints->Point( DIM_END );
1846 
1847  else if( isModified( m_editPoints->Point( DIM_END ) ) )
1848  return m_editPoints->Point( DIM_START );
1849 
1850  else
1851  return EDIT_POINT( m_editedPoint->GetPosition() ); // no constraint
1852 
1853  break;
1854  }
1855 
1856  case PCB_DIM_CENTER_T:
1857  {
1858  if( isModified( m_editPoints->Point( DIM_END ) ) )
1859  return m_editPoints->Point( DIM_START );
1860 
1861  break;
1862  }
1863 
1864  default:
1865  break;
1866  }
1867 
1868  // In any other case we may align item to its original position
1869  return m_original;
1870 }
1871 
1872 
1874 {
1875  const auto type = aItem.Type();
1876 
1877  // Works only for zones and line segments
1878  if( type == PCB_ZONE_T || type == PCB_FP_ZONE_T )
1879  return true;
1880 
1881  if( type == PCB_SHAPE_T || type == PCB_FP_SHAPE_T )
1882  {
1883  const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
1884  return shape.GetShape() == SHAPE_T::SEGMENT || shape.GetShape() == SHAPE_T::POLY;
1885  }
1886 
1887  return false;
1888 }
1889 
1890 
1892 {
1893  if( aSelection.Size() != 1 )
1894  return false;
1895 
1896  const EDA_ITEM* item = aSelection.Front();
1897 
1898  return ( item != nullptr ) && canAddCorner( *item );
1899 }
1900 
1901 
1902 // Finds a corresponding vertex in a polygon set
1903 static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
1904 findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
1905 {
1906  for( auto it = aPolySet.IterateWithHoles(); it; ++it )
1907  {
1908  auto vertexIdx = it.GetIndex();
1909 
1910  if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
1911  return std::make_pair( true, vertexIdx );
1912  }
1913 
1914  return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
1915 }
1916 
1917 
1919 {
1920  if( !m_editPoints || !m_editedPoint )
1921  return false;
1922 
1923  EDA_ITEM* item = m_editPoints->GetParent();
1924  SHAPE_POLY_SET* polyset = nullptr;
1925 
1926  if( !item )
1927  return false;
1928 
1929  switch( item->Type() )
1930  {
1931  case PCB_ZONE_T:
1932  case PCB_FP_ZONE_T:
1933  polyset = static_cast<ZONE*>( item )->Outline();
1934  break;
1935 
1936  case PCB_SHAPE_T:
1937  case PCB_FP_SHAPE_T:
1938  if( static_cast<PCB_SHAPE*>( item )->GetShape() == SHAPE_T::POLY )
1939  polyset = &static_cast<PCB_SHAPE*>( item )->GetPolyShape();
1940  else
1941  return false;
1942 
1943  break;
1944 
1945  default:
1946  return false;
1947  }
1948 
1949  auto vertex = findVertex( *polyset, *m_editedPoint );
1950 
1951  if( !vertex.first )
1952  return false;
1953 
1954  const auto& vertexIdx = vertex.second;
1955 
1956  // Check if there are enough vertices so one can be removed without
1957  // degenerating the polygon.
1958  // The first condition allows one to remove all corners from holes (when
1959  // there are only 2 vertices left, a hole is removed).
1960  if( vertexIdx.m_contour == 0 &&
1961  polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
1962  return false;
1963 
1964  // Remove corner does not work with lines
1965  if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
1966  return false;
1967 
1968  return m_editedPoint != nullptr;
1969 }
1970 
1971 
1973 {
1974  if( !m_editPoints )
1975  return 0;
1976 
1977  EDA_ITEM* item = m_editPoints->GetParent();
1978  PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1979  const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
1980 
1981  // called without an active edited polygon
1982  if( !item || !canAddCorner( *item ) )
1983  return 0;
1984 
1985  PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
1986  BOARD_COMMIT commit( frame );
1987 
1988  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T
1989  || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
1990  {
1991  unsigned int nearestIdx = 0;
1992  unsigned int nextNearestIdx = 0;
1993  unsigned int nearestDist = INT_MAX;
1994  unsigned int firstPointInContour = 0;
1995  SHAPE_POLY_SET* zoneOutline;
1996 
1997  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
1998  {
1999  ZONE* zone = static_cast<ZONE*>( item );
2000  zoneOutline = zone->Outline();
2001  zone->SetNeedRefill( true );
2002  }
2003  else
2004  zoneOutline = &( graphicItem->GetPolyShape() );
2005 
2006  commit.Modify( item );
2007 
2008  // Search the best outline segment to add a new corner
2009  // and therefore break this segment into two segments
2010 
2011  // Object to iterate through the corners of the outlines (main contour and its holes)
2012  SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0, zoneOutline->OutlineCount()-1,
2013  /* IterateHoles */ true );
2014  int curr_idx = 0;
2015 
2016  // Iterate through all the corners of the outlines and search the best segment
2017  for( ; iterator; iterator++, curr_idx++ )
2018  {
2019  int jj = curr_idx+1;
2020 
2021  if( iterator.IsEndContour() )
2022  { // We reach the last point of the current contour (main or hole)
2023  jj = firstPointInContour;
2024  firstPointInContour = curr_idx+1; // Prepare next contour analysis
2025  }
2026 
2027  SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
2028 
2029  unsigned int distance = curr_segment.Distance( cursorPos );
2030 
2031  if( distance < nearestDist )
2032  {
2033  nearestDist = distance;
2034  nearestIdx = curr_idx;
2035  nextNearestIdx = jj;
2036  }
2037  }
2038 
2039  // Find the point on the closest segment
2040  auto& sideOrigin = zoneOutline->CVertex( nearestIdx );
2041  auto& sideEnd = zoneOutline->CVertex( nextNearestIdx );
2042  SEG nearestSide( sideOrigin, sideEnd );
2043  VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
2044 
2045  // Do not add points that have the same coordinates as ones that already belong to polygon
2046  // instead, add a point in the middle of the side
2047  if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
2048  nearestPoint = ( sideOrigin + sideEnd ) / 2;
2049 
2050  zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
2051 
2052  // We re-hatch the filled zones but not polygons
2053  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2054  static_cast<ZONE*>( item )->HatchBorder();
2055 
2056 
2057  commit.Push( _( "Add a zone corner" ) );
2058  }
2059  else if( graphicItem && graphicItem->GetShape() == SHAPE_T::SEGMENT )
2060  {
2061  commit.Modify( graphicItem );
2062 
2063  SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
2064  VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
2065 
2066  // Move the end of the line to the break point..
2067  graphicItem->SetEnd( wxPoint( nearestPoint.x, nearestPoint.y ) );
2068 
2069  if( graphicItem->Type() == PCB_FP_SHAPE_T )
2070  static_cast<FP_SHAPE*>( graphicItem )->SetLocalCoord();
2071 
2072  // and add another one starting from the break point
2073  PCB_SHAPE* newSegment;
2074 
2075  if( item->Type() == PCB_FP_SHAPE_T )
2076  {
2077  FP_SHAPE* edge = static_cast<FP_SHAPE*>( graphicItem );
2078  assert( edge->GetParent()->Type() == PCB_FOOTPRINT_T );
2079  newSegment = new FP_SHAPE( *edge );
2080  }
2081  else
2082  {
2083  newSegment = new PCB_SHAPE( *graphicItem );
2084  }
2085 
2086  newSegment->ClearSelected();
2087  newSegment->SetStart( wxPoint( nearestPoint.x, nearestPoint.y ) );
2088  newSegment->SetEnd( wxPoint( seg.B.x, seg.B.y ) );
2089 
2090  if( newSegment->Type() == PCB_FP_SHAPE_T )
2091  static_cast<FP_SHAPE*>( newSegment )->SetLocalCoord();
2092 
2093  commit.Add( newSegment );
2094  commit.Push( _( "Split segment" ) );
2095  }
2096 
2097  updatePoints();
2098  return 0;
2099 }
2100 
2101 
2103 {
2104  if( !m_editPoints || !m_editedPoint )
2105  return 0;
2106 
2107  EDA_ITEM* item = m_editPoints->GetParent();
2108 
2109  if( !item )
2110  return 0;
2111 
2112  SHAPE_POLY_SET* polygon = nullptr;
2113 
2114  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2115  {
2116  ZONE* zone = static_cast<ZONE*>( item );
2117  polygon = zone->Outline();
2118  zone->SetNeedRefill( true );
2119  }
2120  else if( item->Type() == PCB_FP_SHAPE_T || item->Type() == PCB_SHAPE_T )
2121  {
2122  PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2123 
2124  if( shape->GetShape() == SHAPE_T::POLY )
2125  polygon = &shape->GetPolyShape();
2126  }
2127 
2128  if( !polygon )
2129  return 0;
2130 
2131  PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
2132  BOARD_COMMIT commit( frame );
2133  auto vertex = findVertex( *polygon, *m_editedPoint );
2134 
2135  if( vertex.first )
2136  {
2137  const auto& vertexIdx = vertex.second;
2138  auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
2139 
2140  if( outline.PointCount() > 3 )
2141  {
2142  // the usual case: remove just the corner when there are >3 vertices
2143  commit.Modify( item );
2144  polygon->RemoveVertex( vertexIdx );
2145  validatePolygon( *polygon );
2146  }
2147  else
2148  {
2149  // either remove a hole or the polygon when there are <= 3 corners
2150  if( vertexIdx.m_contour > 0 )
2151  {
2152  // remove hole
2153  commit.Modify( item );
2154  polygon->RemoveContour( vertexIdx.m_contour );
2155  }
2156  else
2157  {
2159  commit.Remove( item );
2160  }
2161  }
2162 
2163  setEditedPoint( nullptr );
2164 
2165  commit.Push( _( "Remove a zone/polygon corner" ) );
2166 
2167  // Refresh zone hatching
2168  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2169  static_cast<ZONE*>( item )->HatchBorder();
2170 
2171  updatePoints();
2172  }
2173 
2174  return 0;
2175 }
2176 
2177 
2179 {
2180  updatePoints();
2181  return 0;
2182 }
2183 
2184 
2186 {
2188  return 0;
2189 }
2190 
2191 
2193 {
2203 }
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:89
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:126
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:489
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:97
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:107
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:1168
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:132
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:144
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:101
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:145
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:122
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:505
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:941
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:148
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:227
An interface for classes handling user events controlling the view behavior such as zooming,...
#define _(s)
const VECTOR2D DragOrigin() const
Returns information about mouse buttons state.
Definition: tool_event.h:269
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
static LSET AllLayersMask()
Definition: lset.cpp: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:207
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:138
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:147
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:92
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:135
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:147
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:113
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.