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( new EC_PERPLINE( m_editPoints->Line( i ) ) );
1393 }
1394
1395 break;
1396 }
1397
1398 case SHAPE_T::BEZIER:
1399 if( isModified( m_editPoints->Point( BEZIER_START ) ) )
1400 shape->SetStart( m_editPoints->Point( BEZIER_START ).GetPosition() );
1401 else if( isModified( m_editPoints->Point( BEZIER_CTRL_PT1 ) ) )
1402 shape->SetBezierC1( m_editPoints->Point( BEZIER_CTRL_PT1 ).GetPosition() );
1403 else if( isModified( m_editPoints->Point( BEZIER_CTRL_PT2 ) ) )
1404 shape->SetBezierC2( m_editPoints->Point( BEZIER_CTRL_PT2 ).GetPosition() );
1405 else if( isModified( m_editPoints->Point( BEZIER_END ) ) )
1406 shape->SetEnd( m_editPoints->Point( BEZIER_END ).GetPosition() );
1407
1409 break;
1410
1411 default: // suppress warnings
1412 break;
1413 }
1414
1415 if( shape->IsProxyItem() )
1416 {
1417 for( PAD* pad : shape->GetParentFootprint()->Pads() )
1418 {
1419 if( pad->IsEntered() )
1420 view()->Update( pad );
1421 }
1422 }
1423
1424 // Nuke outline font render caches
1425 if( PCB_TEXTBOX* textBox = dynamic_cast<PCB_TEXTBOX*>( item ) )
1426 textBox->ClearRenderCache();
1427
1428 break;
1429 }
1430
1431 case PCB_TABLECELL_T:
1432 {
1433 PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( item );
1434 PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
1435
1436 if( isModified( m_editPoints->Point( COL_WIDTH ) ) )
1437 {
1438 cell->SetEnd( VECTOR2I( m_editPoints->Point( 0 ).GetX(), cell->GetEndY() ) );
1439
1440 int colWidth = cell->GetRectangleWidth();
1441
1442 for( int ii = 0; ii < cell->GetColSpan() - 1; ++ii )
1443 colWidth -= table->GetColWidth( cell->GetColumn() + ii );
1444
1445 table->SetColWidth( cell->GetColumn() + cell->GetColSpan() - 1, colWidth );
1446 table->Normalize();
1447 }
1448 else if( isModified( m_editPoints->Point( ROW_HEIGHT ) ) )
1449 {
1450 cell->SetEnd( VECTOR2I( cell->GetEndX(), m_editPoints->Point( 1 ).GetY() ) );
1451
1452 int rowHeight = cell->GetRectangleHeight();
1453
1454 for( int ii = 0; ii < cell->GetRowSpan() - 1; ++ii )
1455 rowHeight -= table->GetRowHeight( cell->GetRow() + ii );
1456
1457 table->SetRowHeight( cell->GetRow() + cell->GetRowSpan() - 1, rowHeight );
1458 table->Normalize();
1459 }
1460
1461 getView()->Update( table );
1462 break;
1463 }
1464
1465 case PCB_PAD_T:
1466 {
1467 PAD* pad = static_cast<PAD*>( item );
1468
1469 switch( pad->GetShape() )
1470 {
1471 case PAD_SHAPE::CIRCLE:
1472 {
1473 VECTOR2I end = m_editPoints->Point( 0 ).GetPosition();
1474 int diameter = 2 * ( end - pad->GetPosition() ).EuclideanNorm();
1475
1476 pad->SetSize( VECTOR2I( diameter, diameter ) );
1477 break;
1478 }
1479
1480 case PAD_SHAPE::OVAL:
1481 case PAD_SHAPE::TRAPEZOID:
1482 case PAD_SHAPE::RECTANGLE:
1483 case PAD_SHAPE::ROUNDRECT:
1484 case PAD_SHAPE::CHAMFERED_RECT:
1485 {
1486 VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
1487 VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
1488 VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
1489 VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
1490 VECTOR2I holeCenter = pad->GetPosition();
1491 VECTOR2I holeSize = pad->GetDrillSize();
1492
1493 pinEditedCorner( topLeft, topRight, botLeft, botRight, holeCenter, holeSize );
1494
1495 if( ( pad->GetOffset().x || pad->GetOffset().y )
1496 || ( pad->GetDrillSize().x && pad->GetDrillSize().y ) )
1497 {
1498 // Keep hole pinned at the current location; adjust the pad around the hole
1499
1500 VECTOR2I center = pad->GetPosition();
1501 int dist[4];
1502
1503 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1504 || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1505 {
1506 dist[0] = center.x - topLeft.x;
1507 dist[1] = center.y - topLeft.y;
1508 dist[2] = botRight.x - center.x;
1509 dist[3] = botRight.y - center.y;
1510 }
1511 else
1512 {
1513 dist[0] = center.x - botLeft.x;
1514 dist[1] = center.y - topRight.y;
1515 dist[2] = topRight.x - center.x;
1516 dist[3] = botLeft.y - center.y;
1517 }
1518
1519 VECTOR2I padSize( dist[0] + dist[2], dist[1] + dist[3] );
1520 VECTOR2I deltaOffset( padSize.x / 2 - dist[2], padSize.y / 2 - dist[3] );
1521
1522 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
1523 std::swap( padSize.x, padSize.y );
1524
1525 RotatePoint( deltaOffset, -pad->GetOrientation() );
1526
1527 pad->SetSize( padSize );
1528 pad->SetOffset( -deltaOffset );
1529 }
1530 else
1531 {
1532 // Keep pad position at the center of the pad shape
1533
1534 int left, top, right, bottom;
1535
1536 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1537 || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1538 {
1539 left = topLeft.x;
1540 top = topLeft.y;
1541 right = botRight.x;
1542 bottom = botRight.y;
1543 }
1544 else
1545 {
1546 left = botLeft.x;
1547 top = topRight.y;
1548 right = topRight.x;
1549 bottom = botLeft.y;
1550 }
1551
1552 VECTOR2I padSize( abs( right - left ), abs( bottom - top ) );
1553
1554 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
1555 std::swap( padSize.x, padSize.y );
1556
1557 pad->SetSize( padSize );
1558 pad->SetPosition( VECTOR2I( ( left + right ) / 2, ( top + bottom ) / 2 ) );
1559 }
1560 break;
1561 }
1562
1563 default: // suppress warnings
1564 break;
1565 }
1566
1567 break;
1568 }
1569
1570 case PCB_ZONE_T:
1571 {
1572 ZONE* zone = static_cast<ZONE*>( item );
1573 zone->UnFill();
1574 SHAPE_POLY_SET& outline = *zone->Outline();
1575
1576 for( int i = 0; i < outline.TotalVertices(); ++i )
1577 {
1578 if( outline.CVertex( i ) != m_editPoints->Point( i ).GetPosition() )
1579 zone->SetNeedRefill( true );
1580
1581 outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
1582 }
1583
1584 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1585 {
1586 if( !isModified( m_editPoints->Line( i ) ) )
1587 m_editPoints->Line( i ).SetConstraint( new EC_PERPLINE( m_editPoints->Line( i ) ) );
1588 }
1589
1590 zone->HatchBorder();
1591 break;
1592 }
1593
1594 case PCB_GENERATOR_T:
1595 {
1596 GENERATOR_TOOL* generatorTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
1597 PCB_GENERATOR* generatorItem = static_cast<PCB_GENERATOR*>( item );
1598
1599 generatorItem->UpdateFromEditPoints( m_editPoints, aCommit );
1600 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genUpdateEdit, aCommit, generatorItem );
1601
1602 // Note: POINT_EDITOR::m_preview holds only the canvas-draw status "popup"; the meanders
1603 // themselves (ROUTER_PREVIEW_ITEMs) are owned by the router.
1604
1606
1607 for( EDA_ITEM* previewItem : generatorItem->GetPreviewItems( generatorTool, frame(),
1609 {
1610 m_preview.Add( previewItem );
1611 }
1612
1613 getView()->Update( &m_preview );
1614 break;
1615 }
1616
1617 case PCB_DIM_ALIGNED_T:
1618 {
1619 PCB_DIM_ALIGNED* dimension = static_cast<PCB_DIM_ALIGNED*>( item );
1620
1621 // Check which point is currently modified and updated dimension's points respectively
1622 if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) )
1623 {
1624 VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetStart() );
1625 VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1626
1627 if( featureLine.Cross( crossBar ) > 0 )
1628 dimension->SetHeight( -featureLine.EuclideanNorm() );
1629 else
1630 dimension->SetHeight( featureLine.EuclideanNorm() );
1631
1632 dimension->Update();
1633 }
1634 else if( isModified( m_editPoints->Point( DIM_CROSSBAREND ) ) )
1635 {
1636 VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
1637 VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1638
1639 if( featureLine.Cross( crossBar ) > 0 )
1640 dimension->SetHeight( -featureLine.EuclideanNorm() );
1641 else
1642 dimension->SetHeight( featureLine.EuclideanNorm() );
1643
1644 dimension->Update();
1645 }
1646 else if( isModified( m_editPoints->Point( DIM_START ) ) )
1647 {
1648 dimension->SetStart( m_editedPoint->GetPosition() );
1649 dimension->Update();
1650
1652 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1653 m_editPoints->Point( DIM_START ) ) );
1654 m_editPoints->Point( DIM_CROSSBAREND ).
1655 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1656 m_editPoints->Point( DIM_END ) ) );
1657 }
1658 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1659 {
1660 dimension->SetEnd( m_editedPoint->GetPosition() );
1661 dimension->Update();
1662
1664 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1665 m_editPoints->Point( DIM_START ) ) );
1666 m_editPoints->Point( DIM_CROSSBAREND ).
1667 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1668 m_editPoints->Point( DIM_END ) ) );
1669 }
1670 else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1671 {
1672 // Force manual mode if we weren't already in it
1673 dimension->SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
1674 dimension->SetTextPos( m_editedPoint->GetPosition() );
1675 dimension->Update();
1676 }
1677
1678 break;
1679 }
1680
1682 {
1683 PCB_DIM_ORTHOGONAL* dimension = static_cast<PCB_DIM_ORTHOGONAL*>( item );
1684
1685 if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) ||
1687 {
1688 BOX2I bounds( dimension->GetStart(), dimension->GetEnd() - dimension->GetStart() );
1689
1690 const VECTOR2I& cursorPos = m_editedPoint->GetPosition();
1691
1692 // Find vector from nearest dimension point to edit position
1693 VECTOR2I directionA( cursorPos - dimension->GetStart() );
1694 VECTOR2I directionB( cursorPos - dimension->GetEnd() );
1695 VECTOR2I direction = ( directionA < directionB ) ? directionA : directionB;
1696
1697 bool vert;
1698 VECTOR2D featureLine( cursorPos - dimension->GetStart() );
1699
1700 // Only change the orientation when we move outside the bounds
1701 if( !bounds.Contains( cursorPos ) )
1702 {
1703 // If the dimension is horizontal or vertical, set correct orientation
1704 // otherwise, test if we're left/right of the bounding box or above/below it
1705 if( bounds.GetWidth() == 0 )
1706 vert = true;
1707 else if( bounds.GetHeight() == 0 )
1708 vert = false;
1709 else if( cursorPos.x > bounds.GetLeft() && cursorPos.x < bounds.GetRight() )
1710 vert = false;
1711 else if( cursorPos.y > bounds.GetTop() && cursorPos.y < bounds.GetBottom() )
1712 vert = true;
1713 else
1714 vert = std::abs( direction.y ) < std::abs( direction.x );
1715
1718 }
1719 else
1720 {
1722 }
1723
1724 dimension->SetHeight( vert ? featureLine.x : featureLine.y );
1725 }
1726 else if( isModified( m_editPoints->Point( DIM_START ) ) )
1727 {
1728 dimension->SetStart( m_editedPoint->GetPosition() );
1729 }
1730 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1731 {
1732 dimension->SetEnd( m_editedPoint->GetPosition() );
1733 }
1734 else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1735 {
1736 // Force manual mode if we weren't already in it
1737 dimension->SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
1738 dimension->SetTextPos( VECTOR2I( m_editedPoint->GetPosition() ) );
1739 }
1740
1741 dimension->Update();
1742
1743 break;
1744 }
1745
1746 case PCB_DIM_CENTER_T:
1747 {
1748 PCB_DIM_CENTER* dimension = static_cast<PCB_DIM_CENTER*>( item );
1749
1750 if( isModified( m_editPoints->Point( DIM_START ) ) )
1751 dimension->SetStart( m_editedPoint->GetPosition() );
1752 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1753 dimension->SetEnd( m_editedPoint->GetPosition() );
1754
1755 dimension->Update();
1756
1757 break;
1758 }
1759
1760 case PCB_DIM_RADIAL_T:
1761 {
1762 PCB_DIM_RADIAL* dimension = static_cast<PCB_DIM_RADIAL*>( item );
1763
1764 if( isModified( m_editPoints->Point( DIM_START ) ) )
1765 {
1766 dimension->SetStart( m_editedPoint->GetPosition() );
1767 dimension->Update();
1768
1769 m_editPoints->Point( DIM_KNEE ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_START ),
1770 m_editPoints->Point( DIM_END ) ) );
1771 }
1772 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1773 {
1774 VECTOR2I oldKnee = dimension->GetKnee();
1775
1776 dimension->SetEnd( m_editedPoint->GetPosition() );
1777 dimension->Update();
1778
1779 VECTOR2I kneeDelta = dimension->GetKnee() - oldKnee;
1780 dimension->SetTextPos( dimension->GetTextPos() + kneeDelta );
1781 dimension->Update();
1782
1783 m_editPoints->Point( DIM_KNEE ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_START ),
1784 m_editPoints->Point( DIM_END ) ) );
1785 }
1786 else if( isModified( m_editPoints->Point( DIM_KNEE ) ) )
1787 {
1788 VECTOR2I oldKnee = dimension->GetKnee();
1789 VECTOR2I arrowVec = m_editPoints->Point( DIM_KNEE ).GetPosition()
1790 - m_editPoints->Point( DIM_END ).GetPosition();
1791
1792 dimension->SetLeaderLength( arrowVec.EuclideanNorm() );
1793 dimension->Update();
1794
1795 VECTOR2I kneeDelta = dimension->GetKnee() - oldKnee;
1796 dimension->SetTextPos( dimension->GetTextPos() + kneeDelta );
1797 dimension->Update();
1798 }
1799 else if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
1800 {
1801 dimension->SetTextPos( m_editedPoint->GetPosition() );
1802 dimension->Update();
1803 }
1804
1805 break;
1806 }
1807
1808 case PCB_DIM_LEADER_T:
1809 {
1810 PCB_DIM_LEADER* dimension = static_cast<PCB_DIM_LEADER*>( item );
1811
1812 if( isModified( m_editPoints->Point( DIM_START ) ) )
1813 {
1814 dimension->SetStart( (VECTOR2I) m_editedPoint->GetPosition() );
1815 }
1816 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1817 {
1818 VECTOR2I newPoint( m_editedPoint->GetPosition() );
1819 VECTOR2I delta = newPoint - dimension->GetEnd();
1820
1821 dimension->SetEnd( newPoint );
1822 dimension->SetTextPos( dimension->GetTextPos() + delta );
1823 }
1824 else if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
1825 {
1826 dimension->SetTextPos( (VECTOR2I) m_editedPoint->GetPosition() );
1827 }
1828
1829 dimension->Update();
1830
1831 break;
1832 }
1833
1834 default:
1835 break;
1836 }
1837
1838 getView()->Update( item );
1839
1840 frame()->SetMsgPanel( item );
1841}
1842
1843
1845{
1846 if( !m_editPoints )
1847 return;
1848
1849 EDA_ITEM* item = m_editPoints->GetParent();
1850
1851 if( !item )
1852 return;
1853
1854 switch( item->Type() )
1855 {
1857 {
1858 PCB_REFERENCE_IMAGE* bitmap = (PCB_REFERENCE_IMAGE*) item;
1859 VECTOR2I topLeft = bitmap->GetPosition() - bitmap->GetSize() / 2;
1860 VECTOR2I botRight = bitmap->GetPosition() + bitmap->GetSize() / 2;
1861
1862 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( topLeft );
1863 m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
1864 m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
1865 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( botRight );
1866
1867 break;
1868 }
1869
1870 case PCB_TEXTBOX_T:
1871 {
1872 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
1873 int target = shape->GetShape() == SHAPE_T::RECTANGLE ? 4 : 0;
1874
1875 // Careful; textbox shape is mutable between cardinal and non-cardinal rotations...
1876 if( int( m_editPoints->PointsSize() ) != target )
1877 {
1878 getView()->Remove( m_editPoints.get() );
1879 m_editedPoint = nullptr;
1880
1881 m_editPoints = makePoints( item );
1882
1883 if( m_editPoints )
1884 getView()->Add( m_editPoints.get() );
1885
1886 break;
1887 }
1888
1889 if( shape->GetShape() == SHAPE_T::RECTANGLE )
1890 {
1891 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shape->GetTopLeft() );
1892 m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( shape->GetBotRight().x,
1893 shape->GetTopLeft().y );
1894 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shape->GetBotRight() );
1895 m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( shape->GetTopLeft().x,
1896 shape->GetBotRight().y );
1897 }
1898 else if( shape->GetShape() == SHAPE_T::POLY )
1899 {
1900 // Not currently editable while rotated.
1901 }
1902
1903 break;
1904 }
1905
1906 case PCB_SHAPE_T:
1907 {
1908 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
1909
1910 switch( shape->GetShape() )
1911 {
1912 case SHAPE_T::SEGMENT:
1913 m_editPoints->Point( SEG_START ).SetPosition( shape->GetStart() );
1914 m_editPoints->Point( SEG_END ).SetPosition( shape->GetEnd() );
1915 break;
1916
1917 case SHAPE_T::RECTANGLE:
1918 {
1919 VECTOR2I topLeft = shape->GetTopLeft();
1920 VECTOR2I botRight = shape->GetBotRight();
1921
1922 m_editPoints->SetSwapX( topLeft.x > botRight.x );
1923 m_editPoints->SetSwapY( topLeft.y > botRight.y );
1924
1925 if( m_editPoints->SwapX() )
1926 std::swap( topLeft.x, botRight.x );
1927
1928 if( m_editPoints->SwapY() )
1929 std::swap( topLeft.y, botRight.y );
1930
1931 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( topLeft );
1932 m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
1933 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( botRight );
1934 m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
1935 break;
1936 }
1937
1938 case SHAPE_T::ARC:
1939 m_editPoints->Point( ARC_CENTER ).SetPosition( shape->GetCenter() );
1940 m_editPoints->Point( ARC_START ).SetPosition( shape->GetStart() );
1941 m_editPoints->Point( ARC_MID ).SetPosition( shape->GetArcMid() );
1942 m_editPoints->Point( ARC_END ).SetPosition( shape->GetEnd() );
1943 break;
1944
1945 case SHAPE_T::CIRCLE:
1946 m_editPoints->Point( CIRC_CENTER ).SetPosition( shape->GetCenter() );
1947 m_editPoints->Point( CIRC_END ).SetPosition( shape->GetEnd() );
1948 break;
1949
1950 case SHAPE_T::POLY:
1951 {
1952 std::vector<VECTOR2I> points;
1953 shape->DupPolyPointsList( points );
1954
1955 if( m_editPoints->PointsSize() != (unsigned) points.size() )
1956 {
1957 getView()->Remove( m_editPoints.get() );
1958 m_editedPoint = nullptr;
1959
1960 m_editPoints = makePoints( item );
1961
1962 if( m_editPoints )
1963 getView()->Add( m_editPoints.get() );
1964 }
1965 else
1966 {
1967 for( unsigned i = 0; i < points.size(); i++ )
1968 m_editPoints->Point( i ).SetPosition( points[i] );
1969 }
1970
1971 break;
1972 }
1973
1974 case SHAPE_T::BEZIER:
1975 m_editPoints->Point( BEZIER_START ).SetPosition( shape->GetStart() );
1976 m_editPoints->Point( BEZIER_CTRL_PT1 ).SetPosition( shape->GetBezierC1() );
1977 m_editPoints->Point( BEZIER_CTRL_PT2 ).SetPosition( shape->GetBezierC2() );
1978 m_editPoints->Point( BEZIER_END ).SetPosition( shape->GetEnd() );
1979 break;
1980
1981 default: // suppress warnings
1982 break;
1983 }
1984
1985 break;
1986 }
1987
1988 case PCB_TABLECELL_T:
1989 {
1990 PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( item );
1991
1992 m_editPoints->Point( 0 ).SetPosition( cell->GetEndX(),
1993 cell->GetEndY() - cell->GetRectangleHeight() / 2 );
1994 m_editPoints->Point( 1 ).SetPosition( cell->GetEndX() - cell->GetRectangleWidth() / 2,
1995 cell->GetEndY() );
1996 break;
1997 }
1998
1999 case PCB_PAD_T:
2000 {
2001 const PAD* pad = static_cast<const PAD*>( item );
2002 bool locked = pad->GetParent() && pad->IsLocked();
2003 VECTOR2I shapePos = pad->ShapePos();
2004 VECTOR2I halfSize( pad->GetSize().x / 2, pad->GetSize().y / 2 );
2005
2006 switch( pad->GetShape() )
2007 {
2008 case PAD_SHAPE::CIRCLE:
2009 {
2010 int target = locked ? 0 : 1;
2011
2012 // Careful; pad shape is mutable...
2013 if( int( m_editPoints->PointsSize() ) != target )
2014 {
2015 getView()->Remove( m_editPoints.get() );
2016 m_editedPoint = nullptr;
2017
2018 m_editPoints = makePoints( item );
2019
2020 if( m_editPoints )
2021 getView()->Add( m_editPoints.get() );
2022 }
2023 else if( target == 1 )
2024 {
2025 shapePos.x += halfSize.x;
2026 m_editPoints->Point( 0 ).SetPosition( shapePos );
2027 }
2028 }
2029 break;
2030
2031 case PAD_SHAPE::OVAL:
2032 case PAD_SHAPE::TRAPEZOID:
2033 case PAD_SHAPE::RECTANGLE:
2034 case PAD_SHAPE::ROUNDRECT:
2035 case PAD_SHAPE::CHAMFERED_RECT:
2036 {
2037 // Careful; pad shape and orientation are mutable...
2038 int target = locked || !pad->GetOrientation().IsCardinal() ? 0 : 4;
2039
2040 if( int( m_editPoints->PointsSize() ) != target )
2041 {
2042 getView()->Remove( m_editPoints.get() );
2043 m_editedPoint = nullptr;
2044
2045 m_editPoints = makePoints( item );
2046
2047 if( m_editPoints )
2048 getView()->Add( m_editPoints.get() );
2049 }
2050 else if( target == 4 )
2051 {
2052 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
2053 std::swap( halfSize.x, halfSize.y );
2054
2055 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shapePos - halfSize );
2057 .SetPosition(
2058 VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
2059 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shapePos + halfSize );
2060 m_editPoints->Point( RECT_BOT_LEFT )
2061 .SetPosition(
2062 VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
2063 }
2064
2065 break;
2066 }
2067
2068 default: // suppress warnings
2069 break;
2070 }
2071 }
2072 break;
2073
2074 case PCB_ZONE_T:
2075 {
2076 ZONE* zone = static_cast<ZONE*>( item );
2077 const SHAPE_POLY_SET* outline = zone->Outline();
2078
2079 if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
2080 {
2081 getView()->Remove( m_editPoints.get() );
2082 m_editedPoint = nullptr;
2083
2084 m_editPoints = makePoints( item );
2085
2086 if( m_editPoints )
2087 getView()->Add( m_editPoints.get() );
2088 }
2089 else
2090 {
2091 for( int i = 0; i < outline->TotalVertices(); ++i )
2092 m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
2093 }
2094
2095 break;
2096 }
2097
2098 case PCB_GENERATOR_T:
2099 {
2100 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( item );
2101 generator->UpdateEditPoints( m_editPoints );
2102 break;
2103 }
2104
2105 case PCB_DIM_ALIGNED_T:
2107 {
2108 const PCB_DIM_ALIGNED* dimension = static_cast<const PCB_DIM_ALIGNED*>( item );
2109
2110 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
2111 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
2112 m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->GetTextPos() );
2113 m_editPoints->Point( DIM_CROSSBARSTART ).SetPosition( dimension->GetCrossbarStart() );
2114 m_editPoints->Point( DIM_CROSSBAREND ).SetPosition( dimension->GetCrossbarEnd() );
2115 break;
2116 }
2117
2118 case PCB_DIM_CENTER_T:
2119 {
2120 const PCB_DIM_CENTER* dimension = static_cast<const PCB_DIM_CENTER*>( item );
2121
2122 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
2123 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
2124 break;
2125 }
2126
2127 case PCB_DIM_RADIAL_T:
2128 {
2129 const PCB_DIM_RADIAL* dimension = static_cast<const PCB_DIM_RADIAL*>( item );
2130
2131 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
2132 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
2133 m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->GetTextPos() );
2134 m_editPoints->Point( DIM_KNEE ).SetPosition( dimension->GetKnee() );
2135 break;
2136 }
2137
2138 case PCB_DIM_LEADER_T:
2139 {
2140 const PCB_DIM_LEADER* dimension = static_cast<const PCB_DIM_LEADER*>( item );
2141
2142 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
2143 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
2144 m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->GetTextPos() );
2145 break;
2146 }
2147
2148 default:
2149 break;
2150 }
2151
2152 getView()->Update( m_editPoints.get() );
2153}
2154
2155
2157{
2159
2160 if( aPoint )
2161 {
2162 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2163 controls->ForceCursorPosition( true, aPoint->GetPosition() );
2164 controls->ShowCursor( true );
2165 }
2166 else
2167 {
2168 if( frame()->ToolStackIsEmpty() )
2169 controls->ShowCursor( false );
2170
2171 controls->ForceCursorPosition( false );
2172 }
2173
2174 m_editedPoint = aPoint;
2175}
2176
2177
2179{
2180 if( aEnabled )
2181 {
2182 EDA_ITEM* parent = m_editPoints->GetParent();
2183 EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
2184 bool isPoly;
2185
2186 switch( parent->Type() )
2187 {
2188 case PCB_ZONE_T:
2189 isPoly = true;
2190 break;
2191
2192 case PCB_SHAPE_T:
2193 isPoly = static_cast<PCB_SHAPE*>( parent )->GetShape() == SHAPE_T::POLY;
2194 break;
2195
2196 default:
2197 isPoly = false;
2198 break;
2199 }
2200
2201 if( line && isPoly )
2202 {
2203 EC_CONVERGING* altConstraint = new EC_CONVERGING( *line, *m_editPoints );
2204 m_altConstraint.reset( (EDIT_CONSTRAINT<EDIT_POINT>*) altConstraint );
2205 }
2206 else
2207 {
2208 // Find a proper constraining point for 45 degrees mode
2211 }
2212 }
2213 else
2214 {
2215 m_altConstraint.reset();
2216 }
2217}
2218
2219
2221{
2222 EDA_ITEM* item = m_editPoints->GetParent();
2223
2224 switch( item->Type() )
2225 {
2226 case PCB_SHAPE_T:
2227 switch( static_cast<const PCB_SHAPE*>( item )->GetShape() )
2228 {
2229 case SHAPE_T::SEGMENT:
2230 return *( m_editPoints->Next( *m_editedPoint ) ); // select the other end of line
2231
2232 case SHAPE_T::ARC:
2233 case SHAPE_T::CIRCLE:
2234 return m_editPoints->Point( CIRC_CENTER );
2235
2236 default: // suppress warnings
2237 break;
2238 }
2239
2240 break;
2241
2242 case PCB_DIM_ALIGNED_T:
2243 {
2244 // Constraint for crossbar
2245 if( isModified( m_editPoints->Point( DIM_START ) ) )
2246 return m_editPoints->Point( DIM_END );
2247
2248 else if( isModified( m_editPoints->Point( DIM_END ) ) )
2249 return m_editPoints->Point( DIM_START );
2250
2251 else
2252 return EDIT_POINT( m_editedPoint->GetPosition() ); // no constraint
2253
2254 break;
2255 }
2256
2257 case PCB_DIM_CENTER_T:
2258 {
2259 if( isModified( m_editPoints->Point( DIM_END ) ) )
2260 return m_editPoints->Point( DIM_START );
2261
2262 break;
2263 }
2264
2265 case PCB_DIM_RADIAL_T:
2266 {
2267 if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
2268 return m_editPoints->Point( DIM_KNEE );
2269
2270 break;
2271 }
2272
2273 default:
2274 break;
2275 }
2276
2277 // In any other case we may align item to its original position
2278 return m_original;
2279}
2280
2281
2283{
2284 const auto type = aItem.Type();
2285
2286 // Works only for zones and line segments
2287 if( type == PCB_ZONE_T )
2288 return true;
2289
2290 if( type == PCB_SHAPE_T )
2291 {
2292 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
2293 return shape.GetShape() == SHAPE_T::SEGMENT || shape.GetShape() == SHAPE_T::POLY;
2294 }
2295
2296 return false;
2297}
2298
2299
2301{
2302 if( aSelection.Size() != 1 )
2303 return false;
2304
2305 const EDA_ITEM* item = aSelection.Front();
2306
2307 return ( item != nullptr ) && canAddCorner( *item );
2308}
2309
2310
2311// Finds a corresponding vertex in a polygon set
2312static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
2313findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
2314{
2315 for( auto it = aPolySet.IterateWithHoles(); it; ++it )
2316 {
2317 auto vertexIdx = it.GetIndex();
2318
2319 if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
2320 return std::make_pair( true, vertexIdx );
2321 }
2322
2323 return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
2324}
2325
2326
2328{
2329 if( !m_editPoints || !m_editedPoint )
2330 return false;
2331
2332 EDA_ITEM* item = m_editPoints->GetParent();
2333 SHAPE_POLY_SET* polyset = nullptr;
2334
2335 if( !item )
2336 return false;
2337
2338 switch( item->Type() )
2339 {
2340 case PCB_ZONE_T:
2341 polyset = static_cast<ZONE*>( item )->Outline();
2342 break;
2343
2344 case PCB_SHAPE_T:
2345 if( static_cast<PCB_SHAPE*>( item )->GetShape() == SHAPE_T::POLY )
2346 polyset = &static_cast<PCB_SHAPE*>( item )->GetPolyShape();
2347 else
2348 return false;
2349
2350 break;
2351
2352 default:
2353 return false;
2354 }
2355
2356 std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX> vertex = findVertex( *polyset, *m_editedPoint );
2357
2358 if( !vertex.first )
2359 return false;
2360
2361 const SHAPE_POLY_SET::VERTEX_INDEX& vertexIdx = vertex.second;
2362
2363 // Check if there are enough vertices so one can be removed without
2364 // degenerating the polygon.
2365 // The first condition allows one to remove all corners from holes (when
2366 // there are only 2 vertices left, a hole is removed).
2367 if( vertexIdx.m_contour == 0 &&
2368 polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
2369 {
2370 return false;
2371 }
2372
2373 // Remove corner does not work with lines
2374 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2375 return false;
2376
2377 return m_editedPoint != nullptr;
2378}
2379
2380
2382{
2383 if( !m_editPoints )
2384 return 0;
2385
2386 EDA_ITEM* item = m_editPoints->GetParent();
2387 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2388 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
2389
2390 // called without an active edited polygon
2391 if( !item || !canAddCorner( *item ) )
2392 return 0;
2393
2394 PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
2395 BOARD_COMMIT commit( frame );
2396
2397 if( item->Type() == PCB_ZONE_T || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
2398 {
2399 unsigned int nearestIdx = 0;
2400 unsigned int nextNearestIdx = 0;
2401 unsigned int nearestDist = INT_MAX;
2402 unsigned int firstPointInContour = 0;
2403 SHAPE_POLY_SET* zoneOutline;
2404
2405 if( item->Type() == PCB_ZONE_T )
2406 {
2407 ZONE* zone = static_cast<ZONE*>( item );
2408 zoneOutline = zone->Outline();
2409 zone->SetNeedRefill( true );
2410 }
2411 else
2412 {
2413 zoneOutline = &( graphicItem->GetPolyShape() );
2414 }
2415
2416 commit.Modify( item );
2417
2418 // Search the best outline segment to add a new corner
2419 // and therefore break this segment into two segments
2420
2421 // Object to iterate through the corners of the outlines (main contour and its holes)
2422 SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0, zoneOutline->OutlineCount()-1,
2423 /* IterateHoles */ true );
2424 int curr_idx = 0;
2425
2426 // Iterate through all the corners of the outlines and search the best segment
2427 for( ; iterator; iterator++, curr_idx++ )
2428 {
2429 int jj = curr_idx+1;
2430
2431 if( iterator.IsEndContour() )
2432 { // We reach the last point of the current contour (main or hole)
2433 jj = firstPointInContour;
2434 firstPointInContour = curr_idx+1; // Prepare next contour analysis
2435 }
2436
2437 SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
2438
2439 unsigned int distance = curr_segment.Distance( cursorPos );
2440
2441 if( distance < nearestDist )
2442 {
2443 nearestDist = distance;
2444 nearestIdx = curr_idx;
2445 nextNearestIdx = jj;
2446 }
2447 }
2448
2449 // Find the point on the closest segment
2450 const VECTOR2I& sideOrigin = zoneOutline->CVertex( nearestIdx );
2451 const VECTOR2I& sideEnd = zoneOutline->CVertex( nextNearestIdx );
2452 SEG nearestSide( sideOrigin, sideEnd );
2453 VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
2454
2455 // Do not add points that have the same coordinates as ones that already belong to polygon
2456 // instead, add a point in the middle of the side
2457 if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
2458 nearestPoint = ( sideOrigin + sideEnd ) / 2;
2459
2460 zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
2461
2462 // We re-hatch the filled zones but not polygons
2463 if( item->Type() == PCB_ZONE_T )
2464 static_cast<ZONE*>( item )->HatchBorder();
2465
2466
2467 commit.Push( _( "Add Zone Corner" ) );
2468 }
2469 else if( graphicItem && graphicItem->GetShape() == SHAPE_T::SEGMENT )
2470 {
2471 commit.Modify( graphicItem );
2472
2473 SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
2474 VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
2475
2476 // Move the end of the line to the break point..
2477 graphicItem->SetEnd( VECTOR2I( nearestPoint.x, nearestPoint.y ) );
2478
2479 // and add another one starting from the break point
2480 PCB_SHAPE* newSegment = new PCB_SHAPE( *graphicItem );
2481
2482 newSegment->ClearSelected();
2483 newSegment->SetStart( VECTOR2I( nearestPoint.x, nearestPoint.y ) );
2484 newSegment->SetEnd( VECTOR2I( seg.B.x, seg.B.y ) );
2485
2486 commit.Add( newSegment );
2487 commit.Push( _( "Split Segment" ) );
2488 }
2489
2490 updatePoints();
2491 return 0;
2492}
2493
2494
2496{
2497 if( !m_editPoints || !m_editedPoint )
2498 return 0;
2499
2500 EDA_ITEM* item = m_editPoints->GetParent();
2501
2502 if( !item )
2503 return 0;
2504
2505 SHAPE_POLY_SET* polygon = nullptr;
2506
2507 if( item->Type() == PCB_ZONE_T )
2508 {
2509 ZONE* zone = static_cast<ZONE*>( item );
2510 polygon = zone->Outline();
2511 zone->SetNeedRefill( true );
2512 }
2513 else if( item->Type() == PCB_SHAPE_T )
2514 {
2515 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2516
2517 if( shape->GetShape() == SHAPE_T::POLY )
2518 polygon = &shape->GetPolyShape();
2519 }
2520
2521 if( !polygon )
2522 return 0;
2523
2524 PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
2525 BOARD_COMMIT commit( frame );
2526 auto vertex = findVertex( *polygon, *m_editedPoint );
2527
2528 if( vertex.first )
2529 {
2530 const auto& vertexIdx = vertex.second;
2531 auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
2532
2533 if( outline.PointCount() > 3 )
2534 {
2535 // the usual case: remove just the corner when there are >3 vertices
2536 commit.Modify( item );
2537 polygon->RemoveVertex( vertexIdx );
2538 }
2539 else
2540 {
2541 // either remove a hole or the polygon when there are <= 3 corners
2542 if( vertexIdx.m_contour > 0 )
2543 {
2544 // remove hole
2545 commit.Modify( item );
2546 polygon->RemoveContour( vertexIdx.m_contour );
2547 }
2548 else
2549 {
2551 commit.Remove( item );
2552 }
2553 }
2554
2555 setEditedPoint( nullptr );
2556
2557 if( item->Type() == PCB_ZONE_T )
2558 commit.Push( _( "Remove Zone Corner" ) );
2559 else
2560 commit.Push( _( "Remove Polygon Corner" ) );
2561
2562 // Refresh zone hatching
2563 if( item->Type() == PCB_ZONE_T )
2564 static_cast<ZONE*>( item )->HatchBorder();
2565
2566 updatePoints();
2567 }
2568
2569 return 0;
2570}
2571
2572
2574{
2575 updatePoints();
2576 return 0;
2577}
2578
2579
2581{
2582 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2583
2585 {
2586 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2588 else
2590
2591 switch( m_arcEditMode )
2592 {
2593 case ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS:
2594 m_arcEditMode = ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION;
2595 break;
2596 case ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION:
2597 m_arcEditMode = ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS;
2598 break;
2599 }
2600 }
2601 else
2602 {
2604 }
2605
2606 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2608 else
2610
2611 return 0;
2612}
2613
2614
2616{
2632}
ARC_EDIT_MODE
Settings for arc editing.
Definition: app_settings.h:52
@ KEEP_CENTER_ADJUST_ANGLE_RADIUS
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
static TOOL_ACTION cycleArcEditMode
Definition: actions.h:211
static TOOL_ACTION activatePointEditor
Definition: actions.h:210
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:79
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:264
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:245
virtual bool IsLocked() const
Definition: board_item.cpp:75
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:218
size_type GetHeight() const
Definition: box2.h:205
coord_type GetTop() const
Definition: box2.h:219
size_type GetWidth() const
Definition: box2.h:204
bool Contains(const Vec &aPoint) const
Definition: box2.h:158
coord_type GetRight() const
Definition: box2.h:207
coord_type GetLeft() const
Definition: box2.h:218
coord_type GetBottom() const
Definition: box2.h:212
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:89
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:133
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
void ClearSelected()
Definition: eda_item.h:122
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:206
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:205
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:564
virtual VECTOR2I GetTopLeft() const
Definition: eda_shape.h:194
int GetEndX() const
Definition: eda_shape.h:169
int GetRectangleWidth() const
Definition: eda_shape.cpp:166
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:279
SHAPE_T GetShape() const
Definition: eda_shape.h:125
virtual VECTOR2I GetBotRight() const
Definition: eda_shape.h:195
virtual void SetBottom(int val)
Definition: eda_shape.h:200
virtual void SetTop(int val)
Definition: eda_shape.h:197
int GetEndY() const
Definition: eda_shape.h:168
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:511
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:167
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:134
void DupPolyPointsList(std::vector< VECTOR2I > &aBuffer) const
Duplicate the list of corners in a std::vector<VECTOR2I>
Definition: eda_shape.cpp:1339
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:130
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:171
void SetBezierC1(const VECTOR2I &aPt)
Definition: eda_shape.h:202
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:644
virtual void SetLeft(int val)
Definition: eda_shape.h:198
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:203
int GetRectangleHeight() const
Definition: eda_shape.cpp:153
virtual void SetRight(int val)
Definition: eda_shape.h:199
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:582
const VECTOR2I & GetTextPos() const
Definition: eda_text.h:230
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:418
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:283
static const TOOL_EVENT SelectedEvent
Definition: actions.h:270
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:277
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition: actions.h:284
static const TOOL_EVENT PointSelectedEvent
Definition: actions.h:269
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:280
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:271
PADS & Pads()
Definition: footprint.h:195
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:317
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:357
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:1687
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:35
static LSET AllLayersMask()
Definition: lset.cpp:767
Definition: pad.h:54
ARC_EDIT_MODE m_ArcEditMode
static TOOL_ACTION pointEditorArcKeepCenter
Definition: pcb_actions.h:293
static TOOL_ACTION pointEditorMoveMidpoint
Definition: pcb_actions.h:297
static TOOL_ACTION genPushEdit
Definition: pcb_actions.h:283
static TOOL_ACTION genStartEdit
Definition: pcb_actions.h:281
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:68
static TOOL_ACTION pointEditorMoveCorner
Definition: pcb_actions.h:296
static TOOL_ACTION genUpdateEdit
Definition: pcb_actions.h:282
static TOOL_ACTION pointEditorArcKeepEndpoint
Definition: pcb_actions.h:294
static TOOL_ACTION pointEditorRemoveCorner
Definition: pcb_actions.h:291
static TOOL_ACTION pointEditorAddCorner
Definition: pcb_actions.h:290
static TOOL_ACTION reselectItem
Definition: pcb_actions.h:73
static TOOL_ACTION genRevertEdit
Definition: pcb_actions.h:284
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)
Update edit points with item's points.
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
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:75
bool IsProxyItem() const override
Definition: pcb_shape.h:110
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: pcb_shape.cpp:455
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:111
void Normalize() override
Perform any normalization required after a user rotate and/or flip.
Definition: pcb_table.cpp:132
int GetColWidth(int aCol) const
Definition: pcb_table.h:113
void SetRowHeight(int aRow, int aHeight)
Definition: pcb_table.h:121
int GetRowHeight(int aRow) const
Definition: pcb_table.h:123
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:327
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition: seg.h:220
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition: seg.cpp:388
virtual void Add(EDA_ITEM *aItem)
Definition: selection.cpp:42
EDA_ITEM * Front() const
Definition: selection.h:172
int Size() const
Returns the number of selected parts.
Definition: selection.h:116
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:218
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:150
bool PostAction(const std::string &aActionName, T aParam)
Run the specified action after the current action (coroutine) ends.
Definition: tool_manager.h:235
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:197
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:278
extended_type Cross(const VECTOR2< T > &aVector) const
Compute cross product of self with aVector.
Definition: vector2d.h:535
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:380
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:73
void SetNeedRefill(bool aNeedRefill)
Definition: zone.h:265
bool UnFill()
Removes the zone filling.
Definition: zone.cpp:209
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:913
SHAPE_POLY_SET * Outline()
Definition: zone.h:337
@ 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:403
static constexpr EDA_ANGLE ANGLE_270
Definition: eda_angle.h:406
@ ALL_LAYERS
@ OBJECT_LAYERS
@ IGNORE_SNAPS
@ SNAP_BY_GRID
@ SNAP_TO_GRID
@ RECT_RIGHT
@ RECT_LEFT
@ RECT_BOT
@ RECT_TOP
TABLECELL_POINTS
@ ROW_HEIGHT
@ COL_WIDTH
ARC_POINTS
@ ARC_START
@ ARC_END
@ ARC_CENTER
CIRCLE_POINTS
@ CIRC_END
@ CIRC_CENTER
BEZIER_POINTS
@ BEZIER_END
@ BEZIER_CTRL_PT2
@ BEZIER_START
@ BEZIER_CTRL_PT1
@ 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:390
#define STATUS_ITEMS_ONLY
Definition: pcb_generator.h:67
RECT_LINES
@ RECT_RIGHT
@ RECT_LEFT
@ RECT_BOT
@ RECT_TOP
@ 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
@ BEZIER_END
@ BEZIER_CTRL_PT2
@ BEZIER_START
@ BEZIER_CTRL_PT1
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:88
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
@ 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< int32_t > VECTOR2I
Definition: vector2d.h:673
VECTOR2< double > VECTOR2D
Definition: vector2d.h:672