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) 2018-2022 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
29using 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 <tools/pcb_actions.h>
39#include <board_commit.h>
40#include <pcb_edit_frame.h>
41#include <fp_shape.h>
42#include <fp_textbox.h>
43#include <pcb_bitmap.h>
44#include <pcb_dimension.h>
45#include <pcb_textbox.h>
46#include <pad.h>
47#include <zone.h>
48#include <footprint.h>
51#include <progress_reporter.h>
52
53const unsigned int PCB_POINT_EDITOR::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
54
55// Few constants to avoid using bare numbers for point indices
57{
59};
60
61
63{
65};
66
67
69{
71};
72
73
75{
77};
78
79
81{
83};
84
85
87{
92};
93
94
96{
104
105
107 PCB_TOOL_BASE( "pcbnew.PointEditor" ),
108 m_selectionTool( nullptr ),
109 m_editedPoint( nullptr ),
110 m_hoveredPoint( nullptr ),
111 m_original( VECTOR2I( 0, 0 ) ),
112 m_refill( false ),
114 m_altConstrainer( VECTOR2I( 0, 0 ) )
115{
116}
117
118
120{
121 m_refill = false;
122 m_editPoints.reset();
123 m_altConstraint.reset();
124 getViewControls()->SetAutoPan( false );
125}
126
127
129{
130 // Find the selection tool, so they can cooperate
132
133 wxASSERT_MSG( m_selectionTool, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
134
135 auto& menu = m_selectionTool->GetToolMenu().GetMenu();
138 std::bind( &PCB_POINT_EDITOR::removeCornerCondition, this, _1 ) );
139
140 return true;
141}
142
143
144void PCB_POINT_EDITOR::buildForPolyOutline( std::shared_ptr<EDIT_POINTS> points,
145 const SHAPE_POLY_SET* aOutline )
146{
147 int cornersCount = aOutline->TotalVertices();
148
149 for( auto iterator = aOutline->CIterateWithHoles(); iterator; iterator++ )
150 {
151 points->AddPoint( *iterator );
152
153 if( iterator.IsEndContour() )
154 points->AddBreak();
155 }
156
157 // Lines have to be added after creating edit points, 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
177std::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 if( aItem->Type() == PCB_TEXTBOX_T || aItem->Type() == PCB_FP_TEXTBOX_T )
185 {
186 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
187
188 // We can't currently handle TEXTBOXes that have been turned into SHAPE_T::POLYs due
189 // to non-cardinal rotations
190 if( shape->GetShape() != SHAPE_T::RECT )
191 return points;
192 }
193
194 // Generate list of edit points basing on the item type
195 switch( aItem->Type() )
196 {
197 case PCB_BITMAP_T:
198 {
199 PCB_BITMAP* bitmap = (PCB_BITMAP*) aItem;
200 VECTOR2I topLeft = bitmap->GetPosition() - bitmap->GetSize() / 2;
201 VECTOR2I botRight = bitmap->GetPosition() + bitmap->GetSize() / 2;
202
203 points->AddPoint( topLeft );
204 points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
205 points->AddPoint( botRight );
206 points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
207
208 break;
209 }
210
211 case PCB_TEXTBOX_T:
212 case PCB_FP_TEXTBOX_T:
213 case PCB_SHAPE_T:
214 case PCB_FP_SHAPE_T:
215 {
216 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
217
218 switch( shape->GetShape() )
219 {
220 case SHAPE_T::SEGMENT:
221 points->AddPoint( shape->GetStart() );
222 points->AddPoint( shape->GetEnd() );
223 break;
224
225 case SHAPE_T::RECT:
226 points->AddPoint( shape->GetTopLeft() );
227 points->AddPoint( VECTOR2I( shape->GetBotRight().x, shape->GetTopLeft().y ) );
228 points->AddPoint( shape->GetBotRight() );
229 points->AddPoint( VECTOR2I( shape->GetTopLeft().x, shape->GetBotRight().y ) );
230
231 points->AddLine( points->Point( RECT_TOP_LEFT ), points->Point( RECT_TOP_RIGHT ) );
232 points->Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( points->Line( RECT_TOP ) ) );
233 points->AddLine( points->Point( RECT_TOP_RIGHT ), points->Point( RECT_BOT_RIGHT ) );
234 points->Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_RIGHT ) ) );
235 points->AddLine( points->Point( RECT_BOT_RIGHT ), points->Point( RECT_BOT_LEFT ) );
236 points->Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_BOT ) ) );
237 points->AddLine( points->Point( RECT_BOT_LEFT ), points->Point( RECT_TOP_LEFT ) );
238 points->Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_LEFT ) ) );
239
240 break;
241
242 case SHAPE_T::ARC:
243 points->AddPoint( shape->GetCenter() );
244 points->AddPoint( shape->GetStart() );
245 points->AddPoint( shape->GetArcMid() );
246 points->AddPoint( shape->GetEnd() );
247
248 points->Point( ARC_MID ).SetGridConstraint( IGNORE_GRID );
249 points->Point( ARC_START ).SetGridConstraint( SNAP_TO_GRID );
250 points->Point( ARC_CENTER ).SetGridConstraint( SNAP_BY_GRID );
251 points->Point( ARC_END ).SetGridConstraint( SNAP_TO_GRID );
252 break;
253
254 case SHAPE_T::CIRCLE:
255 points->AddPoint( shape->GetCenter() );
256 points->AddPoint( shape->GetEnd() );
257 break;
258
259 case SHAPE_T::POLY:
260 buildForPolyOutline( points, &shape->GetPolyShape() );
261 break;
262
263 case SHAPE_T::BEZIER:
264 points->AddPoint( shape->GetStart() );
265 points->AddPoint( shape->GetBezierC1() );
266 points->AddPoint( shape->GetBezierC2() );
267 points->AddPoint( shape->GetEnd() );
268 break;
269
270 default: // suppress warnings
271 break;
272 }
273
274 break;
275 }
276
277 case PCB_PAD_T:
278 {
279 const PAD* pad = static_cast<const PAD*>( aItem );
280 VECTOR2I shapePos = pad->ShapePos();
281 VECTOR2I halfSize( pad->GetSize().x / 2, pad->GetSize().y / 2 );
282
283 if( !m_isFootprintEditor || pad->IsLocked() )
284 break;
285
286 switch( pad->GetShape() )
287 {
289 points->AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y ) );
290 break;
291
292 case PAD_SHAPE::OVAL:
294 case PAD_SHAPE::RECT:
297 {
298 if( !pad->GetOrientation().IsCardinal() )
299 break;
300
301 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
302 std::swap( halfSize.x, halfSize.y );
303
304 points->AddPoint( shapePos - halfSize );
305 points->AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
306 points->AddPoint( shapePos + halfSize );
307 points->AddPoint( VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
308 }
309 break;
310
311 default: // suppress warnings
312 break;
313 }
314 }
315 break;
316
317 case PCB_FP_ZONE_T:
318 case PCB_ZONE_T:
319 {
320 const ZONE* zone = static_cast<const ZONE*>( aItem );
321 buildForPolyOutline( points, zone->Outline() );
322 }
323 break;
324
329 {
330 const PCB_DIM_ALIGNED* dimension = static_cast<const PCB_DIM_ALIGNED*>( aItem );
331
332 points->AddPoint( dimension->GetStart() );
333 points->AddPoint( dimension->GetEnd() );
334 points->AddPoint( dimension->Text().GetPosition() );
335 points->AddPoint( dimension->GetCrossbarStart() );
336 points->AddPoint( dimension->GetCrossbarEnd() );
337
338 points->Point( DIM_START ).SetSnapConstraint( ALL_LAYERS );
339 points->Point( DIM_END ).SetSnapConstraint( ALL_LAYERS );
340
341 if( aItem->Type() == PCB_DIM_ALIGNED_T )
342 {
343 // Dimension height setting - edit points should move only along the feature lines
344 points->Point( DIM_CROSSBARSTART )
345 .SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARSTART ),
346 points->Point( DIM_START ) ) );
347 points->Point( DIM_CROSSBAREND )
348 .SetConstraint( new EC_LINE( points->Point( DIM_CROSSBAREND ),
349 points->Point( DIM_END ) ) );
350 }
351
352 break;
353 }
354
355 case PCB_DIM_CENTER_T:
357 {
358 const PCB_DIM_CENTER* dimension = static_cast<const PCB_DIM_CENTER*>( aItem );
359
360 points->AddPoint( dimension->GetStart() );
361 points->AddPoint( dimension->GetEnd() );
362
363 points->Point( DIM_START ).SetSnapConstraint( ALL_LAYERS );
364
365 points->Point( DIM_END ).SetConstraint( new EC_45DEGREE( points->Point( DIM_END ),
366 points->Point( DIM_START ) ) );
367 points->Point( DIM_END ).SetSnapConstraint( IGNORE_SNAPS );
368
369 break;
370 }
371
372 case PCB_DIM_RADIAL_T:
374 {
375 const PCB_DIM_RADIAL* dimension = static_cast<const PCB_DIM_RADIAL*>( aItem );
376
377 points->AddPoint( dimension->GetStart() );
378 points->AddPoint( dimension->GetEnd() );
379 points->AddPoint( dimension->Text().GetPosition() );
380 points->AddPoint( dimension->GetKnee() );
381
382 points->Point( DIM_START ).SetSnapConstraint( ALL_LAYERS );
383 points->Point( DIM_END ).SetSnapConstraint( ALL_LAYERS );
384
385 points->Point( DIM_KNEE ).SetConstraint( new EC_LINE( points->Point( DIM_START ),
386 points->Point( DIM_END ) ) );
387 points->Point( DIM_KNEE ).SetSnapConstraint( IGNORE_SNAPS );
388
389 points->Point( DIM_TEXT ).SetConstraint( new EC_45DEGREE( points->Point( DIM_TEXT ),
390 points->Point( DIM_KNEE ) ) );
391 points->Point( DIM_TEXT ).SetSnapConstraint( IGNORE_SNAPS );
392
393 break;
394 }
395
396 case PCB_DIM_LEADER_T:
398 {
399 const PCB_DIM_LEADER* dimension = static_cast<const PCB_DIM_LEADER*>( aItem );
400
401 points->AddPoint( dimension->GetStart() );
402 points->AddPoint( dimension->GetEnd() );
403 points->AddPoint( dimension->Text().GetPosition() );
404
405 points->Point( DIM_START ).SetSnapConstraint( ALL_LAYERS );
406 points->Point( DIM_END ).SetSnapConstraint( ALL_LAYERS );
407
408 points->Point( DIM_TEXT ).SetConstraint( new EC_45DEGREE( points->Point( DIM_TEXT ),
409 points->Point( DIM_END ) ) );
410 points->Point( DIM_TEXT ).SetSnapConstraint( IGNORE_SNAPS );
411
412 break;
413 }
414
415 default:
416 points.reset();
417 break;
418 }
419
420 return points;
421}
422
423
425{
426 EDIT_POINT* point;
427 EDIT_POINT* hovered = nullptr;
428
429 if( aEvent.IsMotion() )
430 {
431 point = m_editPoints->FindPoint( aEvent.Position(), getView() );
432 hovered = point;
433 }
434 else if( aEvent.IsDrag( BUT_LEFT ) )
435 {
436 point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
437 }
438 else
439 {
440 point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
441 }
442
443 if( hovered )
444 {
445 if( m_hoveredPoint != hovered )
446 {
447 if( m_hoveredPoint )
448 m_hoveredPoint->SetHover( false );
449
450 m_hoveredPoint = hovered;
452 }
453 }
454 else if( m_hoveredPoint )
455 {
456 m_hoveredPoint->SetHover( false );
457 m_hoveredPoint = nullptr;
458 }
459
460 if( m_editedPoint != point )
461 setEditedPoint( point );
462}
463
464
466{
468 return 0;
469
470 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
472
473 if( selection.Size() != 1 || selection.Front()->GetEditFlags() )
474 return 0;
475
476 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
477
478 if( !item || item->IsLocked() )
479 return 0;
480
481 Activate();
482 // Must be done after Activate() so that it gets set into the correct context
483 getViewControls()->ShowCursor( true );
484
486 m_editPoints = makePoints( item );
487
488 if( !m_editPoints )
489 return 0;
490
491 getView()->Add( m_editPoints.get() );
492 setEditedPoint( nullptr );
493 updateEditedPoint( aEvent );
494 m_refill = false;
495 bool inDrag = false;
496 bool useAltContraint = false;
497
498 BOARD_COMMIT commit( editFrame );
499
500 // Main loop: keep receiving events
501 while( TOOL_EVENT* evt = Wait() )
502 {
503 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
504 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
505
506 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
507 {
508 useAltContraint = editFrame->GetPcbNewSettings()->m_Use45DegreeLimit;
510 }
511 else
512 {
513 useAltContraint = editFrame->GetFootprintEditorSettings()->m_Use45Limit;
515 }
516
517 if( !m_editPoints || evt->IsSelectionEvent() ||
518 evt->Matches( EVENTS::InhibitSelectionEditing ) )
519 {
520 break;
521 }
522
523 EDIT_POINT* prevHover = m_hoveredPoint;
524
525 if( !inDrag )
526 updateEditedPoint( *evt );
527
528 if( prevHover != m_hoveredPoint )
529 getView()->Update( m_editPoints.get() );
530
531 if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
532 {
533 if( !inDrag )
534 {
535 frame()->UndoRedoBlock( true );
536
538
540 m_original = *m_editedPoint; // Save the original position
541 getViewControls()->SetAutoPan( true );
542 inDrag = true;
543
545 grid.SetAuxAxes( true, m_original.GetPosition() );
546
547 setAltConstraint( true );
549
550 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
551 {
552 EDIT_POINT& point = m_editPoints->Point( ii );
553
554 if( &point != m_editedPoint )
555 point.SetActive( false );
556 }
557 }
558
559 // Keep point inside of limits with some padding
560 VECTOR2I pos = GetClampedCoords<double, int>( evt->Position(), COORDS_PADDING );
561 LSET snapLayers;
562
564 {
565 case IGNORE_SNAPS: break;
566 case OBJECT_LAYERS: snapLayers = item->GetLayerSet(); break;
567 case ALL_LAYERS: snapLayers = LSET::AllLayersMask(); break;
568 }
569
571 {
572 if( grid.GetUseGrid() )
573 {
574 VECTOR2I gridPt = grid.BestSnapAnchor( pos, {}, { item } );
575
577 VECTOR2I delta = pos - last;
578 VECTOR2I deltaGrid = gridPt - grid.BestSnapAnchor( last, {}, { item } );
579
580 if( abs( delta.x ) > grid.GetGrid().x / 2 )
581 pos.x = last.x + deltaGrid.x;
582 else
583 pos.x = last.x;
584
585 if( abs( delta.y ) > grid.GetGrid().y / 2 )
586 pos.y = last.y + deltaGrid.y;
587 else
588 pos.y = last.y;
589 }
590 }
591
593
594 // The alternative constraint limits to 45 degrees
595 if( useAltContraint )
596 {
597 m_altConstraint->Apply( grid );
598 }
599 else if( m_editedPoint->IsConstrained() )
600 {
602 }
604 {
606 snapLayers, { item } ) );
607 }
608
609 updateItem();
611 updatePoints();
612 }
613 else if( m_editedPoint && evt->Action() == TA_MOUSE_DOWN && evt->Buttons() == BUT_LEFT )
614 {
616
617 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
618 {
619 EDIT_POINT& point = m_editPoints->Point( ii );
620
621 if( &point != m_editedPoint )
622 point.SetActive( false );
623 }
624
625 getView()->Update( m_editPoints.get() );
626 }
627 else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
628 {
629 if( m_editedPoint )
630 {
631 m_editedPoint->SetActive( false );
632 getView()->Update( m_editPoints.get() );
633 }
634
635 getViewControls()->SetAutoPan( false );
636 setAltConstraint( false );
637
638 commit.Push( _( "Drag a corner" ) );
639 inDrag = false;
640 frame()->UndoRedoBlock( false );
641
642 m_refill = true;
643 }
644 else if( evt->IsCancelInteractive() || evt->IsActivate() )
645 {
646 if( inDrag ) // Restore the last change
647 {
648 commit.Revert();
649 inDrag = false;
650 frame()->UndoRedoBlock( false );
651 }
652
653 // Only cancel point editor when activating a new tool
654 // Otherwise, allow the points to persist when moving up the
655 // tool stack
656 if( evt->IsActivate() && !evt->IsMoveTool() )
657 break;
658 }
659 else if( evt->Action() == TA_UNDO_REDO_POST )
660 {
661 break;
662 }
663 else
664 {
665 evt->SetPassEvent();
666 }
667 }
668
669 if( m_editPoints )
670 {
671 getView()->Remove( m_editPoints.get() );
672 m_editPoints.reset();
673 }
674
675 m_editedPoint = nullptr;
676
678
679 return 0;
680}
681
683 const VECTOR2I& aStart, const VECTOR2I& aMid,
684 const VECTOR2I& aEnd,
685 const VECTOR2I& aCursor ) const
686{
687 VECTOR2I start = aStart;
688 VECTOR2I end = aEnd;
689 VECTOR2I center = aCenter;
690 bool movingStart;
691 bool arcValid = true;
692
693 VECTOR2I p1, p2, p3;
694 // p1 does not move, p2 does.
695
696 if( aStart != aArc->GetStart() )
697 {
698 start = aCursor;
699 p1 = aEnd;
700 p2 = aStart;
701 p3 = aMid;
702 movingStart = true;
703 }
704 else if( aEnd != aArc->GetEnd() )
705 {
706 end = aCursor;
707 p1 = aStart;
708 p2 = aEnd;
709 p3 = aMid;
710 movingStart = false;
711 }
712 else
713 {
714 return;
715 }
716
717 VECTOR2D v1, v2, v3, v4;
718
719 // Move the coordinate system
720 v1 = p1 - aCenter;
721 v2 = p2 - aCenter;
722 v3 = p3 - aCenter;
723
724 VECTOR2D u1, u2, u3;
725
726 // A point cannot be both the center and on the arc.
727 if( ( v1.EuclideanNorm() == 0 ) || ( v2.EuclideanNorm() == 0 ) )
728 return;
729
730 u1 = v1 / v1.EuclideanNorm();
731 u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
732 u2 = u2 / u2.EuclideanNorm();
733
734 // [ u1, u3 ] is a base centered on the circle with:
735 // u1 : unit vector toward the point that does not move
736 // u2 : unit vector toward the mid point.
737
738 // Get vectors v1, and v2 in that coordinate system.
739
740 double det = u1.x * u2.y - u2.x * u1.y;
741
742 // u1 and u2 are unit vectors, and perpendicular.
743 // det should not be 0. In case it is, do not change the arc.
744 if( det == 0 )
745 return;
746
747 double tmpx = v1.x * u2.y - v1.y * u2.x;
748 double tmpy = -v1.x * u1.y + v1.y * u1.x;
749 v1.x = tmpx;
750 v1.y = tmpy;
751 v1 = v1 / det;
752
753 tmpx = v2.x * u2.y - v2.y * u2.x;
754 tmpy = -v2.x * u1.y + v2.y * u1.x;
755 v2.x = tmpx;
756 v2.y = tmpy;
757 v2 = v2 / det;
758
759 double R = v1.EuclideanNorm();
760 bool transformCircle = false;
761
762 /* p2
763 * X***
764 * ** <---- This is the arc
765 * y ^ **
766 * | R *
767 * | <-----------> *
768 * x------x------>--------x p1
769 * C' <----> C x
770 * delta
771 *
772 * p1 does not move, and the tangent at p1 remains the same.
773 * => The new center, C', will be on the C-p1 axis.
774 * p2 moves
775 *
776 * The radius of the new circle is delta + R
777 *
778 * || C' p2 || = || C' P1 ||
779 * is the same as :
780 * ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
781 *
782 * delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
783 *
784 * We can use this equation for any point p2 with p2.x < R
785 */
786
787 if( v2.x == R )
788 {
789 // Straight line, do nothing
790 }
791 else
792 {
793 if( v2.x > R )
794 {
795 // If we need to invert the curvature.
796 // We modify the input so we can use the same equation
797 transformCircle = true;
798 v2.x = 2 * R - v2.x;
799 }
800
801 // We can keep the tangent constraint.
802 double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
803
804 // This is just to limit the radius, so nothing overflows later when drawing.
806 arcValid = false;
807
808 // Never recorded a problem, but still checking.
809 if( !std::isfinite( delta ) )
810 arcValid = false;
811
812 // v4 is the new center
813 v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
814
815 tmpx = v4.x * u1.x + v4.y * u2.x;
816 tmpy = v4.x * u1.y + v4.y * u2.y;
817 v4.x = tmpx;
818 v4.y = tmpy;
819
820 center = v4 + aCenter;
821
822 if( arcValid )
823 {
824 aArc->SetCenter( center );
825
826 if( movingStart )
827 aArc->SetStart( start );
828 else
829 aArc->SetEnd( end );
830 }
831 }
832}
833
834
846 VECTOR2I& aBotLeft, VECTOR2I& aBotRight,
847 const VECTOR2I& aHole, const VECTOR2I& aHoleSize ) const
848{
849 int minWidth = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
850 int minHeight = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
851
852 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) ) )
853 {
854 if( aHoleSize.x )
855 {
856 // pin edited point to the top/left of the hole
857 aTopLeft.x = std::min( aTopLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
858 aTopLeft.y = std::min( aTopLeft.y, aHole.y - aHoleSize.y / 2 - minHeight );
859 }
860 else
861 {
862 // pin edited point within opposite corner
863 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
864 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
865 }
866
867 // push edited point edges to adjacent corners
868 aTopRight.y = aTopLeft.y;
869 aBotLeft.x = aTopLeft.x;
870 }
871 else if( isModified( m_editPoints->Point( RECT_TOP_RIGHT ) ) )
872 {
873 if( aHoleSize.x )
874 {
875 // pin edited point to the top/right of the hole
876 aTopRight.x = std::max( aTopRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
877 aTopRight.y = std::min( aTopRight.y, aHole.y - aHoleSize.y / 2 - minHeight );
878 }
879 else
880 {
881 // pin edited point within opposite corner
882 aTopRight.x = std::max( aTopRight.x, aBotLeft.x + minWidth );
883 aTopRight.y = std::min( aTopRight.y, aBotLeft.y - minHeight );
884 }
885
886 // push edited point edges to adjacent corners
887 aTopLeft.y = aTopRight.y;
888 aBotRight.x = aTopRight.x;
889 }
890 else if( isModified( m_editPoints->Point( RECT_BOT_LEFT ) ) )
891 {
892 if( aHoleSize.x )
893 {
894 // pin edited point to the bottom/left of the hole
895 aBotLeft.x = std::min( aBotLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
896 aBotLeft.y = std::max( aBotLeft.y, aHole.y + aHoleSize.y / 2 + minHeight );
897 }
898 else
899 {
900 // pin edited point within opposite corner
901 aBotLeft.x = std::min( aBotLeft.x, aTopRight.x - minWidth );
902 aBotLeft.y = std::max( aBotLeft.y, aTopRight.y + minHeight );
903 }
904
905 // push edited point edges to adjacent corners
906 aBotRight.y = aBotLeft.y;
907 aTopLeft.x = aBotLeft.x;
908 }
909 else if( isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
910 {
911 if( aHoleSize.x )
912 {
913 // pin edited point to the bottom/right of the hole
914 aBotRight.x = std::max( aBotRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
915 aBotRight.y = std::max( aBotRight.y, aHole.y + aHoleSize.y / 2 + minHeight );
916 }
917 else
918 {
919 // pin edited point within opposite corner
920 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
921 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
922 }
923
924 // push edited point edges to adjacent corners
925 aBotLeft.y = aBotRight.y;
926 aTopRight.x = aBotRight.x;
927 }
928 else if( isModified( m_editPoints->Line( RECT_TOP ) ) )
929 {
930 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
931 }
932 else if( isModified( m_editPoints->Line( RECT_LEFT ) ) )
933 {
934 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
935 }
936 else if( isModified( m_editPoints->Line( RECT_BOT ) ) )
937 {
938 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
939 }
940 else if( isModified( m_editPoints->Line( RECT_RIGHT ) ) )
941 {
942 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
943 }
944}
945
946
948 const VECTOR2I& aStart, const VECTOR2I& aMid,
949 const VECTOR2I& aEnd,
950 const VECTOR2I& aCursor ) const
951{
952 int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
953 bool movingStart;
954
955 VECTOR2I p1, p2, prev_p1;
956
957 // user is moving p1, we want to move p2 to the new radius.
958
959 if( aStart != aArc->GetStart() )
960 {
961 prev_p1 = aArc->GetStart();
962 p1 = aStart;
963 p2 = aEnd;
964 movingStart = true;
965 }
966 else
967 {
968 prev_p1 = aArc->GetEnd();
969 p1 = aEnd;
970 p2 = aStart;
971 movingStart = false;
972 }
973
974 p1 = p1 - aCenter;
975 p2 = p2 - aCenter;
976
977 if( p1.x == 0 && p1.y == 0 )
978 p1 = prev_p1 - aCenter;
979
980 if( p2.x == 0 && p2.y == 0 )
981 p2 = { 1, 0 };
982
983 double radius = p1.EuclideanNorm();
984
985 if( radius < minRadius )
986 radius = minRadius;
987
988 p1 = aCenter + p1.Resize( radius );
989 p2 = aCenter + p2.Resize( radius );
990
991 aArc->SetCenter( aCenter );
992
993 if( movingStart )
994 {
995 aArc->SetStart( p1 );
996 aArc->SetEnd( p2 );
997 }
998 else
999 {
1000 aArc->SetStart( p2 );
1001 aArc->SetEnd( p1 );
1002 }
1003}
1004
1005
1007 const VECTOR2I& aStart, const VECTOR2I& aMid,
1008 const VECTOR2I& aEnd, const VECTOR2I& aCursor ) const
1009{
1010 int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
1011
1012 SEG chord( aStart, aEnd );
1013
1014 // Now, update the edit point position
1015 // Express the point in a circle-centered coordinate system.
1016 VECTOR2I start = aStart - aCenter;
1017 VECTOR2I end = aEnd - aCenter;
1018
1019 double radius = ( aCursor - aCenter ).EuclideanNorm();
1020
1021 if( radius < minRadius )
1022 radius = minRadius;
1023
1024 start = start.Resize( radius );
1025 end = end.Resize( radius );
1026
1027 start = start + aCenter;
1028 end = end + aCenter;
1029
1030 aArc->SetStart( start );
1031 aArc->SetEnd( end );
1032}
1033
1034
1036 const VECTOR2I& aEnd,
1037 const VECTOR2I& aCursor ) const
1038{
1039 // Let 'm' be the middle point of the chord between the start and end points
1040 VECTOR2I m = ( aStart + aEnd ) / 2;
1041
1042 // Legal midpoints lie on a vector starting just off the chord midpoint and extending out
1043 // past the existing midpoint. We do not allow arc inflection while point editing.
1044 const int JUST_OFF = ( aStart - aEnd ).EuclideanNorm() / 100;
1045 VECTOR2I v = (VECTOR2I) aArc->GetArcMid() - m;
1046 SEG legal( m + v.Resize( JUST_OFF ), m + v.Resize( INT_MAX / 2 ) );
1047 VECTOR2I mid = legal.NearestPoint( aCursor );
1048
1049 aArc->SetArcGeometry( aStart, mid, aEnd );
1050}
1051
1052
1054{
1055 EDA_ITEM* item = m_editPoints->GetParent();
1056
1057 if( !item )
1058 return;
1059
1060 switch( item->Type() )
1061 {
1062 case PCB_BITMAP_T:
1063 {
1064 PCB_BITMAP* bitmap = (PCB_BITMAP*) item;
1065 VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
1066 VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
1067 VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
1068 VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
1069
1070 pinEditedCorner( topLeft, topRight, botLeft, botRight );
1071
1072 double oldWidth = bitmap->GetSize().x;
1073 double newWidth = std::max( topRight.x - topLeft.x, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
1074 double widthRatio = newWidth / oldWidth;
1075
1076 double oldHeight = bitmap->GetSize().y;
1077 double newHeight =
1078 std::max( botLeft.y - topLeft.y, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
1079 double heightRatio = newHeight / oldHeight;
1080
1081 bitmap->SetImageScale( bitmap->GetImageScale() * std::min( widthRatio, heightRatio ) );
1082
1083 break;
1084 }
1085
1086 case PCB_TEXTBOX_T:
1087 case PCB_FP_TEXTBOX_T:
1088 case PCB_SHAPE_T:
1089 case PCB_FP_SHAPE_T:
1090 {
1091 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1092
1093 switch( shape->GetShape() )
1094 {
1095 case SHAPE_T::SEGMENT:
1096 if( isModified( m_editPoints->Point( SEG_START ) ) )
1097 shape->SetStart( m_editPoints->Point( SEG_START ).GetPosition() );
1098 else if( isModified( m_editPoints->Point( SEG_END ) ) )
1099 shape->SetEnd( m_editPoints->Point( SEG_END ).GetPosition() );
1100
1101 break;
1102
1103 case SHAPE_T::RECT:
1104 {
1105 VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
1106 VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
1107 VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
1108 VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
1109
1110 pinEditedCorner( topLeft, topRight, botLeft, botRight );
1111
1112 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1113 || isModified( m_editPoints->Point( RECT_TOP_RIGHT ) )
1114 || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) )
1115 || isModified( m_editPoints->Point( RECT_BOT_LEFT ) ) )
1116 {
1117 shape->SetLeft( topLeft.x );
1118 shape->SetTop( topLeft.y );
1119 shape->SetRight( botRight.x );
1120 shape->SetBottom( botRight.y );
1121 }
1122 else if( isModified( m_editPoints->Line( RECT_TOP ) ) )
1123 {
1124 shape->SetTop( topLeft.y );
1125 }
1126 else if( isModified( m_editPoints->Line( RECT_LEFT ) ) )
1127 {
1128 shape->SetLeft( topLeft.x );
1129 }
1130 else if( isModified( m_editPoints->Line( RECT_BOT ) ) )
1131 {
1132 shape->SetBottom( botRight.y );
1133 }
1134 else if( isModified( m_editPoints->Line( RECT_RIGHT ) ) )
1135 {
1136 shape->SetRight( botRight.x );
1137 }
1138
1139 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1140 {
1141 if( !isModified( m_editPoints->Line( i ) ) )
1142 {
1143 m_editPoints->Line( i ).SetConstraint(
1144 new EC_PERPLINE( m_editPoints->Line( i ) ) );
1145 }
1146 }
1147
1148 break;
1149 }
1150
1151 case SHAPE_T::ARC:
1152 {
1153 VECTOR2I center = m_editPoints->Point( ARC_CENTER ).GetPosition();
1154 VECTOR2I mid = m_editPoints->Point( ARC_MID ).GetPosition();
1155 VECTOR2I start = m_editPoints->Point( ARC_START ).GetPosition();
1156 VECTOR2I end = m_editPoints->Point( ARC_END ).GetPosition();
1157
1158 if( isModified( m_editPoints->Point( ARC_CENTER ) ) )
1159 {
1160 VECTOR2I moveVector = VECTOR2I( center.x, center.y ) - shape->GetCenter();
1161 shape->Move( moveVector );
1162 }
1163 else if( isModified( m_editPoints->Point( ARC_MID ) ) )
1164 {
1165 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition( false );
1166
1168 editArcMidKeepEndpoints( shape, start, end, cursorPos );
1169 else
1170 editArcMidKeepCenter( shape, center, start, mid, end, cursorPos );
1171 }
1172 else if( isModified( m_editPoints->Point( ARC_START ) )
1173 || isModified( m_editPoints->Point( ARC_END ) ) )
1174 {
1175 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
1176
1178 editArcEndpointKeepTangent( shape, center, start, mid, end, cursorPos );
1179 else
1180 editArcEndpointKeepCenter( shape, center, start, mid, end, cursorPos );
1181 }
1182
1183 break;
1184 }
1185
1186 case SHAPE_T::CIRCLE:
1187 {
1188 const VECTOR2I& center = m_editPoints->Point( CIRC_CENTER ).GetPosition();
1189 const VECTOR2I& end = m_editPoints->Point( CIRC_END ).GetPosition();
1190
1191 if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
1192 {
1193 VECTOR2I moveVector = VECTOR2I( center.x, center.y ) - shape->GetCenter();
1194 shape->Move( moveVector );
1195 }
1196 else
1197 {
1198 shape->SetEnd( VECTOR2I( end.x, end.y ) );
1199 }
1200
1201 break;
1202 }
1203
1204 case SHAPE_T::POLY:
1205 {
1206 SHAPE_POLY_SET& outline = shape->GetPolyShape();
1207
1208 for( int i = 0; i < outline.TotalVertices(); ++i )
1209 outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
1210
1211 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1212 {
1213 if( !isModified( m_editPoints->Line( i ) ) )
1214 m_editPoints->Line( i ).SetConstraint(
1215 new EC_PERPLINE( m_editPoints->Line( i ) ) );
1216 }
1217
1218 validatePolygon( outline );
1219 break;
1220 }
1221
1222 case SHAPE_T::BEZIER:
1223 if( isModified( m_editPoints->Point( BEZIER_CURVE_START ) ) )
1224 shape->SetStart( m_editPoints->Point( BEZIER_CURVE_START ).GetPosition() );
1226 shape->SetBezierC1( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).GetPosition() );
1228 shape->SetBezierC2( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).GetPosition() );
1229 else if( isModified( m_editPoints->Point( BEZIER_CURVE_END ) ) )
1230 shape->SetEnd( m_editPoints->Point( BEZIER_CURVE_END ).GetPosition() );
1231
1233 break;
1234
1235 default: // suppress warnings
1236 break;
1237 }
1238
1239 if( FP_SHAPE* fpShape = dynamic_cast<FP_SHAPE*>( item ) )
1240 {
1241 // Update relative coordinates for footprint shapes
1242 fpShape->SetLocalCoord();
1243
1244 if( fpShape->IsAnnotationProxy() )
1245 {
1246 for( PAD* pad : fpShape->GetParentFootprint()->Pads() )
1247 {
1248 if( pad->GetFlags() & ENTERED )
1249 view()->Update( pad );
1250 }
1251 }
1252 }
1253
1254 // Nuke outline font render caches
1255 if( PCB_TEXTBOX* textBox = dynamic_cast<PCB_TEXTBOX*>( item ) )
1256 textBox->ClearRenderCache();
1257 else if( FP_TEXTBOX* fpTextBox = dynamic_cast<FP_TEXTBOX*>( item ) )
1258 fpTextBox->ClearRenderCache();
1259
1260 break;
1261 }
1262
1263 case PCB_PAD_T:
1264 {
1265 PAD* pad = static_cast<PAD*>( item );
1266
1267 switch( pad->GetShape() )
1268 {
1269 case PAD_SHAPE::CIRCLE:
1270 {
1271 VECTOR2I end = m_editPoints->Point( 0 ).GetPosition();
1272 int diameter = (int) EuclideanNorm( end - pad->GetPosition() ) * 2;
1273
1274 pad->SetSize( wxSize( diameter, diameter ) );
1275 break;
1276 }
1277
1278 case PAD_SHAPE::OVAL:
1280 case PAD_SHAPE::RECT:
1283 {
1284 VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
1285 VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
1286 VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
1287 VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
1288 VECTOR2I holeCenter = pad->GetPosition();
1289 VECTOR2I holeSize = pad->GetDrillSize();
1290
1291 pinEditedCorner( topLeft, topRight, botLeft, botRight, holeCenter, holeSize );
1292
1293 if( ( pad->GetOffset().x || pad->GetOffset().y )
1294 || ( pad->GetDrillSize().x && pad->GetDrillSize().y ) )
1295 {
1296 // Keep hole pinned at the current location; adjust the pad around the hole
1297
1298 VECTOR2I center = pad->GetPosition();
1299 int dist[4];
1300
1301 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1302 || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1303 {
1304 dist[0] = center.x - topLeft.x;
1305 dist[1] = center.y - topLeft.y;
1306 dist[2] = botRight.x - center.x;
1307 dist[3] = botRight.y - center.y;
1308 }
1309 else
1310 {
1311 dist[0] = center.x - botLeft.x;
1312 dist[1] = center.y - topRight.y;
1313 dist[2] = topRight.x - center.x;
1314 dist[3] = botLeft.y - center.y;
1315 }
1316
1317 wxSize padSize( dist[0] + dist[2], dist[1] + dist[3] );
1318 VECTOR2I deltaOffset( padSize.x / 2 - dist[2], padSize.y / 2 - dist[3] );
1319
1320 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
1321 std::swap( padSize.x, padSize.y );
1322
1323 RotatePoint( deltaOffset, -pad->GetOrientation() );
1324
1325 pad->SetSize( padSize );
1326 pad->SetOffset( -deltaOffset );
1327 }
1328 else
1329 {
1330 // Keep pad position at the center of the pad shape
1331
1332 int left, top, right, bottom;
1333
1334 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1335 || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1336 {
1337 left = topLeft.x;
1338 top = topLeft.y;
1339 right = botRight.x;
1340 bottom = botRight.y;
1341 }
1342 else
1343 {
1344 left = botLeft.x;
1345 top = topRight.y;
1346 right = topRight.x;
1347 bottom = botLeft.y;
1348 }
1349
1350 wxSize padSize( abs( right - left ), abs( bottom - top ) );
1351
1352 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
1353 std::swap( padSize.x, padSize.y );
1354
1355 pad->SetSize( padSize );
1356 pad->SetPosition( VECTOR2I( ( left + right ) / 2, ( top + bottom ) / 2 ) );
1357 pad->SetLocalCoord();
1358 }
1359 break;
1360 }
1361
1362 default: // suppress warnings
1363 break;
1364 }
1365
1366 break;
1367 }
1368
1369 case PCB_FP_ZONE_T:
1370 case PCB_ZONE_T:
1371 {
1372 ZONE* zone = static_cast<ZONE*>( item );
1373 zone->UnFill();
1374 SHAPE_POLY_SET& outline = *zone->Outline();
1375
1376 for( int i = 0; i < outline.TotalVertices(); ++i )
1377 {
1378 if( outline.CVertex( i ) != m_editPoints->Point( i ).GetPosition() )
1379 zone->SetNeedRefill( true );
1380
1381 outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
1382 }
1383
1384 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1385 {
1386 if( !isModified( m_editPoints->Line( i ) ) )
1387 m_editPoints->Line( i ).SetConstraint( new EC_PERPLINE( m_editPoints->Line( i ) ) );
1388 }
1389
1390 validatePolygon( outline );
1391 zone->HatchBorder();
1392
1393 // TODO Refill zone when KiCad supports auto re-fill
1394 break;
1395 }
1396
1397 case PCB_DIM_ALIGNED_T:
1399 {
1400 PCB_DIM_ALIGNED* dimension = static_cast<PCB_DIM_ALIGNED*>( item );
1401
1402 // Check which point is currently modified and updated dimension's points respectively
1403 if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) )
1404 {
1405 VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetStart() );
1406 VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1407
1408 if( featureLine.Cross( crossBar ) > 0 )
1409 dimension->SetHeight( -featureLine.EuclideanNorm() );
1410 else
1411 dimension->SetHeight( featureLine.EuclideanNorm() );
1412
1413 dimension->Update();
1414 }
1415 else if( isModified( m_editPoints->Point( DIM_CROSSBAREND ) ) )
1416 {
1417 VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
1418 VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1419
1420 if( featureLine.Cross( crossBar ) > 0 )
1421 dimension->SetHeight( -featureLine.EuclideanNorm() );
1422 else
1423 dimension->SetHeight( featureLine.EuclideanNorm() );
1424
1425 dimension->Update();
1426 }
1427 else if( isModified( m_editPoints->Point( DIM_START ) ) )
1428 {
1429 dimension->SetStart( m_editedPoint->GetPosition() );
1430 dimension->Update();
1431
1433 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1434 m_editPoints->Point( DIM_START ) ) );
1435 m_editPoints->Point( DIM_CROSSBAREND ).
1436 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1437 m_editPoints->Point( DIM_END ) ) );
1438 }
1439 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1440 {
1441 dimension->SetEnd( m_editedPoint->GetPosition() );
1442 dimension->Update();
1443
1445 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1446 m_editPoints->Point( DIM_START ) ) );
1447 m_editPoints->Point( DIM_CROSSBAREND ).
1448 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1449 m_editPoints->Point( DIM_END ) ) );
1450 }
1451 else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1452 {
1453 // Force manual mode if we weren't already in it
1455 dimension->Text().SetPosition( m_editedPoint->GetPosition() );
1456 dimension->Update();
1457 }
1458
1459 break;
1460 }
1461
1464 {
1465 PCB_DIM_ORTHOGONAL* dimension = static_cast<PCB_DIM_ORTHOGONAL*>( item );
1466
1467 if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) ||
1469 {
1470 BOX2I bounds( dimension->GetStart(), dimension->GetEnd() - dimension->GetStart() );
1471
1472 const VECTOR2I& cursorPos = m_editedPoint->GetPosition();
1473
1474 // Find vector from nearest dimension point to edit position
1475 VECTOR2I directionA( cursorPos - dimension->GetStart() );
1476 VECTOR2I directionB( cursorPos - dimension->GetEnd() );
1477 VECTOR2I direction = ( directionA < directionB ) ? directionA : directionB;
1478
1479 bool vert;
1480 VECTOR2D featureLine( cursorPos - dimension->GetStart() );
1481
1482 // Only change the orientation when we move outside the bounds
1483 if( !bounds.Contains( cursorPos ) )
1484 {
1485 // If the dimension is horizontal or vertical, set correct orientation
1486 // otherwise, test if we're left/right of the bounding box or above/below it
1487 if( bounds.GetWidth() == 0 )
1488 vert = true;
1489 else if( bounds.GetHeight() == 0 )
1490 vert = false;
1491 else if( cursorPos.x > bounds.GetLeft() && cursorPos.x < bounds.GetRight() )
1492 vert = false;
1493 else if( cursorPos.y > bounds.GetTop() && cursorPos.y < bounds.GetBottom() )
1494 vert = true;
1495 else
1496 vert = std::abs( direction.y ) < std::abs( direction.x );
1497
1500 }
1501 else
1502 {
1504 }
1505
1506 dimension->SetHeight( vert ? featureLine.x : featureLine.y );
1507 }
1508 else if( isModified( m_editPoints->Point( DIM_START ) ) )
1509 {
1510 dimension->SetStart( m_editedPoint->GetPosition() );
1511 }
1512 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1513 {
1514 dimension->SetEnd( m_editedPoint->GetPosition() );
1515 }
1516 else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1517 {
1518 // Force manual mode if we weren't already in it
1520 dimension->Text().SetPosition( VECTOR2I( m_editedPoint->GetPosition() ) );
1521 }
1522
1523 dimension->Update();
1524
1525 break;
1526 }
1527
1528 case PCB_DIM_CENTER_T:
1530 {
1531 PCB_DIM_CENTER* dimension = static_cast<PCB_DIM_CENTER*>( item );
1532
1533 if( isModified( m_editPoints->Point( DIM_START ) ) )
1534 dimension->SetStart( m_editedPoint->GetPosition() );
1535 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1536 dimension->SetEnd( m_editedPoint->GetPosition() );
1537
1538 dimension->Update();
1539
1540 break;
1541 }
1542
1543 case PCB_DIM_RADIAL_T:
1545 {
1546 PCB_DIM_RADIAL* dimension = static_cast<PCB_DIM_RADIAL*>( item );
1547
1548 if( isModified( m_editPoints->Point( DIM_START ) ) )
1549 {
1550 dimension->SetStart( m_editedPoint->GetPosition() );
1551 dimension->Update();
1552
1553 m_editPoints->Point( DIM_KNEE ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_START ),
1554 m_editPoints->Point( DIM_END ) ) );
1555 }
1556 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1557 {
1558 VECTOR2I oldKnee = dimension->GetKnee();
1559
1560 dimension->SetEnd( m_editedPoint->GetPosition() );
1561 dimension->Update();
1562
1563 VECTOR2I kneeDelta = dimension->GetKnee() - oldKnee;
1564 dimension->Text().SetPosition( dimension->Text().GetPosition() + kneeDelta );
1565 dimension->Update();
1566
1567 m_editPoints->Point( DIM_KNEE ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_START ),
1568 m_editPoints->Point( DIM_END ) ) );
1569 }
1570 else if( isModified( m_editPoints->Point( DIM_KNEE ) ) )
1571 {
1572 VECTOR2I oldKnee = dimension->GetKnee();
1573 VECTOR2I arrowVec = m_editPoints->Point( DIM_KNEE ).GetPosition()
1574 - m_editPoints->Point( DIM_END ).GetPosition();
1575
1576 dimension->SetLeaderLength( arrowVec.EuclideanNorm() );
1577 dimension->Update();
1578
1579 VECTOR2I kneeDelta = dimension->GetKnee() - oldKnee;
1580 dimension->Text().SetPosition( dimension->Text().GetPosition() + kneeDelta );
1581 dimension->Update();
1582 }
1583 else if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
1584 {
1585 dimension->Text().SetPosition( m_editedPoint->GetPosition() );
1586 dimension->Update();
1587 }
1588
1589 break;
1590 }
1591
1592 case PCB_DIM_LEADER_T:
1594 {
1595 PCB_DIM_LEADER* dimension = static_cast<PCB_DIM_LEADER*>( item );
1596
1597 if( isModified( m_editPoints->Point( DIM_START ) ) )
1598 {
1599 dimension->SetStart( (VECTOR2I) m_editedPoint->GetPosition() );
1600 }
1601 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1602 {
1603 VECTOR2I newPoint( m_editedPoint->GetPosition() );
1604 VECTOR2I delta = newPoint - dimension->GetEnd();
1605
1606 dimension->SetEnd( newPoint );
1607 dimension->Text().SetPosition( dimension->Text().GetPosition() + delta );
1608 }
1609 else if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
1610 {
1611 dimension->Text().SetPosition( (VECTOR2I) m_editedPoint->GetPosition() );
1612 }
1613
1614 dimension->Update();
1615
1616 break;
1617 }
1618
1619 default:
1620 break;
1621 }
1622
1623 getView()->Update( item );
1624
1625 frame()->SetMsgPanel( item );
1626}
1627
1628
1630{
1631 return true;
1632}
1633
1634
1636{
1637 if( !m_editPoints )
1638 return;
1639
1640 EDA_ITEM* item = m_editPoints->GetParent();
1641
1642 if( !item )
1643 return;
1644
1645 switch( item->Type() )
1646 {
1647 case PCB_BITMAP_T:
1648 {
1649 PCB_BITMAP* bitmap = (PCB_BITMAP*) item;
1650 VECTOR2I topLeft = bitmap->GetPosition() - bitmap->GetSize() / 2;
1651 VECTOR2I botRight = bitmap->GetPosition() + bitmap->GetSize() / 2;
1652
1653 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( topLeft );
1654 m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
1655 m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
1656 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( botRight );
1657
1658 break;
1659 }
1660
1661 case PCB_TEXTBOX_T:
1662 case PCB_FP_TEXTBOX_T:
1663 {
1664 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
1665 int target = shape->GetShape() == SHAPE_T::RECT ? 4 : 0;
1666
1667 // Careful; textbox shape is mutable between cardinal and non-cardinal rotations...
1668 if( int( m_editPoints->PointsSize() ) != target )
1669 {
1670 getView()->Remove( m_editPoints.get() );
1671 m_editedPoint = nullptr;
1672
1673 m_editPoints = makePoints( item );
1674
1675 if( m_editPoints )
1676 getView()->Add( m_editPoints.get() );
1677
1678 break;
1679 }
1680
1681 if( shape->GetShape() == SHAPE_T::RECT )
1682 {
1683 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shape->GetTopLeft() );
1684 m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( shape->GetBotRight().x,
1685 shape->GetTopLeft().y );
1686 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shape->GetBotRight() );
1687 m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( shape->GetTopLeft().x,
1688 shape->GetBotRight().y );
1689 }
1690 else if( shape->GetShape() == SHAPE_T::POLY )
1691 {
1692 // Not currently editable while rotated.
1693 }
1694
1695 break;
1696 }
1697
1698 case PCB_SHAPE_T:
1699 case PCB_FP_SHAPE_T:
1700 {
1701 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
1702
1703 switch( shape->GetShape() )
1704 {
1705 case SHAPE_T::SEGMENT:
1706 m_editPoints->Point( SEG_START ).SetPosition( shape->GetStart() );
1707 m_editPoints->Point( SEG_END ).SetPosition( shape->GetEnd() );
1708 break;
1709
1710 case SHAPE_T::RECT:
1711 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shape->GetTopLeft() );
1712 m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( shape->GetBotRight().x,
1713 shape->GetTopLeft().y );
1714 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shape->GetBotRight() );
1715 m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( shape->GetTopLeft().x,
1716 shape->GetBotRight().y );
1717 break;
1718
1719 case SHAPE_T::ARC:
1720 m_editPoints->Point( ARC_CENTER ).SetPosition( shape->GetCenter() );
1721 m_editPoints->Point( ARC_START ).SetPosition( shape->GetStart() );
1722 m_editPoints->Point( ARC_MID ).SetPosition( shape->GetArcMid() );
1723 m_editPoints->Point( ARC_END ).SetPosition( shape->GetEnd() );
1724 break;
1725
1726 case SHAPE_T::CIRCLE:
1727 m_editPoints->Point( CIRC_CENTER ).SetPosition( shape->GetCenter() );
1728 m_editPoints->Point( CIRC_END ).SetPosition( shape->GetEnd() );
1729 break;
1730
1731 case SHAPE_T::POLY:
1732 {
1733 std::vector<VECTOR2I> points;
1734 shape->DupPolyPointsList( points );
1735
1736 if( m_editPoints->PointsSize() != (unsigned) points.size() )
1737 {
1738 getView()->Remove( m_editPoints.get() );
1739 m_editedPoint = nullptr;
1740
1741 m_editPoints = makePoints( item );
1742
1743 if( m_editPoints )
1744 getView()->Add( m_editPoints.get() );
1745 }
1746 else
1747 {
1748 for( unsigned i = 0; i < points.size(); i++ )
1749 m_editPoints->Point( i ).SetPosition( points[i] );
1750 }
1751
1752 break;
1753 }
1754
1755 case SHAPE_T::BEZIER:
1756 m_editPoints->Point( BEZIER_CURVE_START ).SetPosition( shape->GetStart() );
1757 m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).SetPosition( shape->GetBezierC1() );
1758 m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).SetPosition( shape->GetBezierC2() );
1759 m_editPoints->Point( BEZIER_CURVE_END ).SetPosition( shape->GetEnd() );
1760 break;
1761
1762 default: // suppress warnings
1763 break;
1764 }
1765
1766 break;
1767 }
1768
1769 case PCB_PAD_T:
1770 {
1771 const PAD* pad = static_cast<const PAD*>( item );
1772 bool locked = pad->GetParent() && pad->IsLocked();
1773 VECTOR2I shapePos = pad->ShapePos();
1774 VECTOR2I halfSize( pad->GetSize().x / 2, pad->GetSize().y / 2 );
1775
1776 switch( pad->GetShape() )
1777 {
1778 case PAD_SHAPE::CIRCLE:
1779 {
1780 int target = locked ? 0 : 1;
1781
1782 // Careful; pad shape is mutable...
1783 if( int( m_editPoints->PointsSize() ) != target )
1784 {
1785 getView()->Remove( m_editPoints.get() );
1786 m_editedPoint = nullptr;
1787
1788 m_editPoints = makePoints( item );
1789
1790 if( m_editPoints )
1791 getView()->Add( m_editPoints.get() );
1792 }
1793 else if( target == 1 )
1794 {
1795 shapePos.x += halfSize.x;
1796 m_editPoints->Point( 0 ).SetPosition( shapePos );
1797 }
1798 }
1799 break;
1800
1801 case PAD_SHAPE::OVAL:
1803 case PAD_SHAPE::RECT:
1806 {
1807 // Careful; pad shape and orientation are mutable...
1808 int target = locked || !pad->GetOrientation().IsCardinal() ? 0 : 4;
1809
1810 if( int( m_editPoints->PointsSize() ) != target )
1811 {
1812 getView()->Remove( m_editPoints.get() );
1813 m_editedPoint = nullptr;
1814
1815 m_editPoints = makePoints( item );
1816
1817 if( m_editPoints )
1818 getView()->Add( m_editPoints.get() );
1819 }
1820 else if( target == 4 )
1821 {
1822 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
1823 std::swap( halfSize.x, halfSize.y );
1824
1825 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shapePos - halfSize );
1827 .SetPosition(
1828 VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
1829 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shapePos + halfSize );
1830 m_editPoints->Point( RECT_BOT_LEFT )
1831 .SetPosition(
1832 VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
1833 }
1834
1835 break;
1836 }
1837
1838 default: // suppress warnings
1839 break;
1840 }
1841 }
1842 break;
1843
1844 case PCB_FP_ZONE_T:
1845 case PCB_ZONE_T:
1846 {
1847 ZONE* zone = static_cast<ZONE*>( item );
1848 const SHAPE_POLY_SET* outline = zone->Outline();
1849
1850 if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
1851 {
1852 getView()->Remove( m_editPoints.get() );
1853 m_editedPoint = nullptr;
1854
1855 m_editPoints = makePoints( item );
1856
1857 if( m_editPoints )
1858 getView()->Add( m_editPoints.get() );
1859 }
1860 else
1861 {
1862 for( int i = 0; i < outline->TotalVertices(); ++i )
1863 m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
1864 }
1865
1866 break;
1867 }
1868
1869 case PCB_DIM_ALIGNED_T:
1873 {
1874 const PCB_DIM_ALIGNED* dimension = static_cast<const PCB_DIM_ALIGNED*>( item );
1875
1876 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1877 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1878 m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->Text().GetPosition() );
1879 m_editPoints->Point( DIM_CROSSBARSTART ).SetPosition( dimension->GetCrossbarStart() );
1880 m_editPoints->Point( DIM_CROSSBAREND ).SetPosition( dimension->GetCrossbarEnd() );
1881 break;
1882 }
1883
1884 case PCB_DIM_CENTER_T:
1886 {
1887 const PCB_DIM_CENTER* dimension = static_cast<const PCB_DIM_CENTER*>( item );
1888
1889 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1890 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1891 break;
1892 }
1893
1894 case PCB_DIM_RADIAL_T:
1896 {
1897 const PCB_DIM_RADIAL* dimension = static_cast<const PCB_DIM_RADIAL*>( item );
1898
1899 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1900 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1901 m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->Text().GetPosition() );
1902 m_editPoints->Point( DIM_KNEE ).SetPosition( dimension->GetKnee() );
1903 break;
1904 }
1905
1906 case PCB_DIM_LEADER_T:
1908 {
1909 const PCB_DIM_LEADER* dimension = static_cast<const PCB_DIM_LEADER*>( item );
1910
1911 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1912 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1913 m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->Text().GetPosition() );
1914 break;
1915 }
1916
1917 default:
1918 break;
1919 }
1920
1921 getView()->Update( m_editPoints.get() );
1922}
1923
1924
1926{
1928
1929 if( aPoint )
1930 {
1932 controls->ForceCursorPosition( true, aPoint->GetPosition() );
1933 controls->ShowCursor( true );
1934 }
1935 else
1936 {
1937 if( frame()->ToolStackIsEmpty() )
1938 controls->ShowCursor( false );
1939
1940 controls->ForceCursorPosition( false );
1941 }
1942
1943 m_editedPoint = aPoint;
1944}
1945
1946
1948{
1949 if( aEnabled )
1950 {
1951 EDA_ITEM* parent = m_editPoints->GetParent();
1952 EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
1953 bool isPoly;
1954
1955 switch( parent->Type() )
1956 {
1957 case PCB_ZONE_T:
1958 case PCB_FP_ZONE_T:
1959 isPoly = true;
1960 break;
1961
1962 case PCB_SHAPE_T:
1963 case PCB_FP_SHAPE_T:
1964 isPoly = static_cast<PCB_SHAPE*>( parent )->GetShape() == SHAPE_T::POLY;
1965 break;
1966
1967 default:
1968 isPoly = false;
1969 break;
1970 }
1971
1972 if( line && isPoly )
1973 {
1974 EC_CONVERGING* altConstraint = new EC_CONVERGING( *line, *m_editPoints );
1975 m_altConstraint.reset( (EDIT_CONSTRAINT<EDIT_POINT>*) altConstraint );
1976 }
1977 else
1978 {
1979 // Find a proper constraining point for 45 degrees mode
1982 }
1983 }
1984 else
1985 {
1986 m_altConstraint.reset();
1987 }
1988}
1989
1990
1992{
1993 EDA_ITEM* item = m_editPoints->GetParent();
1994
1995 switch( item->Type() )
1996 {
1997 case PCB_SHAPE_T:
1998 case PCB_FP_SHAPE_T:
1999 switch( static_cast<const PCB_SHAPE*>( item )->GetShape() )
2000 {
2001 case SHAPE_T::SEGMENT:
2002 return *( m_editPoints->Next( *m_editedPoint ) ); // select the other end of line
2003
2004 case SHAPE_T::ARC:
2005 case SHAPE_T::CIRCLE:
2006 return m_editPoints->Point( CIRC_CENTER );
2007
2008 default: // suppress warnings
2009 break;
2010 }
2011
2012 break;
2013
2014 case PCB_DIM_ALIGNED_T:
2016 {
2017 // Constraint for crossbar
2018 if( isModified( m_editPoints->Point( DIM_START ) ) )
2019 return m_editPoints->Point( DIM_END );
2020
2021 else if( isModified( m_editPoints->Point( DIM_END ) ) )
2022 return m_editPoints->Point( DIM_START );
2023
2024 else
2025 return EDIT_POINT( m_editedPoint->GetPosition() ); // no constraint
2026
2027 break;
2028 }
2029
2030 case PCB_DIM_CENTER_T:
2032 {
2033 if( isModified( m_editPoints->Point( DIM_END ) ) )
2034 return m_editPoints->Point( DIM_START );
2035
2036 break;
2037 }
2038
2039 case PCB_DIM_RADIAL_T:
2041 {
2042 if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
2043 return m_editPoints->Point( DIM_KNEE );
2044
2045 break;
2046 }
2047
2048 default:
2049 break;
2050 }
2051
2052 // In any other case we may align item to its original position
2053 return m_original;
2054}
2055
2056
2058{
2059 const auto type = aItem.Type();
2060
2061 // Works only for zones and line segments
2062 if( type == PCB_ZONE_T || type == PCB_FP_ZONE_T )
2063 return true;
2064
2065 if( type == PCB_SHAPE_T || type == PCB_FP_SHAPE_T )
2066 {
2067 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
2068 return shape.GetShape() == SHAPE_T::SEGMENT || shape.GetShape() == SHAPE_T::POLY;
2069 }
2070
2071 return false;
2072}
2073
2074
2076{
2077 if( aSelection.Size() != 1 )
2078 return false;
2079
2080 const EDA_ITEM* item = aSelection.Front();
2081
2082 return ( item != nullptr ) && canAddCorner( *item );
2083}
2084
2085
2086// Finds a corresponding vertex in a polygon set
2087static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
2088findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
2089{
2090 for( auto it = aPolySet.IterateWithHoles(); it; ++it )
2091 {
2092 auto vertexIdx = it.GetIndex();
2093
2094 if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
2095 return std::make_pair( true, vertexIdx );
2096 }
2097
2098 return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
2099}
2100
2101
2103{
2104 if( !m_editPoints || !m_editedPoint )
2105 return false;
2106
2107 EDA_ITEM* item = m_editPoints->GetParent();
2108 SHAPE_POLY_SET* polyset = nullptr;
2109
2110 if( !item )
2111 return false;
2112
2113 switch( item->Type() )
2114 {
2115 case PCB_ZONE_T:
2116 case PCB_FP_ZONE_T:
2117 polyset = static_cast<ZONE*>( item )->Outline();
2118 break;
2119
2120 case PCB_SHAPE_T:
2121 case PCB_FP_SHAPE_T:
2122 if( static_cast<PCB_SHAPE*>( item )->GetShape() == SHAPE_T::POLY )
2123 polyset = &static_cast<PCB_SHAPE*>( item )->GetPolyShape();
2124 else
2125 return false;
2126
2127 break;
2128
2129 default:
2130 return false;
2131 }
2132
2133 std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX> vertex = findVertex( *polyset, *m_editedPoint );
2134
2135 if( !vertex.first )
2136 return false;
2137
2138 const SHAPE_POLY_SET::VERTEX_INDEX& vertexIdx = vertex.second;
2139
2140 // Check if there are enough vertices so one can be removed without
2141 // degenerating the polygon.
2142 // The first condition allows one to remove all corners from holes (when
2143 // there are only 2 vertices left, a hole is removed).
2144 if( vertexIdx.m_contour == 0 &&
2145 polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
2146 {
2147 return false;
2148 }
2149
2150 // Remove corner does not work with lines
2151 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2152 return false;
2153
2154 return m_editedPoint != nullptr;
2155}
2156
2157
2159{
2160 if( !m_editPoints )
2161 return 0;
2162
2163 EDA_ITEM* item = m_editPoints->GetParent();
2164 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2165 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
2166
2167 // called without an active edited polygon
2168 if( !item || !canAddCorner( *item ) )
2169 return 0;
2170
2171 PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
2172 BOARD_COMMIT commit( frame );
2173
2174 if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T
2175 || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
2176 {
2177 unsigned int nearestIdx = 0;
2178 unsigned int nextNearestIdx = 0;
2179 unsigned int nearestDist = INT_MAX;
2180 unsigned int firstPointInContour = 0;
2181 SHAPE_POLY_SET* zoneOutline;
2182
2183 if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2184 {
2185 ZONE* zone = static_cast<ZONE*>( item );
2186 zoneOutline = zone->Outline();
2187 zone->SetNeedRefill( true );
2188 }
2189 else
2190 {
2191 zoneOutline = &( graphicItem->GetPolyShape() );
2192 }
2193
2194 commit.Modify( item );
2195
2196 // Search the best outline segment to add a new corner
2197 // and therefore break this segment into two segments
2198
2199 // Object to iterate through the corners of the outlines (main contour and its holes)
2200 SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0, zoneOutline->OutlineCount()-1,
2201 /* IterateHoles */ true );
2202 int curr_idx = 0;
2203
2204 // Iterate through all the corners of the outlines and search the best segment
2205 for( ; iterator; iterator++, curr_idx++ )
2206 {
2207 int jj = curr_idx+1;
2208
2209 if( iterator.IsEndContour() )
2210 { // We reach the last point of the current contour (main or hole)
2211 jj = firstPointInContour;
2212 firstPointInContour = curr_idx+1; // Prepare next contour analysis
2213 }
2214
2215 SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
2216
2217 unsigned int distance = curr_segment.Distance( cursorPos );
2218
2219 if( distance < nearestDist )
2220 {
2221 nearestDist = distance;
2222 nearestIdx = curr_idx;
2223 nextNearestIdx = jj;
2224 }
2225 }
2226
2227 // Find the point on the closest segment
2228 const VECTOR2I& sideOrigin = zoneOutline->CVertex( nearestIdx );
2229 const VECTOR2I& sideEnd = zoneOutline->CVertex( nextNearestIdx );
2230 SEG nearestSide( sideOrigin, sideEnd );
2231 VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
2232
2233 // Do not add points that have the same coordinates as ones that already belong to polygon
2234 // instead, add a point in the middle of the side
2235 if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
2236 nearestPoint = ( sideOrigin + sideEnd ) / 2;
2237
2238 zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
2239
2240 // We re-hatch the filled zones but not polygons
2241 if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2242 static_cast<ZONE*>( item )->HatchBorder();
2243
2244
2245 commit.Push( _( "Add a zone corner" ) );
2246 }
2247 else if( graphicItem && graphicItem->GetShape() == SHAPE_T::SEGMENT )
2248 {
2249 commit.Modify( graphicItem );
2250
2251 SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
2252 VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
2253
2254 // Move the end of the line to the break point..
2255 graphicItem->SetEnd( VECTOR2I( nearestPoint.x, nearestPoint.y ) );
2256
2257 if( graphicItem->Type() == PCB_FP_SHAPE_T )
2258 static_cast<FP_SHAPE*>( graphicItem )->SetLocalCoord();
2259
2260 // and add another one starting from the break point
2261 PCB_SHAPE* newSegment;
2262
2263 if( item->Type() == PCB_FP_SHAPE_T )
2264 {
2265 FP_SHAPE* edge = static_cast<FP_SHAPE*>( graphicItem );
2266 assert( edge->GetParent()->Type() == PCB_FOOTPRINT_T );
2267 newSegment = new FP_SHAPE( *edge );
2268 }
2269 else
2270 {
2271 newSegment = new PCB_SHAPE( *graphicItem );
2272 }
2273
2274 newSegment->ClearSelected();
2275 newSegment->SetStart( VECTOR2I( nearestPoint.x, nearestPoint.y ) );
2276 newSegment->SetEnd( VECTOR2I( seg.B.x, seg.B.y ) );
2277
2278 if( newSegment->Type() == PCB_FP_SHAPE_T )
2279 static_cast<FP_SHAPE*>( newSegment )->SetLocalCoord();
2280
2281 commit.Add( newSegment );
2282 commit.Push( _( "Split segment" ) );
2283 }
2284
2285 updatePoints();
2286 return 0;
2287}
2288
2289
2291{
2292 if( !m_editPoints || !m_editedPoint )
2293 return 0;
2294
2295 EDA_ITEM* item = m_editPoints->GetParent();
2296
2297 if( !item )
2298 return 0;
2299
2300 SHAPE_POLY_SET* polygon = nullptr;
2301
2302 if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2303 {
2304 ZONE* zone = static_cast<ZONE*>( item );
2305 polygon = zone->Outline();
2306 zone->SetNeedRefill( true );
2307 }
2308 else if( item->Type() == PCB_FP_SHAPE_T || item->Type() == PCB_SHAPE_T )
2309 {
2310 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2311
2312 if( shape->GetShape() == SHAPE_T::POLY )
2313 polygon = &shape->GetPolyShape();
2314 }
2315
2316 if( !polygon )
2317 return 0;
2318
2319 PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
2320 BOARD_COMMIT commit( frame );
2321 auto vertex = findVertex( *polygon, *m_editedPoint );
2322
2323 if( vertex.first )
2324 {
2325 const auto& vertexIdx = vertex.second;
2326 auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
2327
2328 if( outline.PointCount() > 3 )
2329 {
2330 // the usual case: remove just the corner when there are >3 vertices
2331 commit.Modify( item );
2332 polygon->RemoveVertex( vertexIdx );
2333 validatePolygon( *polygon );
2334 }
2335 else
2336 {
2337 // either remove a hole or the polygon when there are <= 3 corners
2338 if( vertexIdx.m_contour > 0 )
2339 {
2340 // remove hole
2341 commit.Modify( item );
2342 polygon->RemoveContour( vertexIdx.m_contour );
2343 }
2344 else
2345 {
2347 commit.Remove( item );
2348 }
2349 }
2350
2351 setEditedPoint( nullptr );
2352
2353 commit.Push( _( "Remove a zone/polygon corner" ) );
2354
2355 // Refresh zone hatching
2356 if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2357 static_cast<ZONE*>( item )->HatchBorder();
2358
2359 updatePoints();
2360 }
2361
2362 return 0;
2363}
2364
2365
2367{
2368 updatePoints();
2369 return 0;
2370}
2371
2372
2374{
2375 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2376
2378 {
2379 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2381 else
2383
2384 switch( m_arcEditMode )
2385 {
2388 break;
2391 break;
2392 }
2393 }
2394 else
2395 {
2397 }
2398
2399 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2401 else
2403
2404 return 0;
2405}
2406
2407
2409{
2422}
ARC_EDIT_MODE
Settings for arc editing.
Definition: app_settings.h:70
@ KEEP_ENDPOINTS_OR_START_DIRECTION
@ KEEP_CENTER_ADJUST_ANGLE_RADIUS
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
static TOOL_ACTION cycleArcEditMode
Definition: actions.h:173
static TOOL_ACTION activatePointEditor
Definition: actions.h:172
double m_DrawArcCenterMaxAngle
When drawing an arc, the angle ( center - start ) - ( start - end ) can be limited to avoid extremely...
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
virtual void Revert() override
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:58
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:185
virtual bool IsLocked() const
Definition: board_item.cpp:71
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:163
coord_type GetTop() const
Definition: box2.h:194
coord_type GetHeight() const
Definition: box2.h:188
coord_type GetWidth() const
Definition: box2.h:187
bool Contains(const Vec &aPoint) const
Definition: box2.h:141
coord_type GetRight() const
Definition: box2.h:189
coord_type GetLeft() const
Definition: box2.h:193
coord_type GetBottom() const
Definition: box2.h:190
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
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
COMMIT & Remove(EDA_ITEM *aItem)
Notify observers that aItem has been removed.
Definition: commit.h:90
COMMIT & Add(EDA_ITEM *aItem)
Notify observers that aItem has been added.
Definition: commit.h:78
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.
EDIT_CONSTRAINT that imposes a constraint that two points have to be located at angle of 45 degree mu...
EDIT_CONSTRAINT for 3 segments: dragged and two adjacent ones, enforcing to keep their slopes and all...
EDIT_CONSTRAINT that imposes a constraint that a point has to lie on a line (determined by 2 points).
EDIT_CONSTRAINT for a EDIT_LINE, that constrains the line to move perpendicular to the line itself.
bool IsType(FRAME_T aType) const
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
virtual void UpdateMsgPanel()
Redraw the message panel.
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:147
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
void ClearSelected()
Definition: eda_item.h:121
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:179
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:178
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:458
virtual VECTOR2I GetTopLeft() const
Definition: eda_shape.h:167
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:405
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:247
SHAPE_T GetShape() const
Definition: eda_shape.h:113
virtual VECTOR2I GetBotRight() const
Definition: eda_shape.h:168
virtual void SetBottom(int val)
Definition: eda_shape.h:173
virtual void SetTop(int val)
Definition: eda_shape.h:170
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:145
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:124
void DupPolyPointsList(std::vector< VECTOR2I > &aBuffer) const
Duplicate the list of corners in a std::vector<VECTOR2I>
Definition: eda_shape.cpp:1213
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:120
int GetWidth() const
Definition: eda_shape.h:109
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:149
void SetBezierC1(const VECTOR2I &aPt)
Definition: eda_shape.h:175
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:543
virtual void SetLeft(int val)
Definition: eda_shape.h:171
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:176
virtual void SetRight(int val)
Definition: eda_shape.h:172
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:476
Represent a line connecting two EDIT_POINTs.
Definition: edit_points.h:221
Represent a single point that can be used for modifying items.
Definition: edit_points.h:48
SNAP_CONSTRAINT_TYPE GetSnapConstraint() const
Definition: edit_points.h:181
virtual void SetPosition(const VECTOR2I &aPosition)
Set new coordinates for an EDIT_POINT.
Definition: edit_points.h:107
virtual VECTOR2I GetPosition() const
Return coordinates of an EDIT_POINT.
Definition: edit_points.h:71
virtual void ApplyConstraint(const GRID_HELPER &aGrid)
Correct coordinates of an EDIT_POINT by applying previously set constraint.
Definition: edit_points.h:166
void SetHover(bool aHover=true)
Definition: edit_points.h:176
bool IsConstrained() const
Check if point is constrained.
Definition: edit_points.h:158
void SetActive(bool aActive=true)
Definition: edit_points.h:173
GRID_CONSTRAINT_TYPE GetGridConstraint() const
Definition: edit_points.h:178
static const TOOL_EVENT InhibitSelectionEditing
Definition: actions.h:219
static const TOOL_EVENT SelectedEvent
Definition: actions.h:206
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:213
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition: actions.h:220
static const TOOL_EVENT PointSelectedEvent
Definition: actions.h:205
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:216
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:207
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: pcb_view.cpp:92
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:316
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:346
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:1586
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:530
static LSET AllLayersMask()
Definition: lset.cpp:808
Definition: pad.h:59
ARC_EDIT_MODE m_ArcEditMode
static TOOL_ACTION pointEditorArcKeepCenter
Definition: pcb_actions.h:248
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:59
static TOOL_ACTION pointEditorArcKeepEndpoint
Definition: pcb_actions.h:249
static TOOL_ACTION pointEditorRemoveCorner
Definition: pcb_actions.h:246
static TOOL_ACTION pointEditorAddCorner
Definition: pcb_actions.h:245
Common, abstract interface for edit frames.
void UndoRedoBlock(bool aBlock=true)
Enable/disable undo and redo operations.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
PCBNEW_SETTINGS * GetPcbNewSettings() const
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
FOOTPRINT_EDITOR_SETTINGS * GetFootprintEditorSettings() const
Object to handle a bitmap image that can be inserted in a PCB.
Definition: pcb_bitmap.h:42
VECTOR2I GetPosition() const override
Definition: pcb_bitmap.h:117
void SetImageScale(double aScale)
Definition: pcb_bitmap.h:67
double GetImageScale() const
Definition: pcb_bitmap.h:65
const VECTOR2I GetSize() const
Definition: pcb_bitmap.cpp:140
void Update()
Update the dimension's cached text and geometry.
PCB_TEXT & Text()
void SetTextPositionMode(DIM_TEXT_POSITION aMode)
virtual const VECTOR2I & GetStart() const
The dimension's origin is the first feature point for the dimension.
virtual void SetEnd(const VECTOR2I &aPoint)
virtual void SetStart(const VECTOR2I &aPoint)
virtual const VECTOR2I & GetEnd() const
For better understanding of the points that make a dimension:
const VECTOR2I & GetCrossbarStart() const
const VECTOR2I & GetCrossbarEnd() const
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
Mark the center of a circle or arc with a cross shape.
A leader is a dimension-like object pointing to a specific point.
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
void SetOrientation(DIR aOrientation)
Set the orientation of the dimension line (so, perpendicular to the feature lines).
DIR GetOrientation() const
A radial dimension indicates either the radius or diameter of an arc or circle.
void SetLeaderLength(int aLength)
VECTOR2I GetKnee() const
int changeArcEditMode(const TOOL_EVENT &aEvent)
void pinEditedCorner(VECTOR2I &aTopLeft, VECTOR2I &aTopRight, VECTOR2I &aBotLeft, VECTOR2I &aBotRight, const VECTOR2I &aHole={ 0, 0 }, const VECTOR2I &aHoleSize={ 0, 0 }) const
Set up an alternative constraint (typically enabled upon a modifier key being pressed).
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.
int OnSelectionChange(const TOOL_EVENT &aEvent)
Change selection event handler.
void setAltConstraint(bool aEnabled)
Return a point that should be used as a constrainer for 45 degrees mode.
static const unsigned int COORDS_PADDING
bool removeCornerCondition(const SELECTION &aSelection)
EDIT_POINT * m_hoveredPoint
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.
int addCorner(const TOOL_EVENT &aEvent)
TOOL_ACTION handlers.
EDIT_POINT m_original
Original position for the current drag point.
EDIT_POINT get45DegConstrainer() const
Condition to display "Create corner" context menu entry.
int modifiedSelection(const TOOL_EVENT &aEvent)
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 Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
int removeCorner(const TOOL_EVENT &aEvent)
static bool canAddCorner(const EDA_ITEM &aItem)
Condition to display "Remove corner" context menu entry.
bool Init() override
Init() is called once upon a registration of the tool.
void setEditedPoint(EDIT_POINT *aPoint)
void buildForPolyOutline(std::shared_ptr< EDIT_POINTS > points, const SHAPE_POLY_SET *aOutline)
EDIT_POINT m_altConstrainer
bool isModified(const EDIT_POINT &aPoint) const
ARC_EDIT_MODE m_arcEditMode
EDIT_POINT * m_editedPoint
bool validatePolygon(SHAPE_POLY_SET &aModified) const
Validate a polygon and displays a popup warning if invalid.
void updateEditedPoint(const TOOL_EVENT &aEvent)
Set the current point being edited. NULL means none.
std::shared_ptr< EDIT_CONSTRAINT< EDIT_POINT > > m_altConstraint
void updatePoints()
Update which point is being edited.
static bool addCornerCondition(const SELECTION &aSelection)
Determine if the tool can currently add a corner to the given item.
void setTransitions() override
< Set up handlers for various events.
std::shared_ptr< EDIT_POINTS > m_editPoints
std::shared_ptr< EDIT_POINTS > makePoints(EDA_ITEM *aItem)
Update item's points with edit points.
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.
PCB_SELECTION_TOOL * m_selectionTool
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:67
virtual void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: pcb_shape.cpp:163
virtual VECTOR2I GetPosition() const override
Definition: pcb_text.h:76
virtual void SetPosition(const VECTOR2I &aPos) override
Definition: pcb_text.h:81
KIGFX::PCB_VIEW * view() const
PCB_BASE_EDIT_FRAME * frame() const
KIGFX::VIEW_CONTROLS * controls() const
bool m_isFootprintEditor
const PCB_SELECTION & selection() const
Definition: seg.h:42
VECTOR2I B
Definition: seg.h:50
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition: seg.cpp:261
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition: seg.cpp:319
EDA_ITEM * Front() const
Definition: selection.h:206
int Size() const
Returns the number of selected parts.
Definition: selection.h:113
Base class for iterating over all vertices in a given SHAPE_POLY_SET.
Represent a set of closed polygons.
ITERATOR IterateWithHoles(int aOutline)
void InsertVertex(int aGlobalIndex, const VECTOR2I &aNewVertex)
Adds a vertex in the globally indexed position aGlobalIndex.
void SetVertex(const VERTEX_INDEX &aIndex, const VECTOR2I &aPos)
Accessor function to set the position of a specific point.
int TotalVertices() const
Delete aIdx-th polygon from the set.
POLYGON & Polygon(int aIndex)
void RemoveVertex(int aGlobalIndex)
Delete the aGlobalIndex-th vertex.
void RemoveContour(int aContourIdx, int aPolygonIdx=-1)
Delete the aContourIdx-th contour of the aPolygonIdx-th polygon in the set.
ITERATOR Iterate(int aFirst, int aLast, bool aIterateHoles=false)
Return an object to iterate through the points of the polygons between aFirst and aLast.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the aGlobalIndex-th vertex in the poly set.
int OutlineCount() const
Return the number of vertices in a given outline/hole.
CONST_ITERATOR CIterateWithHoles(int aOutline) const
TOOL_EVENT MakeEvent() const
Return the event associated with the action (i.e.
Definition: tool_action.cpp:72
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
Generic, UI-independent tool event.
Definition: tool_event.h:156
T Parameter() const
Return a non-standard parameter assigned to the event.
Definition: tool_event.h:442
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition: tool_event.h:365
const VECTOR2D Position() const
Returns the point where dragging has started.
Definition: tool_event.h:266
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:288
const VECTOR2D DragOrigin() const
Returns information about mouse buttons state.
Definition: tool_event.h:272
bool IsMotion() const
Definition: tool_event.h:303
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).
TOOL_MENU & GetToolMenu()
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.
void Activate()
Run the tool.
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:142
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:293
extended_type Cross(const VECTOR2< T > &aVector) const
Compute cross product of self with aVector.
Definition: vector2d.h:487
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:378
T y
Definition: vector3.h:62
T x
Definition: vector3.h:61
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
void SetNeedRefill(bool aNeedRefill)
Definition: zone.h:246
bool UnFill()
Removes the zone filling.
Definition: zone.cpp:206
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:852
SHAPE_POLY_SET * Outline()
Definition: zone.h:312
@ CHT_MODIFY
Definition: commit.h:42
This file is part of the common library.
#define _(s)
static constexpr EDA_ANGLE & ANGLE_90
Definition: eda_angle.h:414
static constexpr EDA_ANGLE & ANGLE_270
Definition: eda_angle.h:417
#define ENTERED
indicates a group has been entered
@ ALL_LAYERS
@ OBJECT_LAYERS
@ IGNORE_SNAPS
@ IGNORE_GRID
@ SNAP_BY_GRID
@ SNAP_TO_GRID
ARC_POINTS
CIRCLE_POINTS
@ FRAME_PCB_EDITOR
Definition: frame_type.h:40
constexpr int Mils2IU(const EDA_IU_SCALE &aIuScale, int mils)
Definition: eda_units.h:123
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:401
@ MANUAL
Text placement is manually set by the user.
RECT_LINES
@ RECT_RIGHT
@ RECT_LEFT
@ RECT_BOT
@ RECT_TOP
BEZIER_CURVE_POINTS
@ BEZIER_CURVE_START
@ BEZIER_CURVE_CONTROL_POINT2
@ BEZIER_CURVE_END
@ BEZIER_CURVE_CONTROL_POINT1
static std::pair< bool, SHAPE_POLY_SET::VERTEX_INDEX > findVertex(SHAPE_POLY_SET &aPolySet, const EDIT_POINT &aPoint)
RECT_POINTS
@ RECT_BOT_LEFT
@ RECT_BOT_RIGHT
@ RECT_TOP_RIGHT
@ RECT_TOP_LEFT
DIMENSION_POINTS
@ DIM_KNEE
@ DIM_TEXT
@ DIM_CROSSBAREND
@ DIM_END
@ DIM_START
@ DIM_CROSSBARSTART
@ ARC_MID
@ ARC_START
@ ARC_END
@ ARC_CENTER
@ CIRC_END
@ CIRC_CENTER
SEG_POINTS
@ SEG_END
@ SEG_START
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
#define R()
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
Structure to hold the necessary information in order to index a vertex on a SHAPE_POLY_SET object: th...
VECTOR2I v2(1, 0)
Test suite for KiCad math code.
v1
VECTOR3I v3(1, 1, 1)
constexpr int delta
@ TA_MOUSE_DOWN
Definition: tool_event.h:65
@ TA_UNDO_REDO_POST
Definition: tool_event.h:104
@ MD_SHIFT
Definition: tool_event.h:138
@ BUT_LEFT
Definition: tool_event.h:127
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:129
@ PCB_FP_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:95
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:110
@ PCB_FP_SHAPE_T
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:94
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:107
@ PCB_FP_TEXTBOX_T
class FP_TEXTBOX, wrapped text in a footprint
Definition: typeinfo.h:93
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:108
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition: typeinfo.h:91
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:112
@ PCB_FP_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:97
@ PCB_FP_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:99
@ PCB_FP_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:96
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:106
@ PCB_FP_ZONE_T
class ZONE, managed by a footprint
Definition: typeinfo.h:100
@ PCB_BITMAP_T
class PCB_BITMAP, bitmap on a layer
Definition: typeinfo.h:89
@ PCB_FP_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:98
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:109
VECTOR2< double > VECTOR2D
Definition: vector2d.h:617
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618