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