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-2023 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 break;
248
249 case SHAPE_T::CIRCLE:
250 points->AddPoint( shape->GetCenter() );
251 points->AddPoint( shape->GetEnd() );
252 break;
253
254 case SHAPE_T::POLY:
255 buildForPolyOutline( points, &shape->GetPolyShape() );
256 break;
257
258 case SHAPE_T::BEZIER:
259 points->AddPoint( shape->GetStart() );
260 points->AddPoint( shape->GetBezierC1() );
261 points->AddPoint( shape->GetBezierC2() );
262 points->AddPoint( shape->GetEnd() );
263 break;
264
265 default: // suppress warnings
266 break;
267 }
268
269 break;
270 }
271
272 case PCB_PAD_T:
273 {
274 const PAD* pad = static_cast<const PAD*>( aItem );
275 VECTOR2I shapePos = pad->ShapePos();
276 VECTOR2I halfSize( pad->GetSize().x / 2, pad->GetSize().y / 2 );
277
278 if( !m_isFootprintEditor || pad->IsLocked() )
279 break;
280
281 switch( pad->GetShape() )
282 {
284 points->AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y ) );
285 break;
286
287 case PAD_SHAPE::OVAL:
289 case PAD_SHAPE::RECT:
292 {
293 if( !pad->GetOrientation().IsCardinal() )
294 break;
295
296 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
297 std::swap( halfSize.x, halfSize.y );
298
299 points->AddPoint( shapePos - halfSize );
300 points->AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
301 points->AddPoint( shapePos + halfSize );
302 points->AddPoint( VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
303 }
304 break;
305
306 default: // suppress warnings
307 break;
308 }
309 }
310 break;
311
312 case PCB_FP_ZONE_T:
313 case PCB_ZONE_T:
314 {
315 const ZONE* zone = static_cast<const ZONE*>( aItem );
316 buildForPolyOutline( points, zone->Outline() );
317 }
318 break;
319
324 {
325 const PCB_DIM_ALIGNED* dimension = static_cast<const PCB_DIM_ALIGNED*>( aItem );
326
327 points->AddPoint( dimension->GetStart() );
328 points->AddPoint( dimension->GetEnd() );
329 points->AddPoint( dimension->GetTextPos() );
330 points->AddPoint( dimension->GetCrossbarStart() );
331 points->AddPoint( dimension->GetCrossbarEnd() );
332
333 points->Point( DIM_START ).SetSnapConstraint( ALL_LAYERS );
334 points->Point( DIM_END ).SetSnapConstraint( ALL_LAYERS );
335
336 if( aItem->Type() == PCB_DIM_ALIGNED_T )
337 {
338 // Dimension height setting - edit points should move only along the feature lines
339 points->Point( DIM_CROSSBARSTART )
340 .SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARSTART ),
341 points->Point( DIM_START ) ) );
342 points->Point( DIM_CROSSBAREND )
343 .SetConstraint( new EC_LINE( points->Point( DIM_CROSSBAREND ),
344 points->Point( DIM_END ) ) );
345 }
346
347 break;
348 }
349
350 case PCB_DIM_CENTER_T:
352 {
353 const PCB_DIM_CENTER* dimension = static_cast<const PCB_DIM_CENTER*>( aItem );
354
355 points->AddPoint( dimension->GetStart() );
356 points->AddPoint( dimension->GetEnd() );
357
358 points->Point( DIM_START ).SetSnapConstraint( ALL_LAYERS );
359
360 points->Point( DIM_END ).SetConstraint( new EC_45DEGREE( points->Point( DIM_END ),
361 points->Point( DIM_START ) ) );
362 points->Point( DIM_END ).SetSnapConstraint( IGNORE_SNAPS );
363
364 break;
365 }
366
367 case PCB_DIM_RADIAL_T:
369 {
370 const PCB_DIM_RADIAL* dimension = static_cast<const PCB_DIM_RADIAL*>( aItem );
371
372 points->AddPoint( dimension->GetStart() );
373 points->AddPoint( dimension->GetEnd() );
374 points->AddPoint( dimension->GetTextPos() );
375 points->AddPoint( dimension->GetKnee() );
376
377 points->Point( DIM_START ).SetSnapConstraint( ALL_LAYERS );
378 points->Point( DIM_END ).SetSnapConstraint( ALL_LAYERS );
379
380 points->Point( DIM_KNEE ).SetConstraint( new EC_LINE( points->Point( DIM_START ),
381 points->Point( DIM_END ) ) );
382 points->Point( DIM_KNEE ).SetSnapConstraint( IGNORE_SNAPS );
383
384 points->Point( DIM_TEXT ).SetConstraint( new EC_45DEGREE( points->Point( DIM_TEXT ),
385 points->Point( DIM_KNEE ) ) );
386 points->Point( DIM_TEXT ).SetSnapConstraint( IGNORE_SNAPS );
387
388 break;
389 }
390
391 case PCB_DIM_LEADER_T:
393 {
394 const PCB_DIM_LEADER* dimension = static_cast<const PCB_DIM_LEADER*>( aItem );
395
396 points->AddPoint( dimension->GetStart() );
397 points->AddPoint( dimension->GetEnd() );
398 points->AddPoint( dimension->GetTextPos() );
399
400 points->Point( DIM_START ).SetSnapConstraint( ALL_LAYERS );
401 points->Point( DIM_END ).SetSnapConstraint( ALL_LAYERS );
402
403 points->Point( DIM_TEXT ).SetConstraint( new EC_45DEGREE( points->Point( DIM_TEXT ),
404 points->Point( DIM_END ) ) );
405 points->Point( DIM_TEXT ).SetSnapConstraint( IGNORE_SNAPS );
406
407 break;
408 }
409
410 default:
411 points.reset();
412 break;
413 }
414
415 return points;
416}
417
418
420{
421 EDIT_POINT* point;
422 EDIT_POINT* hovered = nullptr;
423
424 if( aEvent.IsMotion() )
425 {
426 point = m_editPoints->FindPoint( aEvent.Position(), getView() );
427 hovered = point;
428 }
429 else if( aEvent.IsDrag( BUT_LEFT ) )
430 {
431 point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
432 }
433 else
434 {
435 point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
436 }
437
438 if( hovered )
439 {
440 if( m_hoveredPoint != hovered )
441 {
442 if( m_hoveredPoint )
443 m_hoveredPoint->SetHover( false );
444
445 m_hoveredPoint = hovered;
447 }
448 }
449 else if( m_hoveredPoint )
450 {
451 m_hoveredPoint->SetHover( false );
452 m_hoveredPoint = nullptr;
453 }
454
455 if( m_editedPoint != point )
456 setEditedPoint( point );
457}
458
459
461{
463 return 0;
464
465 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
467
468 if( selection.Size() != 1 || selection.Front()->GetEditFlags() )
469 return 0;
470
471 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
472
473 if( !item || item->IsLocked() )
474 return 0;
475
476 Activate();
477 // Must be done after Activate() so that it gets set into the correct context
478 getViewControls()->ShowCursor( true );
479
481 m_editPoints = makePoints( item );
482
483 if( !m_editPoints )
484 return 0;
485
486 getView()->Add( m_editPoints.get() );
487 setEditedPoint( nullptr );
488 updateEditedPoint( aEvent );
489 m_refill = false;
490 bool inDrag = false;
491 bool useAltContraint = false;
492
493 BOARD_COMMIT commit( editFrame );
494
495 // Main loop: keep receiving events
496 while( TOOL_EVENT* evt = Wait() )
497 {
498 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
499 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
500
501 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
502 {
503 useAltContraint = editFrame->GetPcbNewSettings()->m_Use45DegreeLimit;
505 }
506 else
507 {
508 useAltContraint = editFrame->GetFootprintEditorSettings()->m_Use45Limit;
510 }
511
512 if( !m_editPoints || evt->IsSelectionEvent() ||
513 evt->Matches( EVENTS::InhibitSelectionEditing ) )
514 {
515 break;
516 }
517
518 EDIT_POINT* prevHover = m_hoveredPoint;
519
520 if( !inDrag )
521 updateEditedPoint( *evt );
522
523 if( prevHover != m_hoveredPoint )
524 getView()->Update( m_editPoints.get() );
525
526 if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
527 {
528 if( !inDrag )
529 {
530 frame()->UndoRedoBlock( true );
531
533
535 m_original = *m_editedPoint; // Save the original position
536 getViewControls()->SetAutoPan( true );
537 inDrag = true;
538
540 grid.SetAuxAxes( true, m_original.GetPosition() );
541
542 setAltConstraint( true );
544
545 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
546 {
547 EDIT_POINT& point = m_editPoints->Point( ii );
548
549 if( &point != m_editedPoint )
550 point.SetActive( false );
551 }
552 }
553
554 // Keep point inside of limits with some padding
555 VECTOR2I pos = GetClampedCoords<double, int>( evt->Position(), COORDS_PADDING );
556 LSET snapLayers;
557
559 {
560 case IGNORE_SNAPS: break;
561 case OBJECT_LAYERS: snapLayers = item->GetLayerSet(); break;
562 case ALL_LAYERS: snapLayers = LSET::AllLayersMask(); break;
563 }
564
566 {
567 if( grid.GetUseGrid() )
568 {
569 VECTOR2I gridPt = grid.BestSnapAnchor( pos, {}, { item } );
570
572 VECTOR2I delta = pos - last;
573 VECTOR2I deltaGrid = gridPt - grid.BestSnapAnchor( last, {}, { item } );
574
575 if( abs( delta.x ) > grid.GetGrid().x / 2 )
576 pos.x = last.x + deltaGrid.x;
577 else
578 pos.x = last.x;
579
580 if( abs( delta.y ) > grid.GetGrid().y / 2 )
581 pos.y = last.y + deltaGrid.y;
582 else
583 pos.y = last.y;
584 }
585 }
586
588
589 // The alternative constraint limits to 45 degrees
590 if( useAltContraint )
591 {
592 m_altConstraint->Apply( grid );
593 }
594 else if( m_editedPoint->IsConstrained() )
595 {
597 }
599 {
601 snapLayers, { item } ) );
602 }
603
604 updateItem();
606 updatePoints();
607 }
608 else if( m_editedPoint && evt->Action() == TA_MOUSE_DOWN && evt->Buttons() == BUT_LEFT )
609 {
611
612 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
613 {
614 EDIT_POINT& point = m_editPoints->Point( ii );
615
616 if( &point != m_editedPoint )
617 point.SetActive( false );
618 }
619
620 getView()->Update( m_editPoints.get() );
621 }
622 else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
623 {
624 if( m_editedPoint )
625 {
626 m_editedPoint->SetActive( false );
627 getView()->Update( m_editPoints.get() );
628 }
629
630 getViewControls()->SetAutoPan( false );
631 setAltConstraint( false );
632
633 commit.Push( _( "Drag a corner" ) );
634 inDrag = false;
635 frame()->UndoRedoBlock( false );
636
637 m_refill = true;
638 }
639 else if( evt->IsCancelInteractive() || evt->IsActivate() )
640 {
641 if( inDrag ) // Restore the last change
642 {
643 commit.Revert();
644 inDrag = false;
645 frame()->UndoRedoBlock( false );
646 }
647
648 // Only cancel point editor when activating a new tool
649 // Otherwise, allow the points to persist when moving up the
650 // tool stack
651 if( evt->IsActivate() && !evt->IsMoveTool() )
652 break;
653 }
654 else if( evt->Action() == TA_UNDO_REDO_POST )
655 {
656 break;
657 }
658 else
659 {
660 evt->SetPassEvent();
661 }
662 }
663
664 if( m_editPoints )
665 {
666 getView()->Remove( m_editPoints.get() );
667 m_editPoints.reset();
668 }
669
670 m_editedPoint = nullptr;
671
672 return 0;
673}
674
676 const VECTOR2I& aStart, const VECTOR2I& aMid,
677 const VECTOR2I& aEnd,
678 const VECTOR2I& aCursor ) const
679{
680 VECTOR2I center = aCenter;
681 bool movingStart;
682 bool arcValid = true;
683
684 VECTOR2I p1, p2, p3;
685 // p1 does not move, p2 does.
686
687 if( aStart != aArc->GetStart() )
688 {
689 p1 = aEnd;
690 p2 = aStart;
691 p3 = aMid;
692 movingStart = true;
693 }
694 else if( aEnd != aArc->GetEnd() )
695 {
696 p1 = aStart;
697 p2 = aEnd;
698 p3 = aMid;
699 movingStart = false;
700 }
701 else
702 {
703 return;
704 }
705
706 VECTOR2D v1, v2, v3, v4;
707
708 // Move the coordinate system
709 v1 = p1 - aCenter;
710 v2 = p2 - aCenter;
711 v3 = p3 - aCenter;
712
713 VECTOR2D u1, u2, u3;
714
715 // A point cannot be both the center and on the arc.
716 if( ( v1.EuclideanNorm() == 0 ) || ( v2.EuclideanNorm() == 0 ) )
717 return;
718
719 u1 = v1 / v1.EuclideanNorm();
720 u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
721 u2 = u2 / u2.EuclideanNorm();
722
723 // [ u1, u3 ] is a base centered on the circle with:
724 // u1 : unit vector toward the point that does not move
725 // u2 : unit vector toward the mid point.
726
727 // Get vectors v1, and v2 in that coordinate system.
728
729 double det = u1.x * u2.y - u2.x * u1.y;
730
731 // u1 and u2 are unit vectors, and perpendicular.
732 // det should not be 0. In case it is, do not change the arc.
733 if( det == 0 )
734 return;
735
736 double tmpx = v1.x * u2.y - v1.y * u2.x;
737 double tmpy = -v1.x * u1.y + v1.y * u1.x;
738 v1.x = tmpx;
739 v1.y = tmpy;
740 v1 = v1 / det;
741
742 tmpx = v2.x * u2.y - v2.y * u2.x;
743 tmpy = -v2.x * u1.y + v2.y * u1.x;
744 v2.x = tmpx;
745 v2.y = tmpy;
746 v2 = v2 / det;
747
748 double R = v1.EuclideanNorm();
749 bool transformCircle = false;
750
751 /* p2
752 * X***
753 * ** <---- This is the arc
754 * y ^ **
755 * | R *
756 * | <-----------> *
757 * x------x------>--------x p1
758 * C' <----> C x
759 * delta
760 *
761 * p1 does not move, and the tangent at p1 remains the same.
762 * => The new center, C', will be on the C-p1 axis.
763 * p2 moves
764 *
765 * The radius of the new circle is delta + R
766 *
767 * || C' p2 || = || C' P1 ||
768 * is the same as :
769 * ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
770 *
771 * delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
772 *
773 * We can use this equation for any point p2 with p2.x < R
774 */
775
776 if( v2.x == R )
777 {
778 // Straight line, do nothing
779 }
780 else
781 {
782 if( v2.x > R )
783 {
784 // If we need to invert the curvature.
785 // We modify the input so we can use the same equation
786 transformCircle = true;
787 v2.x = 2 * R - v2.x;
788 }
789
790 // We can keep the tangent constraint.
791 double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
792
793 // This is just to limit the radius, so nothing overflows later when drawing.
795 arcValid = false;
796
797 // Never recorded a problem, but still checking.
798 if( !std::isfinite( delta ) )
799 arcValid = false;
800
801 // v4 is the new center
802 v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
803
804 tmpx = v4.x * u1.x + v4.y * u2.x;
805 tmpy = v4.x * u1.y + v4.y * u2.y;
806 v4.x = tmpx;
807 v4.y = tmpy;
808
809 center = v4 + aCenter;
810
811 if( arcValid )
812 {
813 aArc->SetCenter( center );
814
815 if( movingStart )
816 aArc->SetStart( aStart );
817 else
818 aArc->SetEnd( aEnd );
819 }
820 }
821}
822
823
825 const VECTOR2I& aStart, const VECTOR2I& aMid,
826 const VECTOR2I& aEnd ) const
827{
828 const int c_snapEpsilon_sq = 4;
829
830 VECTOR2I m = ( aStart / 2 + aEnd / 2 );
831 VECTOR2I perp = ( aEnd - aStart ).Perpendicular().Resize( INT_MAX / 2 );
832
833 SEG legal( m - perp, m + perp );
834
835 const SEG testSegments[] = { SEG( aCenter, aCenter + VECTOR2( 1, 0 ) ),
836 SEG( aCenter, aCenter + VECTOR2( 0, 1 ) ) };
837
838 std::vector<VECTOR2I> points = { legal.A, legal.B };
839
840 for( const SEG& seg : testSegments )
841 {
842 OPT_VECTOR2I vec = legal.IntersectLines( seg );
843
844 if( vec && legal.SquaredDistance( *vec ) <= c_snapEpsilon_sq )
845 points.push_back( *vec );
846 }
847
848 OPT_VECTOR2I nearest;
850
851 // Snap by distance between cursor and intersections
852 for( const VECTOR2I& pt : points )
853 {
854 SEG::ecoord d_sq = ( pt - aCenter ).SquaredEuclideanNorm();
855
856 if( d_sq < min_d_sq - c_snapEpsilon_sq )
857 {
858 min_d_sq = d_sq;
859 nearest = pt;
860 }
861 }
862
863 if( nearest )
864 aArc->SetCenter( *nearest );
865}
866
867
879 VECTOR2I& aBotLeft, VECTOR2I& aBotRight,
880 const VECTOR2I& aHole, const VECTOR2I& aHoleSize ) const
881{
882 int minWidth = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
883 int minHeight = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
884
885 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) ) )
886 {
887 if( aHoleSize.x )
888 {
889 // pin edited point to the top/left of the hole
890 aTopLeft.x = std::min( aTopLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
891 aTopLeft.y = std::min( aTopLeft.y, aHole.y - aHoleSize.y / 2 - minHeight );
892 }
893 else
894 {
895 // pin edited point within opposite corner
896 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
897 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
898 }
899
900 // push edited point edges to adjacent corners
901 aTopRight.y = aTopLeft.y;
902 aBotLeft.x = aTopLeft.x;
903 }
904 else if( isModified( m_editPoints->Point( RECT_TOP_RIGHT ) ) )
905 {
906 if( aHoleSize.x )
907 {
908 // pin edited point to the top/right of the hole
909 aTopRight.x = std::max( aTopRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
910 aTopRight.y = std::min( aTopRight.y, aHole.y - aHoleSize.y / 2 - minHeight );
911 }
912 else
913 {
914 // pin edited point within opposite corner
915 aTopRight.x = std::max( aTopRight.x, aBotLeft.x + minWidth );
916 aTopRight.y = std::min( aTopRight.y, aBotLeft.y - minHeight );
917 }
918
919 // push edited point edges to adjacent corners
920 aTopLeft.y = aTopRight.y;
921 aBotRight.x = aTopRight.x;
922 }
923 else if( isModified( m_editPoints->Point( RECT_BOT_LEFT ) ) )
924 {
925 if( aHoleSize.x )
926 {
927 // pin edited point to the bottom/left of the hole
928 aBotLeft.x = std::min( aBotLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
929 aBotLeft.y = std::max( aBotLeft.y, aHole.y + aHoleSize.y / 2 + minHeight );
930 }
931 else
932 {
933 // pin edited point within opposite corner
934 aBotLeft.x = std::min( aBotLeft.x, aTopRight.x - minWidth );
935 aBotLeft.y = std::max( aBotLeft.y, aTopRight.y + minHeight );
936 }
937
938 // push edited point edges to adjacent corners
939 aBotRight.y = aBotLeft.y;
940 aTopLeft.x = aBotLeft.x;
941 }
942 else if( isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
943 {
944 if( aHoleSize.x )
945 {
946 // pin edited point to the bottom/right of the hole
947 aBotRight.x = std::max( aBotRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
948 aBotRight.y = std::max( aBotRight.y, aHole.y + aHoleSize.y / 2 + minHeight );
949 }
950 else
951 {
952 // pin edited point within opposite corner
953 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
954 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
955 }
956
957 // push edited point edges to adjacent corners
958 aBotLeft.y = aBotRight.y;
959 aTopRight.x = aBotRight.x;
960 }
961 else if( isModified( m_editPoints->Line( RECT_TOP ) ) )
962 {
963 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
964 }
965 else if( isModified( m_editPoints->Line( RECT_LEFT ) ) )
966 {
967 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
968 }
969 else if( isModified( m_editPoints->Line( RECT_BOT ) ) )
970 {
971 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
972 }
973 else if( isModified( m_editPoints->Line( RECT_RIGHT ) ) )
974 {
975 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
976 }
977}
978
979
981 const VECTOR2I& aStart, const VECTOR2I& aMid,
982 const VECTOR2I& aEnd,
983 const VECTOR2I& aCursor ) const
984{
985 int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
986 bool movingStart;
987
988 VECTOR2I p1, p2, prev_p1;
989
990 // user is moving p1, we want to move p2 to the new radius.
991
992 if( aStart != aArc->GetStart() )
993 {
994 prev_p1 = aArc->GetStart();
995 p1 = aStart;
996 p2 = aEnd;
997 movingStart = true;
998 }
999 else
1000 {
1001 prev_p1 = aArc->GetEnd();
1002 p1 = aEnd;
1003 p2 = aStart;
1004 movingStart = false;
1005 }
1006
1007 p1 = p1 - aCenter;
1008 p2 = p2 - aCenter;
1009
1010 if( p1.x == 0 && p1.y == 0 )
1011 p1 = prev_p1 - aCenter;
1012
1013 if( p2.x == 0 && p2.y == 0 )
1014 p2 = { 1, 0 };
1015
1016 double radius = p1.EuclideanNorm();
1017
1018 if( radius < minRadius )
1019 radius = minRadius;
1020
1021 p1 = aCenter + p1.Resize( radius );
1022 p2 = aCenter + p2.Resize( radius );
1023
1024 aArc->SetCenter( aCenter );
1025
1026 if( movingStart )
1027 {
1028 aArc->SetStart( p1 );
1029 aArc->SetEnd( p2 );
1030 }
1031 else
1032 {
1033 aArc->SetStart( p2 );
1034 aArc->SetEnd( p1 );
1035 }
1036}
1037
1038
1040 const VECTOR2I& aStart, const VECTOR2I& aMid,
1041 const VECTOR2I& aEnd, const VECTOR2I& aCursor ) const
1042{
1043 int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
1044
1045 SEG chord( aStart, aEnd );
1046
1047 // Now, update the edit point position
1048 // Express the point in a circle-centered coordinate system.
1049 VECTOR2I start = aStart - aCenter;
1050 VECTOR2I end = aEnd - aCenter;
1051
1052 double radius = ( aCursor - aCenter ).EuclideanNorm();
1053
1054 if( radius < minRadius )
1055 radius = minRadius;
1056
1057 start = start.Resize( radius );
1058 end = end.Resize( radius );
1059
1060 start = start + aCenter;
1061 end = end + aCenter;
1062
1063 aArc->SetStart( start );
1064 aArc->SetEnd( end );
1065}
1066
1067
1069 const VECTOR2I& aEnd,
1070 const VECTOR2I& aCursor ) const
1071{
1072 // Let 'm' be the middle point of the chord between the start and end points
1073 VECTOR2I m = ( aStart + aEnd ) / 2;
1074
1075 // Legal midpoints lie on a vector starting just off the chord midpoint and extending out
1076 // past the existing midpoint. We do not allow arc inflection while point editing.
1077 const int JUST_OFF = ( aStart - aEnd ).EuclideanNorm() / 100;
1078 VECTOR2I v = (VECTOR2I) aArc->GetArcMid() - m;
1079 SEG legal( m + v.Resize( JUST_OFF ), m + v.Resize( INT_MAX / 2 ) );
1080 VECTOR2I mid = legal.NearestPoint( aCursor );
1081
1082 aArc->SetArcGeometry( aStart, mid, aEnd );
1083}
1084
1085
1087{
1088 EDA_ITEM* item = m_editPoints->GetParent();
1089
1090 if( !item )
1091 return;
1092
1093 switch( item->Type() )
1094 {
1095 case PCB_BITMAP_T:
1096 {
1097 PCB_BITMAP* bitmap = (PCB_BITMAP*) item;
1098 VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
1099 VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
1100 VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
1101 VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
1102
1103 pinEditedCorner( topLeft, topRight, botLeft, botRight );
1104
1105 double oldWidth = bitmap->GetSize().x;
1106 double newWidth = std::max( topRight.x - topLeft.x, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
1107 double widthRatio = newWidth / oldWidth;
1108
1109 double oldHeight = bitmap->GetSize().y;
1110 double newHeight =
1111 std::max( botLeft.y - topLeft.y, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
1112 double heightRatio = newHeight / oldHeight;
1113
1114 bitmap->SetImageScale( bitmap->GetImageScale() * std::min( widthRatio, heightRatio ) );
1115
1116 break;
1117 }
1118
1119 case PCB_TEXTBOX_T:
1120 case PCB_FP_TEXTBOX_T:
1121 case PCB_SHAPE_T:
1122 case PCB_FP_SHAPE_T:
1123 {
1124 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1125
1126 switch( shape->GetShape() )
1127 {
1128 case SHAPE_T::SEGMENT:
1129 if( isModified( m_editPoints->Point( SEG_START ) ) )
1130 shape->SetStart( m_editPoints->Point( SEG_START ).GetPosition() );
1131 else if( isModified( m_editPoints->Point( SEG_END ) ) )
1132 shape->SetEnd( m_editPoints->Point( SEG_END ).GetPosition() );
1133
1134 break;
1135
1136 case SHAPE_T::RECT:
1137 {
1138 VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
1139 VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
1140 VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
1141 VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
1142
1143 pinEditedCorner( topLeft, topRight, botLeft, botRight );
1144
1145 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1146 || isModified( m_editPoints->Point( RECT_TOP_RIGHT ) )
1147 || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) )
1148 || isModified( m_editPoints->Point( RECT_BOT_LEFT ) ) )
1149 {
1150 shape->SetLeft( topLeft.x );
1151 shape->SetTop( topLeft.y );
1152 shape->SetRight( botRight.x );
1153 shape->SetBottom( botRight.y );
1154 }
1155 else if( isModified( m_editPoints->Line( RECT_TOP ) ) )
1156 {
1157 shape->SetTop( topLeft.y );
1158 }
1159 else if( isModified( m_editPoints->Line( RECT_LEFT ) ) )
1160 {
1161 shape->SetLeft( topLeft.x );
1162 }
1163 else if( isModified( m_editPoints->Line( RECT_BOT ) ) )
1164 {
1165 shape->SetBottom( botRight.y );
1166 }
1167 else if( isModified( m_editPoints->Line( RECT_RIGHT ) ) )
1168 {
1169 shape->SetRight( botRight.x );
1170 }
1171
1172 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1173 {
1174 if( !isModified( m_editPoints->Line( i ) ) )
1175 {
1176 m_editPoints->Line( i ).SetConstraint(
1177 new EC_PERPLINE( m_editPoints->Line( i ) ) );
1178 }
1179 }
1180
1181 break;
1182 }
1183
1184 case SHAPE_T::ARC:
1185 {
1186 VECTOR2I center = m_editPoints->Point( ARC_CENTER ).GetPosition();
1187 VECTOR2I mid = m_editPoints->Point( ARC_MID ).GetPosition();
1188 VECTOR2I start = m_editPoints->Point( ARC_START ).GetPosition();
1189 VECTOR2I end = m_editPoints->Point( ARC_END ).GetPosition();
1190
1191 if( isModified( m_editPoints->Point( ARC_CENTER ) ) )
1192 {
1194 {
1195 editArcCenterKeepEndpoints( shape, center, start, mid, end );
1196 }
1197 else
1198 {
1199 VECTOR2I moveVector = VECTOR2I( center.x, center.y ) - shape->GetCenter();
1200 shape->Move( moveVector );
1201 }
1202 }
1203 else if( isModified( m_editPoints->Point( ARC_MID ) ) )
1204 {
1205 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition( false );
1206
1208 editArcMidKeepEndpoints( shape, start, end, cursorPos );
1209 else
1210 editArcMidKeepCenter( shape, center, start, mid, end, cursorPos );
1211 }
1212 else if( isModified( m_editPoints->Point( ARC_START ) )
1213 || isModified( m_editPoints->Point( ARC_END ) ) )
1214 {
1215 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
1216
1218 editArcEndpointKeepTangent( shape, center, start, mid, end, cursorPos );
1219 else
1220 editArcEndpointKeepCenter( shape, center, start, mid, end, cursorPos );
1221 }
1222
1223 break;
1224 }
1225
1226 case SHAPE_T::CIRCLE:
1227 {
1228 const VECTOR2I& center = m_editPoints->Point( CIRC_CENTER ).GetPosition();
1229 const VECTOR2I& end = m_editPoints->Point( CIRC_END ).GetPosition();
1230
1231 if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
1232 {
1233 VECTOR2I moveVector = VECTOR2I( center.x, center.y ) - shape->GetCenter();
1234 shape->Move( moveVector );
1235 }
1236 else
1237 {
1238 shape->SetEnd( VECTOR2I( end.x, end.y ) );
1239 }
1240
1241 break;
1242 }
1243
1244 case SHAPE_T::POLY:
1245 {
1246 SHAPE_POLY_SET& outline = shape->GetPolyShape();
1247
1248 for( int i = 0; i < outline.TotalVertices(); ++i )
1249 outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
1250
1251 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1252 {
1253 if( !isModified( m_editPoints->Line( i ) ) )
1254 m_editPoints->Line( i ).SetConstraint(
1255 new EC_PERPLINE( m_editPoints->Line( i ) ) );
1256 }
1257
1258 validatePolygon( outline );
1259 break;
1260 }
1261
1262 case SHAPE_T::BEZIER:
1263 if( isModified( m_editPoints->Point( BEZIER_CURVE_START ) ) )
1264 shape->SetStart( m_editPoints->Point( BEZIER_CURVE_START ).GetPosition() );
1266 shape->SetBezierC1( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).GetPosition() );
1268 shape->SetBezierC2( m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).GetPosition() );
1269 else if( isModified( m_editPoints->Point( BEZIER_CURVE_END ) ) )
1270 shape->SetEnd( m_editPoints->Point( BEZIER_CURVE_END ).GetPosition() );
1271
1273 break;
1274
1275 default: // suppress warnings
1276 break;
1277 }
1278
1279 if( FP_SHAPE* fpShape = dynamic_cast<FP_SHAPE*>( item ) )
1280 {
1281 // Update relative coordinates for footprint shapes
1282 fpShape->SetLocalCoord();
1283
1284 if( fpShape->IsAnnotationProxy() )
1285 {
1286 for( PAD* pad : fpShape->GetParentFootprint()->Pads() )
1287 {
1288 if( pad->GetFlags() & ENTERED )
1289 view()->Update( pad );
1290 }
1291 }
1292 }
1293
1294 // Nuke outline font render caches
1295 if( PCB_TEXTBOX* textBox = dynamic_cast<PCB_TEXTBOX*>( item ) )
1296 textBox->ClearRenderCache();
1297 else if( FP_TEXTBOX* fpTextBox = dynamic_cast<FP_TEXTBOX*>( item ) )
1298 fpTextBox->ClearRenderCache();
1299
1300 break;
1301 }
1302
1303 case PCB_PAD_T:
1304 {
1305 PAD* pad = static_cast<PAD*>( item );
1306
1307 switch( pad->GetShape() )
1308 {
1309 case PAD_SHAPE::CIRCLE:
1310 {
1311 VECTOR2I end = m_editPoints->Point( 0 ).GetPosition();
1312 int diameter = (int) EuclideanNorm( end - pad->GetPosition() ) * 2;
1313
1314 pad->SetSize( VECTOR2I( diameter, diameter ) );
1315 break;
1316 }
1317
1318 case PAD_SHAPE::OVAL:
1320 case PAD_SHAPE::RECT:
1323 {
1324 VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
1325 VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
1326 VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
1327 VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
1328 VECTOR2I holeCenter = pad->GetPosition();
1329 VECTOR2I holeSize = pad->GetDrillSize();
1330
1331 pinEditedCorner( topLeft, topRight, botLeft, botRight, holeCenter, holeSize );
1332
1333 if( ( pad->GetOffset().x || pad->GetOffset().y )
1334 || ( pad->GetDrillSize().x && pad->GetDrillSize().y ) )
1335 {
1336 // Keep hole pinned at the current location; adjust the pad around the hole
1337
1338 VECTOR2I center = pad->GetPosition();
1339 int dist[4];
1340
1341 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1342 || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1343 {
1344 dist[0] = center.x - topLeft.x;
1345 dist[1] = center.y - topLeft.y;
1346 dist[2] = botRight.x - center.x;
1347 dist[3] = botRight.y - center.y;
1348 }
1349 else
1350 {
1351 dist[0] = center.x - botLeft.x;
1352 dist[1] = center.y - topRight.y;
1353 dist[2] = topRight.x - center.x;
1354 dist[3] = botLeft.y - center.y;
1355 }
1356
1357 VECTOR2I padSize( dist[0] + dist[2], dist[1] + dist[3] );
1358 VECTOR2I deltaOffset( padSize.x / 2 - dist[2], padSize.y / 2 - dist[3] );
1359
1360 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
1361 std::swap( padSize.x, padSize.y );
1362
1363 RotatePoint( deltaOffset, -pad->GetOrientation() );
1364
1365 pad->SetSize( padSize );
1366 pad->SetOffset( -deltaOffset );
1367 }
1368 else
1369 {
1370 // Keep pad position at the center of the pad shape
1371
1372 int left, top, right, bottom;
1373
1374 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1375 || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1376 {
1377 left = topLeft.x;
1378 top = topLeft.y;
1379 right = botRight.x;
1380 bottom = botRight.y;
1381 }
1382 else
1383 {
1384 left = botLeft.x;
1385 top = topRight.y;
1386 right = topRight.x;
1387 bottom = botLeft.y;
1388 }
1389
1390 VECTOR2I padSize( abs( right - left ), abs( bottom - top ) );
1391
1392 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
1393 std::swap( padSize.x, padSize.y );
1394
1395 pad->SetSize( padSize );
1396 pad->SetPosition( VECTOR2I( ( left + right ) / 2, ( top + bottom ) / 2 ) );
1397 pad->SetLocalCoord();
1398 }
1399 break;
1400 }
1401
1402 default: // suppress warnings
1403 break;
1404 }
1405
1406 break;
1407 }
1408
1409 case PCB_FP_ZONE_T:
1410 case PCB_ZONE_T:
1411 {
1412 ZONE* zone = static_cast<ZONE*>( item );
1413 zone->UnFill();
1414 SHAPE_POLY_SET& outline = *zone->Outline();
1415
1416 for( int i = 0; i < outline.TotalVertices(); ++i )
1417 {
1418 if( outline.CVertex( i ) != m_editPoints->Point( i ).GetPosition() )
1419 zone->SetNeedRefill( true );
1420
1421 outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
1422 }
1423
1424 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1425 {
1426 if( !isModified( m_editPoints->Line( i ) ) )
1427 m_editPoints->Line( i ).SetConstraint( new EC_PERPLINE( m_editPoints->Line( i ) ) );
1428 }
1429
1430 validatePolygon( outline );
1431 zone->HatchBorder();
1432
1433 // TODO Refill zone when KiCad supports auto re-fill
1434 break;
1435 }
1436
1437 case PCB_DIM_ALIGNED_T:
1439 {
1440 PCB_DIM_ALIGNED* dimension = static_cast<PCB_DIM_ALIGNED*>( item );
1441
1442 // Check which point is currently modified and updated dimension's points respectively
1443 if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) )
1444 {
1445 VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetStart() );
1446 VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1447
1448 if( featureLine.Cross( crossBar ) > 0 )
1449 dimension->SetHeight( -featureLine.EuclideanNorm() );
1450 else
1451 dimension->SetHeight( featureLine.EuclideanNorm() );
1452
1453 dimension->Update();
1454 }
1455 else if( isModified( m_editPoints->Point( DIM_CROSSBAREND ) ) )
1456 {
1457 VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
1458 VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1459
1460 if( featureLine.Cross( crossBar ) > 0 )
1461 dimension->SetHeight( -featureLine.EuclideanNorm() );
1462 else
1463 dimension->SetHeight( featureLine.EuclideanNorm() );
1464
1465 dimension->Update();
1466 }
1467 else if( isModified( m_editPoints->Point( DIM_START ) ) )
1468 {
1469 dimension->SetStart( m_editedPoint->GetPosition() );
1470 dimension->Update();
1471
1473 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1474 m_editPoints->Point( DIM_START ) ) );
1475 m_editPoints->Point( DIM_CROSSBAREND ).
1476 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1477 m_editPoints->Point( DIM_END ) ) );
1478 }
1479 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1480 {
1481 dimension->SetEnd( m_editedPoint->GetPosition() );
1482 dimension->Update();
1483
1485 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1486 m_editPoints->Point( DIM_START ) ) );
1487 m_editPoints->Point( DIM_CROSSBAREND ).
1488 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1489 m_editPoints->Point( DIM_END ) ) );
1490 }
1491 else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1492 {
1493 // Force manual mode if we weren't already in it
1495 dimension->SetTextPos( m_editedPoint->GetPosition() );
1496 dimension->Update();
1497 }
1498
1499 break;
1500 }
1501
1504 {
1505 PCB_DIM_ORTHOGONAL* dimension = static_cast<PCB_DIM_ORTHOGONAL*>( item );
1506
1507 if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) ||
1509 {
1510 BOX2I bounds( dimension->GetStart(), dimension->GetEnd() - dimension->GetStart() );
1511
1512 const VECTOR2I& cursorPos = m_editedPoint->GetPosition();
1513
1514 // Find vector from nearest dimension point to edit position
1515 VECTOR2I directionA( cursorPos - dimension->GetStart() );
1516 VECTOR2I directionB( cursorPos - dimension->GetEnd() );
1517 VECTOR2I direction = ( directionA < directionB ) ? directionA : directionB;
1518
1519 bool vert;
1520 VECTOR2D featureLine( cursorPos - dimension->GetStart() );
1521
1522 // Only change the orientation when we move outside the bounds
1523 if( !bounds.Contains( cursorPos ) )
1524 {
1525 // If the dimension is horizontal or vertical, set correct orientation
1526 // otherwise, test if we're left/right of the bounding box or above/below it
1527 if( bounds.GetWidth() == 0 )
1528 vert = true;
1529 else if( bounds.GetHeight() == 0 )
1530 vert = false;
1531 else if( cursorPos.x > bounds.GetLeft() && cursorPos.x < bounds.GetRight() )
1532 vert = false;
1533 else if( cursorPos.y > bounds.GetTop() && cursorPos.y < bounds.GetBottom() )
1534 vert = true;
1535 else
1536 vert = std::abs( direction.y ) < std::abs( direction.x );
1537
1540 }
1541 else
1542 {
1544 }
1545
1546 dimension->SetHeight( vert ? featureLine.x : featureLine.y );
1547 }
1548 else if( isModified( m_editPoints->Point( DIM_START ) ) )
1549 {
1550 dimension->SetStart( m_editedPoint->GetPosition() );
1551 }
1552 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1553 {
1554 dimension->SetEnd( m_editedPoint->GetPosition() );
1555 }
1556 else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1557 {
1558 // Force manual mode if we weren't already in it
1560 dimension->SetTextPos( VECTOR2I( m_editedPoint->GetPosition() ) );
1561 }
1562
1563 dimension->Update();
1564
1565 break;
1566 }
1567
1568 case PCB_DIM_CENTER_T:
1570 {
1571 PCB_DIM_CENTER* dimension = static_cast<PCB_DIM_CENTER*>( item );
1572
1573 if( isModified( m_editPoints->Point( DIM_START ) ) )
1574 dimension->SetStart( m_editedPoint->GetPosition() );
1575 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1576 dimension->SetEnd( m_editedPoint->GetPosition() );
1577
1578 dimension->Update();
1579
1580 break;
1581 }
1582
1583 case PCB_DIM_RADIAL_T:
1585 {
1586 PCB_DIM_RADIAL* dimension = static_cast<PCB_DIM_RADIAL*>( item );
1587
1588 if( isModified( m_editPoints->Point( DIM_START ) ) )
1589 {
1590 dimension->SetStart( m_editedPoint->GetPosition() );
1591 dimension->Update();
1592
1593 m_editPoints->Point( DIM_KNEE ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_START ),
1594 m_editPoints->Point( DIM_END ) ) );
1595 }
1596 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1597 {
1598 VECTOR2I oldKnee = dimension->GetKnee();
1599
1600 dimension->SetEnd( m_editedPoint->GetPosition() );
1601 dimension->Update();
1602
1603 VECTOR2I kneeDelta = dimension->GetKnee() - oldKnee;
1604 dimension->SetTextPos( dimension->GetTextPos() + kneeDelta );
1605 dimension->Update();
1606
1607 m_editPoints->Point( DIM_KNEE ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_START ),
1608 m_editPoints->Point( DIM_END ) ) );
1609 }
1610 else if( isModified( m_editPoints->Point( DIM_KNEE ) ) )
1611 {
1612 VECTOR2I oldKnee = dimension->GetKnee();
1613 VECTOR2I arrowVec = m_editPoints->Point( DIM_KNEE ).GetPosition()
1614 - m_editPoints->Point( DIM_END ).GetPosition();
1615
1616 dimension->SetLeaderLength( arrowVec.EuclideanNorm() );
1617 dimension->Update();
1618
1619 VECTOR2I kneeDelta = dimension->GetKnee() - oldKnee;
1620 dimension->SetTextPos( dimension->GetTextPos() + kneeDelta );
1621 dimension->Update();
1622 }
1623 else if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
1624 {
1625 dimension->SetTextPos( m_editedPoint->GetPosition() );
1626 dimension->Update();
1627 }
1628
1629 break;
1630 }
1631
1632 case PCB_DIM_LEADER_T:
1634 {
1635 PCB_DIM_LEADER* dimension = static_cast<PCB_DIM_LEADER*>( item );
1636
1637 if( isModified( m_editPoints->Point( DIM_START ) ) )
1638 {
1639 dimension->SetStart( (VECTOR2I) m_editedPoint->GetPosition() );
1640 }
1641 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1642 {
1643 VECTOR2I newPoint( m_editedPoint->GetPosition() );
1644 VECTOR2I delta = newPoint - dimension->GetEnd();
1645
1646 dimension->SetEnd( newPoint );
1647 dimension->SetTextPos( dimension->GetTextPos() + delta );
1648 }
1649 else if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
1650 {
1651 dimension->SetTextPos( (VECTOR2I) m_editedPoint->GetPosition() );
1652 }
1653
1654 dimension->Update();
1655
1656 break;
1657 }
1658
1659 default:
1660 break;
1661 }
1662
1663 getView()->Update( item );
1664
1665 frame()->SetMsgPanel( item );
1666}
1667
1668
1670{
1671 return true;
1672}
1673
1674
1676{
1677 if( !m_editPoints )
1678 return;
1679
1680 EDA_ITEM* item = m_editPoints->GetParent();
1681
1682 if( !item )
1683 return;
1684
1685 switch( item->Type() )
1686 {
1687 case PCB_BITMAP_T:
1688 {
1689 PCB_BITMAP* bitmap = (PCB_BITMAP*) item;
1690 VECTOR2I topLeft = bitmap->GetPosition() - bitmap->GetSize() / 2;
1691 VECTOR2I botRight = bitmap->GetPosition() + bitmap->GetSize() / 2;
1692
1693 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( topLeft );
1694 m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
1695 m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
1696 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( botRight );
1697
1698 break;
1699 }
1700
1701 case PCB_TEXTBOX_T:
1702 case PCB_FP_TEXTBOX_T:
1703 {
1704 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
1705 int target = shape->GetShape() == SHAPE_T::RECT ? 4 : 0;
1706
1707 // Careful; textbox shape is mutable between cardinal and non-cardinal rotations...
1708 if( int( m_editPoints->PointsSize() ) != target )
1709 {
1710 getView()->Remove( m_editPoints.get() );
1711 m_editedPoint = nullptr;
1712
1713 m_editPoints = makePoints( item );
1714
1715 if( m_editPoints )
1716 getView()->Add( m_editPoints.get() );
1717
1718 break;
1719 }
1720
1721 if( shape->GetShape() == SHAPE_T::RECT )
1722 {
1723 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shape->GetTopLeft() );
1724 m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( shape->GetBotRight().x,
1725 shape->GetTopLeft().y );
1726 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shape->GetBotRight() );
1727 m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( shape->GetTopLeft().x,
1728 shape->GetBotRight().y );
1729 }
1730 else if( shape->GetShape() == SHAPE_T::POLY )
1731 {
1732 // Not currently editable while rotated.
1733 }
1734
1735 break;
1736 }
1737
1738 case PCB_SHAPE_T:
1739 case PCB_FP_SHAPE_T:
1740 {
1741 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
1742
1743 switch( shape->GetShape() )
1744 {
1745 case SHAPE_T::SEGMENT:
1746 m_editPoints->Point( SEG_START ).SetPosition( shape->GetStart() );
1747 m_editPoints->Point( SEG_END ).SetPosition( shape->GetEnd() );
1748 break;
1749
1750 case SHAPE_T::RECT:
1751 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shape->GetTopLeft() );
1752 m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( shape->GetBotRight().x,
1753 shape->GetTopLeft().y );
1754 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shape->GetBotRight() );
1755 m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( shape->GetTopLeft().x,
1756 shape->GetBotRight().y );
1757 break;
1758
1759 case SHAPE_T::ARC:
1760 m_editPoints->Point( ARC_CENTER ).SetPosition( shape->GetCenter() );
1761 m_editPoints->Point( ARC_START ).SetPosition( shape->GetStart() );
1762 m_editPoints->Point( ARC_MID ).SetPosition( shape->GetArcMid() );
1763 m_editPoints->Point( ARC_END ).SetPosition( shape->GetEnd() );
1764 break;
1765
1766 case SHAPE_T::CIRCLE:
1767 m_editPoints->Point( CIRC_CENTER ).SetPosition( shape->GetCenter() );
1768 m_editPoints->Point( CIRC_END ).SetPosition( shape->GetEnd() );
1769 break;
1770
1771 case SHAPE_T::POLY:
1772 {
1773 std::vector<VECTOR2I> points;
1774 shape->DupPolyPointsList( points );
1775
1776 if( m_editPoints->PointsSize() != (unsigned) points.size() )
1777 {
1778 getView()->Remove( m_editPoints.get() );
1779 m_editedPoint = nullptr;
1780
1781 m_editPoints = makePoints( item );
1782
1783 if( m_editPoints )
1784 getView()->Add( m_editPoints.get() );
1785 }
1786 else
1787 {
1788 for( unsigned i = 0; i < points.size(); i++ )
1789 m_editPoints->Point( i ).SetPosition( points[i] );
1790 }
1791
1792 break;
1793 }
1794
1795 case SHAPE_T::BEZIER:
1796 m_editPoints->Point( BEZIER_CURVE_START ).SetPosition( shape->GetStart() );
1797 m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT1 ).SetPosition( shape->GetBezierC1() );
1798 m_editPoints->Point( BEZIER_CURVE_CONTROL_POINT2 ).SetPosition( shape->GetBezierC2() );
1799 m_editPoints->Point( BEZIER_CURVE_END ).SetPosition( shape->GetEnd() );
1800 break;
1801
1802 default: // suppress warnings
1803 break;
1804 }
1805
1806 break;
1807 }
1808
1809 case PCB_PAD_T:
1810 {
1811 const PAD* pad = static_cast<const PAD*>( item );
1812 bool locked = pad->GetParent() && pad->IsLocked();
1813 VECTOR2I shapePos = pad->ShapePos();
1814 VECTOR2I halfSize( pad->GetSize().x / 2, pad->GetSize().y / 2 );
1815
1816 switch( pad->GetShape() )
1817 {
1818 case PAD_SHAPE::CIRCLE:
1819 {
1820 int target = locked ? 0 : 1;
1821
1822 // Careful; pad shape is mutable...
1823 if( int( m_editPoints->PointsSize() ) != target )
1824 {
1825 getView()->Remove( m_editPoints.get() );
1826 m_editedPoint = nullptr;
1827
1828 m_editPoints = makePoints( item );
1829
1830 if( m_editPoints )
1831 getView()->Add( m_editPoints.get() );
1832 }
1833 else if( target == 1 )
1834 {
1835 shapePos.x += halfSize.x;
1836 m_editPoints->Point( 0 ).SetPosition( shapePos );
1837 }
1838 }
1839 break;
1840
1841 case PAD_SHAPE::OVAL:
1843 case PAD_SHAPE::RECT:
1846 {
1847 // Careful; pad shape and orientation are mutable...
1848 int target = locked || !pad->GetOrientation().IsCardinal() ? 0 : 4;
1849
1850 if( int( m_editPoints->PointsSize() ) != target )
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 if( target == 4 )
1861 {
1862 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
1863 std::swap( halfSize.x, halfSize.y );
1864
1865 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shapePos - halfSize );
1867 .SetPosition(
1868 VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
1869 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shapePos + halfSize );
1870 m_editPoints->Point( RECT_BOT_LEFT )
1871 .SetPosition(
1872 VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
1873 }
1874
1875 break;
1876 }
1877
1878 default: // suppress warnings
1879 break;
1880 }
1881 }
1882 break;
1883
1884 case PCB_FP_ZONE_T:
1885 case PCB_ZONE_T:
1886 {
1887 ZONE* zone = static_cast<ZONE*>( item );
1888 const SHAPE_POLY_SET* outline = zone->Outline();
1889
1890 if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
1891 {
1892 getView()->Remove( m_editPoints.get() );
1893 m_editedPoint = nullptr;
1894
1895 m_editPoints = makePoints( item );
1896
1897 if( m_editPoints )
1898 getView()->Add( m_editPoints.get() );
1899 }
1900 else
1901 {
1902 for( int i = 0; i < outline->TotalVertices(); ++i )
1903 m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
1904 }
1905
1906 break;
1907 }
1908
1909 case PCB_DIM_ALIGNED_T:
1913 {
1914 const PCB_DIM_ALIGNED* dimension = static_cast<const PCB_DIM_ALIGNED*>( item );
1915
1916 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1917 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1918 m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->GetTextPos() );
1919 m_editPoints->Point( DIM_CROSSBARSTART ).SetPosition( dimension->GetCrossbarStart() );
1920 m_editPoints->Point( DIM_CROSSBAREND ).SetPosition( dimension->GetCrossbarEnd() );
1921 break;
1922 }
1923
1924 case PCB_DIM_CENTER_T:
1926 {
1927 const PCB_DIM_CENTER* dimension = static_cast<const PCB_DIM_CENTER*>( item );
1928
1929 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1930 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1931 break;
1932 }
1933
1934 case PCB_DIM_RADIAL_T:
1936 {
1937 const PCB_DIM_RADIAL* dimension = static_cast<const PCB_DIM_RADIAL*>( item );
1938
1939 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1940 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1941 m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->GetTextPos() );
1942 m_editPoints->Point( DIM_KNEE ).SetPosition( dimension->GetKnee() );
1943 break;
1944 }
1945
1946 case PCB_DIM_LEADER_T:
1948 {
1949 const PCB_DIM_LEADER* dimension = static_cast<const PCB_DIM_LEADER*>( item );
1950
1951 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
1952 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
1953 m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->GetTextPos() );
1954 break;
1955 }
1956
1957 default:
1958 break;
1959 }
1960
1961 getView()->Update( m_editPoints.get() );
1962}
1963
1964
1966{
1968
1969 if( aPoint )
1970 {
1972 controls->ForceCursorPosition( true, aPoint->GetPosition() );
1973 controls->ShowCursor( true );
1974 }
1975 else
1976 {
1977 if( frame()->ToolStackIsEmpty() )
1978 controls->ShowCursor( false );
1979
1980 controls->ForceCursorPosition( false );
1981 }
1982
1983 m_editedPoint = aPoint;
1984}
1985
1986
1988{
1989 if( aEnabled )
1990 {
1991 EDA_ITEM* parent = m_editPoints->GetParent();
1992 EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
1993 bool isPoly;
1994
1995 switch( parent->Type() )
1996 {
1997 case PCB_ZONE_T:
1998 case PCB_FP_ZONE_T:
1999 isPoly = true;
2000 break;
2001
2002 case PCB_SHAPE_T:
2003 case PCB_FP_SHAPE_T:
2004 isPoly = static_cast<PCB_SHAPE*>( parent )->GetShape() == SHAPE_T::POLY;
2005 break;
2006
2007 default:
2008 isPoly = false;
2009 break;
2010 }
2011
2012 if( line && isPoly )
2013 {
2014 EC_CONVERGING* altConstraint = new EC_CONVERGING( *line, *m_editPoints );
2015 m_altConstraint.reset( (EDIT_CONSTRAINT<EDIT_POINT>*) altConstraint );
2016 }
2017 else
2018 {
2019 // Find a proper constraining point for 45 degrees mode
2022 }
2023 }
2024 else
2025 {
2026 m_altConstraint.reset();
2027 }
2028}
2029
2030
2032{
2033 EDA_ITEM* item = m_editPoints->GetParent();
2034
2035 switch( item->Type() )
2036 {
2037 case PCB_SHAPE_T:
2038 case PCB_FP_SHAPE_T:
2039 switch( static_cast<const PCB_SHAPE*>( item )->GetShape() )
2040 {
2041 case SHAPE_T::SEGMENT:
2042 return *( m_editPoints->Next( *m_editedPoint ) ); // select the other end of line
2043
2044 case SHAPE_T::ARC:
2045 case SHAPE_T::CIRCLE:
2046 return m_editPoints->Point( CIRC_CENTER );
2047
2048 default: // suppress warnings
2049 break;
2050 }
2051
2052 break;
2053
2054 case PCB_DIM_ALIGNED_T:
2056 {
2057 // Constraint for crossbar
2058 if( isModified( m_editPoints->Point( DIM_START ) ) )
2059 return m_editPoints->Point( DIM_END );
2060
2061 else if( isModified( m_editPoints->Point( DIM_END ) ) )
2062 return m_editPoints->Point( DIM_START );
2063
2064 else
2065 return EDIT_POINT( m_editedPoint->GetPosition() ); // no constraint
2066
2067 break;
2068 }
2069
2070 case PCB_DIM_CENTER_T:
2072 {
2073 if( isModified( m_editPoints->Point( DIM_END ) ) )
2074 return m_editPoints->Point( DIM_START );
2075
2076 break;
2077 }
2078
2079 case PCB_DIM_RADIAL_T:
2081 {
2082 if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
2083 return m_editPoints->Point( DIM_KNEE );
2084
2085 break;
2086 }
2087
2088 default:
2089 break;
2090 }
2091
2092 // In any other case we may align item to its original position
2093 return m_original;
2094}
2095
2096
2098{
2099 const auto type = aItem.Type();
2100
2101 // Works only for zones and line segments
2102 if( type == PCB_ZONE_T || type == PCB_FP_ZONE_T )
2103 return true;
2104
2105 if( type == PCB_SHAPE_T || type == PCB_FP_SHAPE_T )
2106 {
2107 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
2108 return shape.GetShape() == SHAPE_T::SEGMENT || shape.GetShape() == SHAPE_T::POLY;
2109 }
2110
2111 return false;
2112}
2113
2114
2116{
2117 if( aSelection.Size() != 1 )
2118 return false;
2119
2120 const EDA_ITEM* item = aSelection.Front();
2121
2122 return ( item != nullptr ) && canAddCorner( *item );
2123}
2124
2125
2126// Finds a corresponding vertex in a polygon set
2127static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
2128findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
2129{
2130 for( auto it = aPolySet.IterateWithHoles(); it; ++it )
2131 {
2132 auto vertexIdx = it.GetIndex();
2133
2134 if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
2135 return std::make_pair( true, vertexIdx );
2136 }
2137
2138 return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
2139}
2140
2141
2143{
2144 if( !m_editPoints || !m_editedPoint )
2145 return false;
2146
2147 EDA_ITEM* item = m_editPoints->GetParent();
2148 SHAPE_POLY_SET* polyset = nullptr;
2149
2150 if( !item )
2151 return false;
2152
2153 switch( item->Type() )
2154 {
2155 case PCB_ZONE_T:
2156 case PCB_FP_ZONE_T:
2157 polyset = static_cast<ZONE*>( item )->Outline();
2158 break;
2159
2160 case PCB_SHAPE_T:
2161 case PCB_FP_SHAPE_T:
2162 if( static_cast<PCB_SHAPE*>( item )->GetShape() == SHAPE_T::POLY )
2163 polyset = &static_cast<PCB_SHAPE*>( item )->GetPolyShape();
2164 else
2165 return false;
2166
2167 break;
2168
2169 default:
2170 return false;
2171 }
2172
2173 std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX> vertex = findVertex( *polyset, *m_editedPoint );
2174
2175 if( !vertex.first )
2176 return false;
2177
2178 const SHAPE_POLY_SET::VERTEX_INDEX& vertexIdx = vertex.second;
2179
2180 // Check if there are enough vertices so one can be removed without
2181 // degenerating the polygon.
2182 // The first condition allows one to remove all corners from holes (when
2183 // there are only 2 vertices left, a hole is removed).
2184 if( vertexIdx.m_contour == 0 &&
2185 polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
2186 {
2187 return false;
2188 }
2189
2190 // Remove corner does not work with lines
2191 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2192 return false;
2193
2194 return m_editedPoint != nullptr;
2195}
2196
2197
2199{
2200 if( !m_editPoints )
2201 return 0;
2202
2203 EDA_ITEM* item = m_editPoints->GetParent();
2204 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2205 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
2206
2207 // called without an active edited polygon
2208 if( !item || !canAddCorner( *item ) )
2209 return 0;
2210
2211 PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
2212 BOARD_COMMIT commit( frame );
2213
2214 if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T
2215 || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
2216 {
2217 unsigned int nearestIdx = 0;
2218 unsigned int nextNearestIdx = 0;
2219 unsigned int nearestDist = INT_MAX;
2220 unsigned int firstPointInContour = 0;
2221 SHAPE_POLY_SET* zoneOutline;
2222
2223 if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2224 {
2225 ZONE* zone = static_cast<ZONE*>( item );
2226 zoneOutline = zone->Outline();
2227 zone->SetNeedRefill( true );
2228 }
2229 else
2230 {
2231 zoneOutline = &( graphicItem->GetPolyShape() );
2232 }
2233
2234 commit.Modify( item );
2235
2236 // Search the best outline segment to add a new corner
2237 // and therefore break this segment into two segments
2238
2239 // Object to iterate through the corners of the outlines (main contour and its holes)
2240 SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0, zoneOutline->OutlineCount()-1,
2241 /* IterateHoles */ true );
2242 int curr_idx = 0;
2243
2244 // Iterate through all the corners of the outlines and search the best segment
2245 for( ; iterator; iterator++, curr_idx++ )
2246 {
2247 int jj = curr_idx+1;
2248
2249 if( iterator.IsEndContour() )
2250 { // We reach the last point of the current contour (main or hole)
2251 jj = firstPointInContour;
2252 firstPointInContour = curr_idx+1; // Prepare next contour analysis
2253 }
2254
2255 SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
2256
2257 unsigned int distance = curr_segment.Distance( cursorPos );
2258
2259 if( distance < nearestDist )
2260 {
2261 nearestDist = distance;
2262 nearestIdx = curr_idx;
2263 nextNearestIdx = jj;
2264 }
2265 }
2266
2267 // Find the point on the closest segment
2268 const VECTOR2I& sideOrigin = zoneOutline->CVertex( nearestIdx );
2269 const VECTOR2I& sideEnd = zoneOutline->CVertex( nextNearestIdx );
2270 SEG nearestSide( sideOrigin, sideEnd );
2271 VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
2272
2273 // Do not add points that have the same coordinates as ones that already belong to polygon
2274 // instead, add a point in the middle of the side
2275 if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
2276 nearestPoint = ( sideOrigin + sideEnd ) / 2;
2277
2278 zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
2279
2280 // We re-hatch the filled zones but not polygons
2281 if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2282 static_cast<ZONE*>( item )->HatchBorder();
2283
2284
2285 commit.Push( _( "Add a zone corner" ) );
2286 }
2287 else if( graphicItem && graphicItem->GetShape() == SHAPE_T::SEGMENT )
2288 {
2289 commit.Modify( graphicItem );
2290
2291 SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
2292 VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
2293
2294 // Move the end of the line to the break point..
2295 graphicItem->SetEnd( VECTOR2I( nearestPoint.x, nearestPoint.y ) );
2296
2297 if( graphicItem->Type() == PCB_FP_SHAPE_T )
2298 static_cast<FP_SHAPE*>( graphicItem )->SetLocalCoord();
2299
2300 // and add another one starting from the break point
2301 PCB_SHAPE* newSegment;
2302
2303 if( item->Type() == PCB_FP_SHAPE_T )
2304 {
2305 FP_SHAPE* edge = static_cast<FP_SHAPE*>( graphicItem );
2306 assert( edge->GetParent()->Type() == PCB_FOOTPRINT_T );
2307 newSegment = new FP_SHAPE( *edge );
2308 }
2309 else
2310 {
2311 newSegment = new PCB_SHAPE( *graphicItem );
2312 }
2313
2314 newSegment->ClearSelected();
2315 newSegment->SetStart( VECTOR2I( nearestPoint.x, nearestPoint.y ) );
2316 newSegment->SetEnd( VECTOR2I( seg.B.x, seg.B.y ) );
2317
2318 if( newSegment->Type() == PCB_FP_SHAPE_T )
2319 static_cast<FP_SHAPE*>( newSegment )->SetLocalCoord();
2320
2321 commit.Add( newSegment );
2322 commit.Push( _( "Split segment" ) );
2323 }
2324
2325 updatePoints();
2326 return 0;
2327}
2328
2329
2331{
2332 if( !m_editPoints || !m_editedPoint )
2333 return 0;
2334
2335 EDA_ITEM* item = m_editPoints->GetParent();
2336
2337 if( !item )
2338 return 0;
2339
2340 SHAPE_POLY_SET* polygon = nullptr;
2341
2342 if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2343 {
2344 ZONE* zone = static_cast<ZONE*>( item );
2345 polygon = zone->Outline();
2346 zone->SetNeedRefill( true );
2347 }
2348 else if( item->Type() == PCB_FP_SHAPE_T || item->Type() == PCB_SHAPE_T )
2349 {
2350 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2351
2352 if( shape->GetShape() == SHAPE_T::POLY )
2353 polygon = &shape->GetPolyShape();
2354 }
2355
2356 if( !polygon )
2357 return 0;
2358
2359 PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
2360 BOARD_COMMIT commit( frame );
2361 auto vertex = findVertex( *polygon, *m_editedPoint );
2362
2363 if( vertex.first )
2364 {
2365 const auto& vertexIdx = vertex.second;
2366 auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
2367
2368 if( outline.PointCount() > 3 )
2369 {
2370 // the usual case: remove just the corner when there are >3 vertices
2371 commit.Modify( item );
2372 polygon->RemoveVertex( vertexIdx );
2373 validatePolygon( *polygon );
2374 }
2375 else
2376 {
2377 // either remove a hole or the polygon when there are <= 3 corners
2378 if( vertexIdx.m_contour > 0 )
2379 {
2380 // remove hole
2381 commit.Modify( item );
2382 polygon->RemoveContour( vertexIdx.m_contour );
2383 }
2384 else
2385 {
2387 commit.Remove( item );
2388 }
2389 }
2390
2391 setEditedPoint( nullptr );
2392
2393 commit.Push( _( "Remove a zone/polygon corner" ) );
2394
2395 // Refresh zone hatching
2396 if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2397 static_cast<ZONE*>( item )->HatchBorder();
2398
2399 updatePoints();
2400 }
2401
2402 return 0;
2403}
2404
2405
2407{
2408 updatePoints();
2409 return 0;
2410}
2411
2412
2414{
2415 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2416
2418 {
2419 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2421 else
2423
2424 switch( m_arcEditMode )
2425 {
2428 break;
2431 break;
2432 }
2433 }
2434 else
2435 {
2437 }
2438
2439 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2441 else
2443
2444 return 0;
2445}
2446
2447
2449{
2463}
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:174
static TOOL_ACTION activatePointEditor
Definition: actions.h:173
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:70
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:197
virtual bool IsLocked() const
Definition: board_item.cpp:71
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:175
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.
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:145
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
void ClearSelected()
Definition: eda_item.h:118
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:470
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:1225
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:555
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:488
const VECTOR2I & GetTextPos() const
Definition: eda_text.h:208
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:373
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:220
static const TOOL_EVENT SelectedEvent
Definition: actions.h:207
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:214
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition: actions.h:221
static const TOOL_EVENT PointSelectedEvent
Definition: actions.h:206
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:217
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:208
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:349
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:1591
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:532
static LSET AllLayersMask()
Definition: lset.cpp:808
Definition: pad.h:60
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:127
void SetImageScale(double aScale)
Definition: pcb_bitmap.h:77
double GetImageScale() const
Definition: pcb_bitmap.h:75
const VECTOR2I GetSize() const
Definition: pcb_bitmap.cpp:160
void Update()
Update the dimension's cached text and geometry.
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.
void editArcCenterKeepEndpoints(PCB_SHAPE *aArc, const VECTOR2I &aCenter, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd) const
Move the arc center but keep endpoint locations.
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
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 A
Definition: seg.h:49
ecoord SquaredDistance(const SEG &aSeg) const
Definition: seg.cpp:75
VECTOR2I::extended_type ecoord
Definition: seg.h:44
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:269
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition: seg.h:210
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition: seg.cpp:329
EDA_ITEM * Front() const
Definition: selection.h:208
int Size() const
Returns the number of selected parts.
Definition: selection.h:115
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:215
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
static constexpr extended_type ECOORD_MAX
Definition: vector2d.h:75
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:265
extended_type Cross(const VECTOR2< T > &aVector) const
Compute cross product of self with aVector.
Definition: vector2d.h:459
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:350
T y
Definition: vector3.h:62
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector3.h:146
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:215
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:861
SHAPE_POLY_SET * Outline()
Definition: zone.h:318
@ 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:431
static constexpr EDA_ANGLE & ANGLE_270
Definition: eda_angle.h:434
#define ENTERED
indicates a group has been entered
@ ALL_LAYERS
@ OBJECT_LAYERS
@ IGNORE_SNAPS
@ SNAP_BY_GRID
@ SNAP_TO_GRID
ARC_POINTS
CIRCLE_POINTS
@ FRAME_PCB_EDITOR
Definition: frame_type.h:40
double m_DrawArcCenterMaxAngle
When drawing an arc, the angle ( center - start ) - ( start - end ) can be limited to avoid extremely...
constexpr int Mils2IU(const EDA_IU_SCALE &aIuScale, int mils)
Definition: eda_units.h:122
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:418
@ 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)
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
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...
VECTOR3I v1(5, 5, 5)
VECTOR2I v2(1, 0)
Test suite for KiCad math code.
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:589
VECTOR2< int > VECTOR2I
Definition: vector2d.h:590