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