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>
36#include <geometry/seg.h>
38#include <confirm.h>
39#include <tools/pcb_actions.h>
45#include <board_commit.h>
46#include <pcb_edit_frame.h>
47#include <pcb_reference_image.h>
48#include <pcb_generator.h>
49#include <pcb_dimension.h>
50#include <pcb_textbox.h>
51#include <pcb_tablecell.h>
52#include <pcb_table.h>
53#include <pad.h>
54#include <zone.h>
55#include <footprint.h>
58#include <progress_reporter.h>
59
60const unsigned int PCB_POINT_EDITOR::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
61
62// Few constants to avoid using bare numbers for point indices
64{
66};
67
68
70{
72};
73
74
76{
78};
79
80
82{
84};
85
86
88{
90};
91
92
94{
96};
97
98
100{
103
104
106{
112
113
115{
123
124
126 PCB_TOOL_BASE( "pcbnew.PointEditor" ),
127 m_selectionTool( nullptr ),
128 m_editedPoint( nullptr ),
129 m_hoveredPoint( nullptr ),
130 m_original( VECTOR2I( 0, 0 ) ),
132 m_altConstrainer( VECTOR2I( 0, 0 ) ),
133 m_inPointEditorTool( false )
134{
135}
136
137
139{
140 m_editPoints.reset();
141 m_altConstraint.reset();
142 getViewControls()->SetAutoPan( false );
143}
144
145
147{
148 // Find the selection tool, so they can cooperate
150
151 wxASSERT_MSG( m_selectionTool, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
152
153 auto& menu = m_selectionTool->GetToolMenu().GetMenu();
156 std::bind( &PCB_POINT_EDITOR::removeCornerCondition, this, _1 ) );
157
158 return true;
159}
160
161
162void PCB_POINT_EDITOR::buildForPolyOutline( std::shared_ptr<EDIT_POINTS> points,
163 const SHAPE_POLY_SET* aOutline )
164{
165 int cornersCount = aOutline->TotalVertices();
166
167 for( auto iterator = aOutline->CIterateWithHoles(); iterator; iterator++ )
168 {
169 points->AddPoint( *iterator );
170
171 if( iterator.IsEndContour() )
172 points->AddBreak();
173 }
174
175 // Lines have to be added after creating edit points, as they use EDIT_POINT references
176 for( int i = 0; i < cornersCount - 1; ++i )
177 {
178 if( points->IsContourEnd( i ) )
179 points->AddLine( points->Point( i ), points->Point( points->GetContourStartIdx( i ) ) );
180 else
181 points->AddLine( points->Point( i ), points->Point( i + 1 ) );
182
183 points->Line( i ).SetConstraint( new EC_PERPLINE( points->Line( i ) ) );
184 }
185
186 // The last missing line, connecting the last and the first polygon point
187 points->AddLine( points->Point( cornersCount - 1 ),
188 points->Point( points->GetContourStartIdx( cornersCount - 1 ) ) );
189
190 points->Line( points->LinesSize() - 1 )
191 .SetConstraint( new EC_PERPLINE( points->Line( points->LinesSize() - 1 ) ) );
192}
193
194
195std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
196{
197 std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
198
199 if( !aItem )
200 return points;
201
202 if( aItem->Type() == PCB_TEXTBOX_T )
203 {
204 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
205
206 // We can't currently handle TEXTBOXes that have been turned into SHAPE_T::POLYs due
207 // to non-cardinal rotations
208 if( shape->GetShape() != SHAPE_T::RECTANGLE )
209 return points;
210 }
211
212 // Generate list of edit points basing on the item type
213 switch( aItem->Type() )
214 {
216 {
217 const REFERENCE_IMAGE& refImage =
218 static_cast<const PCB_REFERENCE_IMAGE&>( *aItem ).GetReferenceImage();
219
220 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
221 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
222
223 points->AddPoint( topLeft );
224 points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
225 points->AddPoint( botRight );
226 points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
227
228 points->AddPoint( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
229
230 break;
231 }
232
233 case PCB_TEXTBOX_T:
234 case PCB_SHAPE_T:
235 {
236 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
237
238 switch( shape->GetShape() )
239 {
240 case SHAPE_T::SEGMENT:
241 points->AddPoint( shape->GetStart() );
242 points->AddPoint( shape->GetEnd() );
243 break;
244
245 case SHAPE_T::RECTANGLE:
246 {
247 VECTOR2I topLeft = shape->GetTopLeft();
248 VECTOR2I botRight = shape->GetBotRight();
249
250 points->SetSwapX( topLeft.x > botRight.x );
251 points->SetSwapY( topLeft.y > botRight.y );
252
253 if( points->SwapX() )
254 std::swap( topLeft.x, botRight.x );
255
256 if( points->SwapY() )
257 std::swap( topLeft.y, botRight.y );
258
259 points->AddPoint( topLeft );
260 points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
261 points->AddPoint( botRight );
262 points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
263
264 points->AddLine( points->Point( RECT_TOP_LEFT ), points->Point( RECT_TOP_RIGHT ) );
265 points->Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( points->Line( RECT_TOP ) ) );
266 points->AddLine( points->Point( RECT_TOP_RIGHT ), points->Point( RECT_BOT_RIGHT ) );
267 points->Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_RIGHT ) ) );
268 points->AddLine( points->Point( RECT_BOT_RIGHT ), points->Point( RECT_BOT_LEFT ) );
269 points->Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_BOT ) ) );
270 points->AddLine( points->Point( RECT_BOT_LEFT ), points->Point( RECT_TOP_LEFT ) );
271 points->Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_LEFT ) ) );
272
273 break;
274 }
275
276 case SHAPE_T::ARC:
277 points->AddPoint( shape->GetStart() );
278 points->AddPoint( shape->GetArcMid() );
279 points->AddPoint( shape->GetEnd() );
280 points->AddPoint( shape->GetCenter() );
281
282 points->AddIndicatorLine( points->Point( ARC_CENTER ), points->Point( ARC_START ) );
283 points->AddIndicatorLine( points->Point( ARC_CENTER ), points->Point( ARC_END ) );
284 break;
285
286 case SHAPE_T::CIRCLE:
287 points->AddPoint( shape->GetCenter() );
288 points->AddPoint( shape->GetEnd() );
289 break;
290
291 case SHAPE_T::POLY:
292 buildForPolyOutline( points, &shape->GetPolyShape() );
293 break;
294
295 case SHAPE_T::BEZIER:
296 points->AddPoint( shape->GetStart() );
297 points->AddPoint( shape->GetBezierC1() );
298 points->AddPoint( shape->GetBezierC2() );
299 points->AddPoint( shape->GetEnd() );
300
301 points->AddIndicatorLine( points->Point( BEZIER_START ),
302 points->Point( BEZIER_CTRL_PT1 ) );
303 points->AddIndicatorLine( points->Point( BEZIER_CTRL_PT2 ),
304 points->Point( BEZIER_END ) );
305 break;
306
307 default: // suppress warnings
308 break;
309 }
310
311 break;
312 }
313
314 case PCB_TABLECELL_T:
315 {
316 PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( aItem );
317 points->AddPoint( cell->GetEnd() - VECTOR2I( 0, cell->GetRectangleHeight() / 2 ) );
318 points->AddPoint( cell->GetEnd() - VECTOR2I( cell->GetRectangleWidth() / 2, 0 ) );
319 break;
320 }
321
322 case PCB_PAD_T:
323 {
324 const PAD* pad = static_cast<const PAD*>( aItem );
325 // TODO(JE) padstacks
326 VECTOR2I shapePos = pad->ShapePos( PADSTACK::ALL_LAYERS );
327 VECTOR2I halfSize( pad->GetSize( PADSTACK::ALL_LAYERS ).x / 2,
328 pad->GetSize( PADSTACK::ALL_LAYERS ).y / 2 );
329
330 if( !m_isFootprintEditor || pad->IsLocked() )
331 break;
332
333 switch( pad->GetShape( PADSTACK::ALL_LAYERS ) )
334 {
335 case PAD_SHAPE::CIRCLE:
336 points->AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y ) );
337 break;
338
339 case PAD_SHAPE::OVAL:
340 case PAD_SHAPE::TRAPEZOID:
341 case PAD_SHAPE::RECTANGLE:
342 case PAD_SHAPE::ROUNDRECT:
343 case PAD_SHAPE::CHAMFERED_RECT:
344 {
345 if( !pad->GetOrientation().IsCardinal() )
346 break;
347
348 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
349 std::swap( halfSize.x, halfSize.y );
350
351 points->AddPoint( shapePos - halfSize );
352 points->AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
353 points->AddPoint( shapePos + halfSize );
354 points->AddPoint( VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
355 }
356 break;
357
358 default: // suppress warnings
359 break;
360 }
361
362 break;
363 }
364
365 case PCB_ZONE_T:
366 {
367 const ZONE* zone = static_cast<const ZONE*>( aItem );
368 buildForPolyOutline( points, zone->Outline() );
369 break;
370 }
371
372 case PCB_GENERATOR_T:
373 {
374 const PCB_GENERATOR* generator = static_cast<const PCB_GENERATOR*>( aItem );
375 generator->MakeEditPoints( points );
376 break;
377 }
378
381 {
382 const PCB_DIM_ALIGNED* dimension = static_cast<const PCB_DIM_ALIGNED*>( aItem );
383
384 points->AddPoint( dimension->GetStart() );
385 points->AddPoint( dimension->GetEnd() );
386 points->AddPoint( dimension->GetTextPos() );
387 points->AddPoint( dimension->GetCrossbarStart() );
388 points->AddPoint( dimension->GetCrossbarEnd() );
389
390 points->Point( DIM_START ).SetSnapConstraint( ALL_LAYERS );
391 points->Point( DIM_END ).SetSnapConstraint( ALL_LAYERS );
392
393 if( aItem->Type() == PCB_DIM_ALIGNED_T )
394 {
395 // Dimension height setting - edit points should move only along the feature lines
396 points->Point( DIM_CROSSBARSTART )
397 .SetConstraint( new EC_LINE( points->Point( DIM_CROSSBARSTART ),
398 points->Point( DIM_START ) ) );
399 points->Point( DIM_CROSSBAREND )
400 .SetConstraint( new EC_LINE( points->Point( DIM_CROSSBAREND ),
401 points->Point( DIM_END ) ) );
402 }
403
404 break;
405 }
406
407 case PCB_DIM_CENTER_T:
408 {
409 const PCB_DIM_CENTER* dimension = static_cast<const PCB_DIM_CENTER*>( aItem );
410
411 points->AddPoint( dimension->GetStart() );
412 points->AddPoint( dimension->GetEnd() );
413
414 points->Point( DIM_START ).SetSnapConstraint( ALL_LAYERS );
415
416 points->Point( DIM_END ).SetConstraint( new EC_45DEGREE( points->Point( DIM_END ),
417 points->Point( DIM_START ) ) );
418 points->Point( DIM_END ).SetSnapConstraint( IGNORE_SNAPS );
419
420 break;
421 }
422
423 case PCB_DIM_RADIAL_T:
424 {
425 const PCB_DIM_RADIAL* dimension = static_cast<const PCB_DIM_RADIAL*>( aItem );
426
427 points->AddPoint( dimension->GetStart() );
428 points->AddPoint( dimension->GetEnd() );
429 points->AddPoint( dimension->GetTextPos() );
430 points->AddPoint( dimension->GetKnee() );
431
432 points->Point( DIM_START ).SetSnapConstraint( ALL_LAYERS );
433 points->Point( DIM_END ).SetSnapConstraint( ALL_LAYERS );
434
435 points->Point( DIM_KNEE ).SetConstraint( new EC_LINE( points->Point( DIM_START ),
436 points->Point( DIM_END ) ) );
437 points->Point( DIM_KNEE ).SetSnapConstraint( IGNORE_SNAPS );
438
439 points->Point( DIM_TEXT ).SetConstraint( new EC_45DEGREE( points->Point( DIM_TEXT ),
440 points->Point( DIM_KNEE ) ) );
441 points->Point( DIM_TEXT ).SetSnapConstraint( IGNORE_SNAPS );
442
443 break;
444 }
445
446 case PCB_DIM_LEADER_T:
447 {
448 const PCB_DIM_LEADER* dimension = static_cast<const PCB_DIM_LEADER*>( aItem );
449
450 points->AddPoint( dimension->GetStart() );
451 points->AddPoint( dimension->GetEnd() );
452 points->AddPoint( dimension->GetTextPos() );
453
454 points->Point( DIM_START ).SetSnapConstraint( ALL_LAYERS );
455 points->Point( DIM_END ).SetSnapConstraint( ALL_LAYERS );
456
457 points->Point( DIM_TEXT ).SetConstraint( new EC_45DEGREE( points->Point( DIM_TEXT ),
458 points->Point( DIM_END ) ) );
459 points->Point( DIM_TEXT ).SetSnapConstraint( IGNORE_SNAPS );
460
461 break;
462 }
463
464 default:
465 points.reset();
466 break;
467 }
468
469 return points;
470}
471
472
474{
475 EDIT_POINT* point;
476 EDIT_POINT* hovered = nullptr;
477
478 if( aEvent.IsMotion() )
479 {
480 point = m_editPoints->FindPoint( aEvent.Position(), getView() );
481 hovered = point;
482 }
483 else if( aEvent.IsDrag( BUT_LEFT ) )
484 {
485 point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
486 }
487 else
488 {
489 point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
490 }
491
492 if( hovered )
493 {
494 if( m_hoveredPoint != hovered )
495 {
496 if( m_hoveredPoint )
497 m_hoveredPoint->SetHover( false );
498
499 m_hoveredPoint = hovered;
501 }
502 }
503 else if( m_hoveredPoint )
504 {
505 m_hoveredPoint->SetHover( false );
506 m_hoveredPoint = nullptr;
507 }
508
509 if( m_editedPoint != point )
510 setEditedPoint( point );
511}
512
513
515{
517 return 0;
518
520 return 0;
521
523
524 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
526
527 if( selection.Size() != 1 || selection.Front()->GetEditFlags() )
528 return 0;
529
530 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
531
532 if( !item || item->IsLocked() )
533 return 0;
534
535 Activate();
536 // Must be done after Activate() so that it gets set into the correct context
537 getViewControls()->ShowCursor( true );
538
540
541 // Use the original object as a construction item
542 std::unique_ptr<BOARD_ITEM> clone;
543
544 m_editPoints = makePoints( item );
545
546 if( !m_editPoints )
547 return 0;
548
550 getView()->Add( &m_preview );
551
552 getView()->Add( m_editPoints.get() );
553 setEditedPoint( nullptr );
554 updateEditedPoint( aEvent );
555 bool inDrag = false;
556 bool useAltContraint = false;
557
558 BOARD_COMMIT commit( editFrame );
559
560 // Main loop: keep receiving events
561 while( TOOL_EVENT* evt = Wait() )
562 {
563 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
564 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
565
566 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
567 {
568 useAltContraint = editFrame->GetPcbNewSettings()->m_Use45DegreeLimit;
570 }
571 else
572 {
573 useAltContraint = editFrame->GetFootprintEditorSettings()->m_Use45Limit;
575 }
576
577 if( !m_editPoints || evt->IsSelectionEvent() ||
578 evt->Matches( EVENTS::InhibitSelectionEditing ) )
579 {
580 break;
581 }
582
583 EDIT_POINT* prevHover = m_hoveredPoint;
584
585 if( !inDrag )
586 updateEditedPoint( *evt );
587
588 if( prevHover != m_hoveredPoint )
589 getView()->Update( m_editPoints.get() );
590
591 if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
592 {
593 if( !inDrag )
594 {
595 frame()->UndoRedoBlock( true );
596
597 if( item->Type() == PCB_GENERATOR_T )
598 {
600 static_cast<PCB_GENERATOR*>( item ) );
601 }
602 else if( item->Type() == PCB_TABLECELL_T )
603 {
604 PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( item );
605 PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
606
607 commit.Modify( table );
608 }
609 else
610 {
611 commit.Modify( item );
612 }
613
615 m_original = *m_editedPoint; // Save the original position
616 getViewControls()->SetAutoPan( true );
617 inDrag = true;
618
620 grid.SetAuxAxes( true, m_original.GetPosition() );
621
622 setAltConstraint( true );
624
625 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
626 {
627 EDIT_POINT& point = m_editPoints->Point( ii );
628
629 if( &point != m_editedPoint )
630 point.SetActive( false );
631 }
632
633 // When we start dragging, create a clone of the item to use as the original
634 // reference geometry (e.g. for intersections and extensions)
635 std::unique_ptr<BOARD_ITEM> oldClone = std::move( clone );
636 clone.reset( static_cast<BOARD_ITEM*>( item->Clone() ) );
637 grid.AddConstructionItems( { clone.get() }, false, true );
638 // Now old clone can be safely deleted
639 }
640
641 // Keep point inside of limits with some padding
642 VECTOR2I pos = GetClampedCoords<double, int>( evt->Position(), COORDS_PADDING );
643 LSET snapLayers;
644
646 {
647 case IGNORE_SNAPS: break;
648 case OBJECT_LAYERS: snapLayers = item->GetLayerSet(); break;
649 case ALL_LAYERS: snapLayers = LSET::AllLayersMask(); break;
650 }
651
653 {
654 if( grid.GetUseGrid() )
655 {
656 VECTOR2I gridPt = grid.BestSnapAnchor( pos, {}, grid.GetItemGrid( item ),
657 { item } );
658
660 VECTOR2I delta = pos - last;
661 VECTOR2I deltaGrid = gridPt - grid.BestSnapAnchor( last, {},
662 grid.GetItemGrid( item ),
663 { item } );
664
665 if( abs( delta.x ) > grid.GetGrid().x / 2 )
666 pos.x = last.x + deltaGrid.x;
667 else
668 pos.x = last.x;
669
670 if( abs( delta.y ) > grid.GetGrid().y / 2 )
671 pos.y = last.y + deltaGrid.y;
672 else
673 pos.y = last.y;
674 }
675 }
676
678
679 // The alternative constraint limits to 45 degrees
680 if( useAltContraint )
681 {
682 m_altConstraint->Apply( grid );
683 }
684 else if( m_editedPoint->IsConstrained() )
685 {
687 }
689 {
691 snapLayers,
692 grid.GetItemGrid( item ),
693 { item } ) );
694 }
695
696 updateItem( &commit );
698 updatePoints();
699 }
700 else if( m_editedPoint && evt->Action() == TA_MOUSE_DOWN && evt->Buttons() == BUT_LEFT )
701 {
703
704 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
705 {
706 EDIT_POINT& point = m_editPoints->Point( ii );
707
708 if( &point != m_editedPoint )
709 point.SetActive( false );
710 }
711
712 getView()->Update( m_editPoints.get() );
713 }
714 else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
715 {
716 if( m_editedPoint )
717 {
718 m_editedPoint->SetActive( false );
719 getView()->Update( m_editPoints.get() );
720 }
721
722 getViewControls()->SetAutoPan( false );
723 setAltConstraint( false );
724
725 if( item->Type() == PCB_GENERATOR_T )
726 {
729 static_cast<PCB_GENERATOR*>( item ) );
730 }
731 else if( item->Type() == PCB_TABLECELL_T )
732 {
733 commit.Push( _( "Resize Table Cells" ) );
734 }
735 else
736 {
737 commit.Push( _( "Move Point" ) );
738 }
739
740 inDrag = false;
741 frame()->UndoRedoBlock( false );
742
744 item ); // FIXME: Needed for generators
745 }
746 else if( evt->IsCancelInteractive() || evt->IsActivate() )
747 {
748 if( inDrag ) // Restore the last change
749 {
750 if( item->Type() == PCB_GENERATOR_T )
751 {
753 static_cast<PCB_GENERATOR*>( item ) );
754 }
755 commit.Revert();
756
757 inDrag = false;
758 frame()->UndoRedoBlock( false );
759 }
760
761 // Only cancel point editor when activating a new tool
762 // Otherwise, allow the points to persist when moving up the
763 // tool stack
764 if( evt->IsActivate() && !evt->IsMoveTool() )
765 break;
766 }
767 else if( evt->Action() == TA_UNDO_REDO_POST )
768 {
769 break;
770 }
771 else
772 {
773 evt->SetPassEvent();
774 }
775 }
776
778 getView()->Remove( &m_preview );
779
780 if( m_editPoints )
781 {
782 getView()->Remove( m_editPoints.get() );
783 m_editPoints.reset();
784 }
785
786 m_editedPoint = nullptr;
787
788 return 0;
789}
790
791
793{
794 if( !m_editPoints || !m_editPoints->GetParent() || !HasPoint() )
795 return 0;
796
797 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
798
799 BOARD_COMMIT commit( editFrame );
800 commit.Stage( m_editPoints->GetParent(), CHT_MODIFY );
801
803 wxString title;
804 wxString msg;
805
806 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
807 {
808 title = _( "Move Midpoint to Location" );
809 msg = _( "Move Midpoint" );
810 }
811 else
812 {
813 title = _( "Move Corner to Location" );
814 msg = _( "Move Corner" );
815 }
816
817 WX_PT_ENTRY_DIALOG dlg( editFrame, title, _( "X:" ), _( "Y:" ), pt );
818
819 if( dlg.ShowModal() == wxID_OK )
820 {
821 pt = editFrame->GetOriginTransforms().FromDisplayAbs( dlg.GetValue() );
823 updateItem( &commit );
824 commit.Push( msg );
825 }
826
827 return 0;
828}
829
830
832 const VECTOR2I& aStart, const VECTOR2I& aMid,
833 const VECTOR2I& aEnd,
834 const VECTOR2I& aCursor ) const
835{
836 VECTOR2I center = aCenter;
837 bool movingStart;
838 bool arcValid = true;
839
840 VECTOR2I p1, p2, p3;
841 // p1 does not move, p2 does.
842
843 if( aStart != aArc->GetStart() )
844 {
845 p1 = aEnd;
846 p2 = aStart;
847 p3 = aMid;
848 movingStart = true;
849 }
850 else if( aEnd != aArc->GetEnd() )
851 {
852 p1 = aStart;
853 p2 = aEnd;
854 p3 = aMid;
855 movingStart = false;
856 }
857 else
858 {
859 return;
860 }
861
862 VECTOR2D v1, v2, v3, v4;
863
864 // Move the coordinate system
865 v1 = p1 - aCenter;
866 v2 = p2 - aCenter;
867 v3 = p3 - aCenter;
868
869 VECTOR2D u1, u2;
870
871 // A point cannot be both the center and on the arc.
872 if( ( v1.EuclideanNorm() == 0 ) || ( v2.EuclideanNorm() == 0 ) )
873 return;
874
875 u1 = v1 / v1.EuclideanNorm();
876 u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
877 u2 = u2 / u2.EuclideanNorm();
878
879 // [ u1, u3 ] is a base centered on the circle with:
880 // u1 : unit vector toward the point that does not move
881 // u2 : unit vector toward the mid point.
882
883 // Get vectors v1, and v2 in that coordinate system.
884
885 double det = u1.x * u2.y - u2.x * u1.y;
886
887 // u1 and u2 are unit vectors, and perpendicular.
888 // det should not be 0. In case it is, do not change the arc.
889 if( det == 0 )
890 return;
891
892 double tmpx = v1.x * u2.y - v1.y * u2.x;
893 double tmpy = -v1.x * u1.y + v1.y * u1.x;
894 v1.x = tmpx;
895 v1.y = tmpy;
896 v1 = v1 / det;
897
898 tmpx = v2.x * u2.y - v2.y * u2.x;
899 tmpy = -v2.x * u1.y + v2.y * u1.x;
900 v2.x = tmpx;
901 v2.y = tmpy;
902 v2 = v2 / det;
903
904 double R = v1.EuclideanNorm();
905 bool transformCircle = false;
906
907 /* p2
908 * X***
909 * ** <---- This is the arc
910 * y ^ **
911 * | R *
912 * | <-----------> *
913 * x------x------>--------x p1
914 * C' <----> C x
915 * delta
916 *
917 * p1 does not move, and the tangent at p1 remains the same.
918 * => The new center, C', will be on the C-p1 axis.
919 * p2 moves
920 *
921 * The radius of the new circle is delta + R
922 *
923 * || C' p2 || = || C' P1 ||
924 * is the same as :
925 * ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
926 *
927 * delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
928 *
929 * We can use this equation for any point p2 with p2.x < R
930 */
931
932 if( v2.x == R )
933 {
934 // Straight line, do nothing
935 }
936 else
937 {
938 if( v2.x > R )
939 {
940 // If we need to invert the curvature.
941 // We modify the input so we can use the same equation
942 transformCircle = true;
943 v2.x = 2 * R - v2.x;
944 }
945
946 // We can keep the tangent constraint.
947 double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
948
949 // This is just to limit the radius, so nothing overflows later when drawing.
950 if( abs( v2.y / ( R - v2.x ) ) > ADVANCED_CFG::GetCfg().m_DrawArcCenterMaxAngle )
951 arcValid = false;
952
953 // Never recorded a problem, but still checking.
954 if( !std::isfinite( delta ) )
955 arcValid = false;
956
957 // v4 is the new center
958 v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
959
960 tmpx = v4.x * u1.x + v4.y * u2.x;
961 tmpy = v4.x * u1.y + v4.y * u2.y;
962 v4.x = tmpx;
963 v4.y = tmpy;
964
965 center = v4 + aCenter;
966
967 if( arcValid )
968 {
969 aArc->SetCenter( center );
970
971 if( movingStart )
972 aArc->SetStart( aStart );
973 else
974 aArc->SetEnd( aEnd );
975 }
976 }
977}
978
979
981 const VECTOR2I& aStart, const VECTOR2I& aMid,
982 const VECTOR2I& aEnd ) const
983{
984 const int c_snapEpsilon_sq = 4;
985
986 VECTOR2I m = ( aStart / 2 + aEnd / 2 );
987 VECTOR2I perp = ( aEnd - aStart ).Perpendicular().Resize( INT_MAX / 2 );
988
989 SEG legal( m - perp, m + perp );
990
991 const SEG testSegments[] = { SEG( aCenter, aCenter + VECTOR2( 1, 0 ) ),
992 SEG( aCenter, aCenter + VECTOR2( 0, 1 ) ) };
993
994 std::vector<VECTOR2I> points = { legal.A, legal.B };
995
996 for( const SEG& seg : testSegments )
997 {
998 OPT_VECTOR2I vec = legal.IntersectLines( seg );
999
1000 if( vec && legal.SquaredDistance( *vec ) <= c_snapEpsilon_sq )
1001 points.push_back( *vec );
1002 }
1003
1004 OPT_VECTOR2I nearest;
1006
1007 // Snap by distance between cursor and intersections
1008 for( const VECTOR2I& pt : points )
1009 {
1010 SEG::ecoord d_sq = ( pt - aCenter ).SquaredEuclideanNorm();
1011
1012 if( d_sq < min_d_sq - c_snapEpsilon_sq )
1013 {
1014 min_d_sq = d_sq;
1015 nearest = pt;
1016 }
1017 }
1018
1019 if( nearest )
1020 aArc->SetCenter( *nearest );
1021}
1022
1023
1035 VECTOR2I& aBotLeft, VECTOR2I& aBotRight,
1036 const VECTOR2I& aHole, const VECTOR2I& aHoleSize ) const
1037{
1038 int minWidth = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
1039 int minHeight = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
1040
1041 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) ) )
1042 {
1043 if( aHoleSize.x )
1044 {
1045 // pin edited point to the top/left of the hole
1046 aTopLeft.x = std::min( aTopLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
1047 aTopLeft.y = std::min( aTopLeft.y, aHole.y - aHoleSize.y / 2 - minHeight );
1048 }
1049 else
1050 {
1051 // pin edited point within opposite corner
1052 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
1053 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
1054 }
1055
1056 // push edited point edges to adjacent corners
1057 aTopRight.y = aTopLeft.y;
1058 aBotLeft.x = aTopLeft.x;
1059 }
1060 else if( isModified( m_editPoints->Point( RECT_TOP_RIGHT ) ) )
1061 {
1062 if( aHoleSize.x )
1063 {
1064 // pin edited point to the top/right of the hole
1065 aTopRight.x = std::max( aTopRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
1066 aTopRight.y = std::min( aTopRight.y, aHole.y - aHoleSize.y / 2 - minHeight );
1067 }
1068 else
1069 {
1070 // pin edited point within opposite corner
1071 aTopRight.x = std::max( aTopRight.x, aBotLeft.x + minWidth );
1072 aTopRight.y = std::min( aTopRight.y, aBotLeft.y - minHeight );
1073 }
1074
1075 // push edited point edges to adjacent corners
1076 aTopLeft.y = aTopRight.y;
1077 aBotRight.x = aTopRight.x;
1078 }
1079 else if( isModified( m_editPoints->Point( RECT_BOT_LEFT ) ) )
1080 {
1081 if( aHoleSize.x )
1082 {
1083 // pin edited point to the bottom/left of the hole
1084 aBotLeft.x = std::min( aBotLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
1085 aBotLeft.y = std::max( aBotLeft.y, aHole.y + aHoleSize.y / 2 + minHeight );
1086 }
1087 else
1088 {
1089 // pin edited point within opposite corner
1090 aBotLeft.x = std::min( aBotLeft.x, aTopRight.x - minWidth );
1091 aBotLeft.y = std::max( aBotLeft.y, aTopRight.y + minHeight );
1092 }
1093
1094 // push edited point edges to adjacent corners
1095 aBotRight.y = aBotLeft.y;
1096 aTopLeft.x = aBotLeft.x;
1097 }
1098 else if( isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1099 {
1100 if( aHoleSize.x )
1101 {
1102 // pin edited point to the bottom/right of the hole
1103 aBotRight.x = std::max( aBotRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
1104 aBotRight.y = std::max( aBotRight.y, aHole.y + aHoleSize.y / 2 + minHeight );
1105 }
1106 else
1107 {
1108 // pin edited point within opposite corner
1109 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
1110 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
1111 }
1112
1113 // push edited point edges to adjacent corners
1114 aBotLeft.y = aBotRight.y;
1115 aTopRight.x = aBotRight.x;
1116 }
1117 else if( isModified( m_editPoints->Line( RECT_TOP ) ) )
1118 {
1119 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
1120 }
1121 else if( isModified( m_editPoints->Line( RECT_LEFT ) ) )
1122 {
1123 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
1124 }
1125 else if( isModified( m_editPoints->Line( RECT_BOT ) ) )
1126 {
1127 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
1128 }
1129 else if( isModified( m_editPoints->Line( RECT_RIGHT ) ) )
1130 {
1131 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
1132 }
1133}
1134
1135
1137 const VECTOR2I& aStart, const VECTOR2I& aMid,
1138 const VECTOR2I& aEnd,
1139 const VECTOR2I& aCursor ) const
1140{
1141 int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
1142 bool movingStart;
1143
1144 VECTOR2I p1, p2, prev_p1;
1145
1146 // user is moving p1, we want to move p2 to the new radius.
1147
1148 if( aStart != aArc->GetStart() )
1149 {
1150 prev_p1 = aArc->GetStart();
1151 p1 = aStart;
1152 p2 = aEnd;
1153 movingStart = true;
1154 }
1155 else
1156 {
1157 prev_p1 = aArc->GetEnd();
1158 p1 = aEnd;
1159 p2 = aStart;
1160 movingStart = false;
1161 }
1162
1163 p1 = p1 - aCenter;
1164 p2 = p2 - aCenter;
1165
1166 if( p1.x == 0 && p1.y == 0 )
1167 p1 = prev_p1 - aCenter;
1168
1169 if( p2.x == 0 && p2.y == 0 )
1170 p2 = { 1, 0 };
1171
1172 double radius = p1.EuclideanNorm();
1173
1174 if( radius < minRadius )
1175 radius = minRadius;
1176
1177 p1 = aCenter + p1.Resize( radius );
1178 p2 = aCenter + p2.Resize( radius );
1179
1180 aArc->SetCenter( aCenter );
1181
1182 if( movingStart )
1183 {
1184 aArc->SetStart( p1 );
1185 aArc->SetEnd( p2 );
1186 }
1187 else
1188 {
1189 aArc->SetStart( p2 );
1190 aArc->SetEnd( p1 );
1191 }
1192}
1193
1194
1196 const VECTOR2I& aStart, const VECTOR2I& aMid,
1197 const VECTOR2I& aEnd, const VECTOR2I& aCursor ) const
1198{
1199 int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
1200
1201 SEG chord( aStart, aEnd );
1202
1203 // Now, update the edit point position
1204 // Express the point in a circle-centered coordinate system.
1205 VECTOR2I start = aStart - aCenter;
1206 VECTOR2I end = aEnd - aCenter;
1207
1208 double radius = ( aCursor - aCenter ).EuclideanNorm();
1209
1210 if( radius < minRadius )
1211 radius = minRadius;
1212
1213 start = start.Resize( radius );
1214 end = end.Resize( radius );
1215
1216 start = start + aCenter;
1217 end = end + aCenter;
1218
1219 aArc->SetStart( start );
1220 aArc->SetEnd( end );
1221}
1222
1223
1225 const VECTOR2I& aEnd,
1226 const VECTOR2I& aCursor ) const
1227{
1228 // Let 'm' be the middle point of the chord between the start and end points
1229 VECTOR2I m = ( aStart + aEnd ) / 2;
1230
1231 // Legal midpoints lie on a vector starting just off the chord midpoint and extending out
1232 // past the existing midpoint. We do not allow arc inflection while point editing.
1233 const int JUST_OFF = ( aStart - aEnd ).EuclideanNorm() / 100;
1234 VECTOR2I v = (VECTOR2I) aArc->GetArcMid() - m;
1235 SEG legal( m + v.Resize( JUST_OFF ), m + v.Resize( INT_MAX / 2 ) );
1236 VECTOR2I mid = legal.NearestPoint( aCursor );
1237
1238 aArc->SetArcGeometry( aStart, mid, aEnd );
1239}
1240
1250{
1251public:
1253 m_dimension( aDimension ), m_originalTextPos( aDimension.GetTextPos() ),
1254 m_oldCrossBar( SEG{ aDimension.GetCrossbarStart(), aDimension.GetCrossbarEnd() } )
1255 {
1256 }
1257
1259 {
1260 const SEG newCrossBar{ m_dimension.GetCrossbarStart(), m_dimension.GetCrossbarEnd() };
1261
1262 if( newCrossBar == m_oldCrossBar )
1263 {
1264 // Crossbar didn't change, text doesn't need to change
1265 return;
1266 }
1267
1268 const VECTOR2I newTextPos = getDimensionNewTextPosition();
1269 m_dimension.SetTextPos( newTextPos );
1270
1271 const GR_TEXT_H_ALIGN_T oldJustify = m_dimension.GetHorizJustify();
1272
1273 // We may need to update the justification if we go past vertical.
1274 if( oldJustify == GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_LEFT
1275 || oldJustify == GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_RIGHT )
1276 {
1278 const VECTOR2I newProject = newCrossBar.LineProject( newTextPos );
1279
1280 const VECTOR2I oldProjectedOffset =
1281 oldProject - m_oldCrossBar.NearestPoint( oldProject );
1282 const VECTOR2I newProjectedOffset = newProject - newCrossBar.NearestPoint( newProject );
1283
1284 const bool textWasLeftOf = oldProjectedOffset.x < 0
1285 || ( oldProjectedOffset.x == 0 && oldProjectedOffset.y > 0 );
1286 const bool textIsLeftOf = newProjectedOffset.x < 0
1287 || ( newProjectedOffset.x == 0 && newProjectedOffset.y > 0 );
1288
1289 if( textWasLeftOf != textIsLeftOf )
1290 {
1291 // Flip whatever the user had set
1293 ( oldJustify == GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_LEFT )
1294 ? GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_RIGHT
1295 : GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_LEFT );
1296 }
1297 }
1298
1299 // Update the dimension (again) to ensure the text knockouts are correct
1301 }
1302
1303private:
1305 {
1306 const SEG newCrossBar{ m_dimension.GetCrossbarStart(), m_dimension.GetCrossbarEnd() };
1307
1308 const EDA_ANGLE oldAngle = EDA_ANGLE( m_oldCrossBar.B - m_oldCrossBar.A );
1309 const EDA_ANGLE newAngle = EDA_ANGLE( newCrossBar.B - newCrossBar.A );
1310 const EDA_ANGLE rotation = oldAngle - newAngle;
1311
1312 // There are two modes - when the text is between the crossbar points, and when it's not.
1314 {
1315 const VECTOR2I cbNearestEndToText =
1317 const VECTOR2I rotTextOffsetFromCbCenter =
1319 const VECTOR2I rotTextOffsetFromCbEnd =
1320 GetRotated( m_originalTextPos - cbNearestEndToText, rotation );
1321
1322 // Which of the two crossbar points is now in the right direction? They could be swapped over now.
1323 // If zero-length, doesn't matter, they're the same thing
1324 const bool startIsInOffsetDirection =
1326 rotTextOffsetFromCbCenter, newCrossBar.Center() );
1327
1328 const VECTOR2I& newCbRefPt = startIsInOffsetDirection ? m_dimension.GetCrossbarStart()
1330
1331 // Apply the new offset to the correct crossbar point
1332 return newCbRefPt + rotTextOffsetFromCbEnd;
1333 }
1334
1335 // If the text was between the crossbar points, it should stay there, but we need to find a
1336 // good place for it. Keep it the same distance from the crossbar line, but rotated as needed.
1337
1338 const VECTOR2I origTextPointProjected = m_oldCrossBar.NearestPoint( m_originalTextPos );
1339 const double oldRatio =
1340 KIGEOM::GetLengthRatioFromStart( origTextPointProjected, m_oldCrossBar );
1341
1342 // Perpendicular from the crossbar line to the text position
1343 // We need to keep this length constant
1344 const VECTOR2I rotCbNormalToText =
1345 GetRotated( m_originalTextPos - origTextPointProjected, rotation );
1346
1347 const VECTOR2I newProjected = newCrossBar.A + ( newCrossBar.B - newCrossBar.A ) * oldRatio;
1348 return newProjected + rotCbNormalToText;
1349 }
1350
1354};
1355
1356
1358{
1359 EDA_ITEM* item = m_editPoints->GetParent();
1360
1361 if( !item )
1362 return;
1363
1364 switch( item->Type() )
1365 {
1367 {
1368 PCB_REFERENCE_IMAGE& bitmap = static_cast<PCB_REFERENCE_IMAGE&>( *item );
1369 REFERENCE_IMAGE& refImg = bitmap.GetReferenceImage();
1370 const VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
1371 const VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
1372 const VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
1373 const VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
1374 const VECTOR2I xfrmOrigin = m_editPoints->Point( REFIMG_ORIGIN ).GetPosition();
1375
1376 if( isModified( m_editPoints->Point( REFIMG_ORIGIN ) ) )
1377 {
1378 // Moving the transform origin
1379 // As the other points didn't move, we can get the image extent from them
1380 const VECTOR2I newOffset = xfrmOrigin - ( topLeft + botRight ) / 2;
1381 refImg.SetTransformOriginOffset( newOffset );
1382 }
1383 else
1384 {
1385 const VECTOR2I oldOrigin = refImg.GetPosition() + refImg.GetTransformOriginOffset();
1386 const VECTOR2I oldSize = refImg.GetSize();
1387 const VECTOR2I pos = refImg.GetPosition();
1388
1389 OPT_VECTOR2I newCorner;
1390 VECTOR2I oldCorner = pos;
1391
1392 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) ) )
1393 {
1394 newCorner = topLeft;
1395 oldCorner -= oldSize / 2;
1396 }
1397 else if( isModified( m_editPoints->Point( RECT_TOP_RIGHT ) ) )
1398 {
1399 newCorner = topRight;
1400 oldCorner -= VECTOR2I( -oldSize.x, oldSize.y ) / 2;
1401 }
1402 else if( isModified( m_editPoints->Point( RECT_BOT_LEFT ) ) )
1403 {
1404 newCorner = botLeft;
1405 oldCorner -= VECTOR2I( oldSize.x, -oldSize.y ) / 2;
1406 }
1407 else if( isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1408 {
1409 newCorner = botRight;
1410 oldCorner += oldSize / 2;
1411 }
1412
1413 if( newCorner )
1414 {
1415 // Turn in the respective vectors from the origin
1416 *newCorner -= xfrmOrigin;
1417 oldCorner -= oldOrigin;
1418
1419 // If we tried to cross the origin, clamp it to stop it
1420 if( sign( newCorner->x ) != sign( oldCorner.x )
1421 || sign( newCorner->y ) != sign( oldCorner.y ) )
1422 {
1423 *newCorner = VECTOR2I( 0, 0 );
1424 }
1425
1426 const double newLength = newCorner->EuclideanNorm();
1427 const double oldLength = oldCorner.EuclideanNorm();
1428
1429 double ratio = oldLength > 0 ? ( newLength / oldLength ) : 1.0;
1430
1431 // Clamp the scaling to a minimum of 50 mils
1432 VECTOR2I newSize = oldSize * ratio;
1433 double newWidth = std::max( newSize.x, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
1434 double newHeight = std::max( newSize.y, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
1435 ratio = std::min( newWidth / oldSize.x, newHeight / oldSize.y );
1436
1437 // Also handles the origin offset
1438 refImg.SetImageScale( refImg.GetImageScale() * ratio );
1439 }
1440 }
1441
1442 break;
1443 }
1444
1445 case PCB_TEXTBOX_T:
1446 case PCB_SHAPE_T:
1447 {
1448 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1449
1450 switch( shape->GetShape() )
1451 {
1452 case SHAPE_T::SEGMENT:
1453 if( isModified( m_editPoints->Point( SEG_START ) ) )
1454 shape->SetStart( m_editPoints->Point( SEG_START ).GetPosition() );
1455 else if( isModified( m_editPoints->Point( SEG_END ) ) )
1456 shape->SetEnd( m_editPoints->Point( SEG_END ).GetPosition() );
1457
1458 break;
1459
1460 case SHAPE_T::RECTANGLE:
1461 {
1462 auto setLeft =
1463 [&]( int left )
1464 {
1465 m_editPoints->SwapX() ? shape->SetRight( left ) : shape->SetLeft( left );
1466 };
1467 auto setRight =
1468 [&]( int right )
1469 {
1470 m_editPoints->SwapX() ? shape->SetLeft( right ) : shape->SetRight( right );
1471 };
1472 auto setTop =
1473 [&]( int top )
1474 {
1475 m_editPoints->SwapY() ? shape->SetBottom( top ) : shape->SetTop( top );
1476 };
1477 auto setBottom =
1478 [&]( int bottom )
1479 {
1480 m_editPoints->SwapY() ? shape->SetTop( bottom ) : shape->SetBottom( bottom );
1481 };
1482
1483 VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
1484 VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
1485 VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
1486 VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
1487
1488 pinEditedCorner( topLeft, topRight, botLeft, botRight );
1489
1490 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1491 || isModified( m_editPoints->Point( RECT_TOP_RIGHT ) )
1492 || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) )
1493 || isModified( m_editPoints->Point( RECT_BOT_LEFT ) ) )
1494 {
1495 setLeft( topLeft.x );
1496 setTop( topLeft.y );
1497 setRight( botRight.x );
1498 setBottom( botRight.y );
1499 }
1500 else if( isModified( m_editPoints->Line( RECT_TOP ) ) )
1501 {
1502 setTop( topLeft.y );
1503 }
1504 else if( isModified( m_editPoints->Line( RECT_LEFT ) ) )
1505 {
1506 setLeft( topLeft.x );
1507 }
1508 else if( isModified( m_editPoints->Line( RECT_BOT ) ) )
1509 {
1510 setBottom( botRight.y );
1511 }
1512 else if( isModified( m_editPoints->Line( RECT_RIGHT ) ) )
1513 {
1514 setRight( botRight.x );
1515 }
1516
1517 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1518 {
1519 if( !isModified( m_editPoints->Line( i ) ) )
1520 {
1521 m_editPoints->Line( i ).SetConstraint(
1522 new EC_PERPLINE( m_editPoints->Line( i ) ) );
1523 }
1524 }
1525
1526 break;
1527 }
1528
1529 case SHAPE_T::ARC:
1530 {
1531 VECTOR2I center = m_editPoints->Point( ARC_CENTER ).GetPosition();
1532 VECTOR2I mid = m_editPoints->Point( ARC_MID ).GetPosition();
1533 VECTOR2I start = m_editPoints->Point( ARC_START ).GetPosition();
1534 VECTOR2I end = m_editPoints->Point( ARC_END ).GetPosition();
1535
1536 if( isModified( m_editPoints->Point( ARC_CENTER ) ) )
1537 {
1538 if( m_arcEditMode == ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION )
1539 {
1540 editArcCenterKeepEndpoints( shape, center, start, mid, end );
1541 }
1542 else
1543 {
1544 VECTOR2I moveVector = VECTOR2I( center.x, center.y ) - shape->GetCenter();
1545 shape->Move( moveVector );
1546 }
1547 }
1548 else if( isModified( m_editPoints->Point( ARC_MID ) ) )
1549 {
1550 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition( false );
1551
1552 if( m_arcEditMode == ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION )
1553 editArcMidKeepEndpoints( shape, start, end, cursorPos );
1554 else
1555 editArcMidKeepCenter( shape, center, start, mid, end, cursorPos );
1556 }
1557 else if( isModified( m_editPoints->Point( ARC_START ) )
1558 || isModified( m_editPoints->Point( ARC_END ) ) )
1559 {
1560 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
1561
1562 if( m_arcEditMode == ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION )
1563 editArcEndpointKeepTangent( shape, center, start, mid, end, cursorPos );
1564 else
1565 editArcEndpointKeepCenter( shape, center, start, mid, end, cursorPos );
1566 }
1567
1568 break;
1569 }
1570
1571 case SHAPE_T::CIRCLE:
1572 {
1573 const VECTOR2I& center = m_editPoints->Point( CIRC_CENTER ).GetPosition();
1574 const VECTOR2I& end = m_editPoints->Point( CIRC_END ).GetPosition();
1575
1576 if( isModified( m_editPoints->Point( CIRC_CENTER ) ) )
1577 {
1578 VECTOR2I moveVector = VECTOR2I( center.x, center.y ) - shape->GetCenter();
1579 shape->Move( moveVector );
1580 }
1581 else
1582 {
1583 shape->SetEnd( VECTOR2I( end.x, end.y ) );
1584 }
1585
1586 break;
1587 }
1588
1589 case SHAPE_T::POLY:
1590 {
1591 SHAPE_POLY_SET& outline = shape->GetPolyShape();
1592
1593 for( int i = 0; i < outline.TotalVertices(); ++i )
1594 outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
1595
1596 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1597 {
1598 if( !isModified( m_editPoints->Line( i ) ) )
1599 m_editPoints->Line( i ).SetConstraint( new EC_PERPLINE( m_editPoints->Line( i ) ) );
1600 }
1601
1602 break;
1603 }
1604
1605 case SHAPE_T::BEZIER:
1606 if( isModified( m_editPoints->Point( BEZIER_START ) ) )
1607 shape->SetStart( m_editPoints->Point( BEZIER_START ).GetPosition() );
1608 else if( isModified( m_editPoints->Point( BEZIER_CTRL_PT1 ) ) )
1609 shape->SetBezierC1( m_editPoints->Point( BEZIER_CTRL_PT1 ).GetPosition() );
1610 else if( isModified( m_editPoints->Point( BEZIER_CTRL_PT2 ) ) )
1611 shape->SetBezierC2( m_editPoints->Point( BEZIER_CTRL_PT2 ).GetPosition() );
1612 else if( isModified( m_editPoints->Point( BEZIER_END ) ) )
1613 shape->SetEnd( m_editPoints->Point( BEZIER_END ).GetPosition() );
1614
1616 break;
1617
1618 default: // suppress warnings
1619 break;
1620 }
1621
1622 if( shape->IsProxyItem() )
1623 {
1624 for( PAD* pad : shape->GetParentFootprint()->Pads() )
1625 {
1626 if( pad->IsEntered() )
1627 view()->Update( pad );
1628 }
1629 }
1630
1631 // Nuke outline font render caches
1632 if( PCB_TEXTBOX* textBox = dynamic_cast<PCB_TEXTBOX*>( item ) )
1633 textBox->ClearRenderCache();
1634
1635 break;
1636 }
1637
1638 case PCB_TABLECELL_T:
1639 {
1640 PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( item );
1641 PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
1642
1643 if( isModified( m_editPoints->Point( COL_WIDTH ) ) )
1644 {
1645 cell->SetEnd( VECTOR2I( m_editPoints->Point( 0 ).GetX(), cell->GetEndY() ) );
1646
1647 int colWidth = cell->GetRectangleWidth();
1648
1649 for( int ii = 0; ii < cell->GetColSpan() - 1; ++ii )
1650 colWidth -= table->GetColWidth( cell->GetColumn() + ii );
1651
1652 table->SetColWidth( cell->GetColumn() + cell->GetColSpan() - 1, colWidth );
1653 table->Normalize();
1654 }
1655 else if( isModified( m_editPoints->Point( ROW_HEIGHT ) ) )
1656 {
1657 cell->SetEnd( VECTOR2I( cell->GetEndX(), m_editPoints->Point( 1 ).GetY() ) );
1658
1659 int rowHeight = cell->GetRectangleHeight();
1660
1661 for( int ii = 0; ii < cell->GetRowSpan() - 1; ++ii )
1662 rowHeight -= table->GetRowHeight( cell->GetRow() + ii );
1663
1664 table->SetRowHeight( cell->GetRow() + cell->GetRowSpan() - 1, rowHeight );
1665 table->Normalize();
1666 }
1667
1668 getView()->Update( table );
1669 break;
1670 }
1671
1672 case PCB_PAD_T:
1673 {
1674 PAD* pad = static_cast<PAD*>( item );
1675
1676 // TODO(JE) padstacks
1677 switch( pad->GetShape( PADSTACK::ALL_LAYERS ) )
1678 {
1679 case PAD_SHAPE::CIRCLE:
1680 {
1681 VECTOR2I end = m_editPoints->Point( 0 ).GetPosition();
1682 int diameter = 2 * ( end - pad->GetPosition() ).EuclideanNorm();
1683
1684 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( diameter, diameter ) );
1685 break;
1686 }
1687
1688 case PAD_SHAPE::OVAL:
1689 case PAD_SHAPE::TRAPEZOID:
1690 case PAD_SHAPE::RECTANGLE:
1691 case PAD_SHAPE::ROUNDRECT:
1692 case PAD_SHAPE::CHAMFERED_RECT:
1693 {
1694 VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
1695 VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
1696 VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
1697 VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
1698 VECTOR2I holeCenter = pad->GetPosition();
1699 VECTOR2I holeSize = pad->GetDrillSize();
1700
1701 pinEditedCorner( topLeft, topRight, botLeft, botRight, holeCenter, holeSize );
1702
1703 if( ( pad->GetOffset( PADSTACK::ALL_LAYERS ).x || pad->GetOffset( PADSTACK::ALL_LAYERS ).y )
1704 || ( pad->GetDrillSize().x && pad->GetDrillSize().y ) )
1705 {
1706 // Keep hole pinned at the current location; adjust the pad around the hole
1707
1708 VECTOR2I center = pad->GetPosition();
1709 int dist[4];
1710
1711 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1712 || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1713 {
1714 dist[0] = center.x - topLeft.x;
1715 dist[1] = center.y - topLeft.y;
1716 dist[2] = botRight.x - center.x;
1717 dist[3] = botRight.y - center.y;
1718 }
1719 else
1720 {
1721 dist[0] = center.x - botLeft.x;
1722 dist[1] = center.y - topRight.y;
1723 dist[2] = topRight.x - center.x;
1724 dist[3] = botLeft.y - center.y;
1725 }
1726
1727 VECTOR2I padSize( dist[0] + dist[2], dist[1] + dist[3] );
1728 VECTOR2I deltaOffset( padSize.x / 2 - dist[2], padSize.y / 2 - dist[3] );
1729
1730 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
1731 std::swap( padSize.x, padSize.y );
1732
1733 RotatePoint( deltaOffset, -pad->GetOrientation() );
1734
1735 pad->SetSize( PADSTACK::ALL_LAYERS, padSize );
1736 pad->SetOffset( PADSTACK::ALL_LAYERS, -deltaOffset );
1737 }
1738 else
1739 {
1740 // Keep pad position at the center of the pad shape
1741
1742 int left, top, right, bottom;
1743
1744 if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) )
1745 || isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) )
1746 {
1747 left = topLeft.x;
1748 top = topLeft.y;
1749 right = botRight.x;
1750 bottom = botRight.y;
1751 }
1752 else
1753 {
1754 left = botLeft.x;
1755 top = topRight.y;
1756 right = topRight.x;
1757 bottom = botLeft.y;
1758 }
1759
1760 VECTOR2I padSize( abs( right - left ), abs( bottom - top ) );
1761
1762 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
1763 std::swap( padSize.x, padSize.y );
1764
1765 pad->SetSize( PADSTACK::ALL_LAYERS, padSize );
1766 pad->SetPosition( VECTOR2I( ( left + right ) / 2, ( top + bottom ) / 2 ) );
1767 }
1768 break;
1769 }
1770
1771 default: // suppress warnings
1772 break;
1773 }
1774
1775 break;
1776 }
1777
1778 case PCB_ZONE_T:
1779 {
1780 ZONE* zone = static_cast<ZONE*>( item );
1781 zone->UnFill();
1782 SHAPE_POLY_SET& outline = *zone->Outline();
1783
1784 for( int i = 0; i < outline.TotalVertices(); ++i )
1785 {
1786 if( outline.CVertex( i ) != m_editPoints->Point( i ).GetPosition() )
1787 zone->SetNeedRefill( true );
1788
1789 outline.SetVertex( i, m_editPoints->Point( i ).GetPosition() );
1790 }
1791
1792 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1793 {
1794 if( !isModified( m_editPoints->Line( i ) ) )
1795 m_editPoints->Line( i ).SetConstraint( new EC_PERPLINE( m_editPoints->Line( i ) ) );
1796 }
1797
1798 zone->HatchBorder();
1799 break;
1800 }
1801
1802 case PCB_GENERATOR_T:
1803 {
1804 GENERATOR_TOOL* generatorTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
1805 PCB_GENERATOR* generatorItem = static_cast<PCB_GENERATOR*>( item );
1806
1807 generatorItem->UpdateFromEditPoints( m_editPoints, aCommit );
1808 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genUpdateEdit, aCommit, generatorItem );
1809
1810 // Note: POINT_EDITOR::m_preview holds only the canvas-draw status "popup"; the meanders
1811 // themselves (ROUTER_PREVIEW_ITEMs) are owned by the router.
1812
1814
1815 for( EDA_ITEM* previewItem : generatorItem->GetPreviewItems( generatorTool, frame(),
1817 {
1818 m_preview.Add( previewItem );
1819 }
1820
1821 getView()->Update( &m_preview );
1822 break;
1823 }
1824
1825 case PCB_DIM_ALIGNED_T:
1826 {
1827 PCB_DIM_ALIGNED* dimension = static_cast<PCB_DIM_ALIGNED*>( item );
1828
1829 DIM_ALIGNED_TEXT_UPDATER textPositionUpdater( *dimension );
1830
1831 // Check which point is currently modified and updated dimension's points respectively
1832 if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) )
1833 {
1834 VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetStart() );
1835 VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1836
1837 if( featureLine.Cross( crossBar ) > 0 )
1838 dimension->SetHeight( -featureLine.EuclideanNorm() );
1839 else
1840 dimension->SetHeight( featureLine.EuclideanNorm() );
1841
1842 dimension->Update();
1843 }
1844 else if( isModified( m_editPoints->Point( DIM_CROSSBAREND ) ) )
1845 {
1846 VECTOR2D featureLine( m_editedPoint->GetPosition() - dimension->GetEnd() );
1847 VECTOR2D crossBar( dimension->GetEnd() - dimension->GetStart() );
1848
1849 if( featureLine.Cross( crossBar ) > 0 )
1850 dimension->SetHeight( -featureLine.EuclideanNorm() );
1851 else
1852 dimension->SetHeight( featureLine.EuclideanNorm() );
1853
1854 dimension->Update();
1855 }
1856 else if( isModified( m_editPoints->Point( DIM_START ) ) )
1857 {
1858 dimension->SetStart( m_editedPoint->GetPosition() );
1859 dimension->Update();
1860
1862 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1863 m_editPoints->Point( DIM_START ) ) );
1864 m_editPoints->Point( DIM_CROSSBAREND ).
1865 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1866 m_editPoints->Point( DIM_END ) ) );
1867 }
1868 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1869 {
1870 dimension->SetEnd( m_editedPoint->GetPosition() );
1871 dimension->Update();
1872
1874 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBARSTART ),
1875 m_editPoints->Point( DIM_START ) ) );
1876 m_editPoints->Point( DIM_CROSSBAREND ).
1877 SetConstraint( new EC_LINE( m_editPoints->Point( DIM_CROSSBAREND ),
1878 m_editPoints->Point( DIM_END ) ) );
1879 }
1880 else if( isModified( m_editPoints->Point(DIM_TEXT ) ) )
1881 {
1882 // Force manual mode if we weren't already in it
1883 dimension->SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
1884 dimension->SetTextPos( m_editedPoint->GetPosition() );
1885 dimension->Update();
1886 }
1887
1888 textPositionUpdater.UpdateTextAfterChange();
1889 break;
1890 }
1891
1893 {
1894 PCB_DIM_ORTHOGONAL* dimension = static_cast<PCB_DIM_ORTHOGONAL*>( item );
1895
1896 DIM_ALIGNED_TEXT_UPDATER textPositionUpdater( *dimension );
1897
1898 if( isModified( m_editPoints->Point( DIM_CROSSBARSTART ) ) ||
1900 {
1901 BOX2I bounds( dimension->GetStart(), dimension->GetEnd() - dimension->GetStart() );
1902
1903 const VECTOR2I& cursorPos = m_editedPoint->GetPosition();
1904
1905 // Find vector from nearest dimension point to edit position
1906 VECTOR2I directionA( cursorPos - dimension->GetStart() );
1907 VECTOR2I directionB( cursorPos - dimension->GetEnd() );
1908 VECTOR2I direction = ( directionA < directionB ) ? directionA : directionB;
1909
1910 bool vert;
1911 VECTOR2D featureLine( cursorPos - dimension->GetStart() );
1912
1913 // Only change the orientation when we move outside the bounds
1914 if( !bounds.Contains( cursorPos ) )
1915 {
1916 // If the dimension is horizontal or vertical, set correct orientation
1917 // otherwise, test if we're left/right of the bounding box or above/below it
1918 if( bounds.GetWidth() == 0 )
1919 vert = true;
1920 else if( bounds.GetHeight() == 0 )
1921 vert = false;
1922 else if( cursorPos.x > bounds.GetLeft() && cursorPos.x < bounds.GetRight() )
1923 vert = false;
1924 else if( cursorPos.y > bounds.GetTop() && cursorPos.y < bounds.GetBottom() )
1925 vert = true;
1926 else
1927 vert = std::abs( direction.y ) < std::abs( direction.x );
1928
1931 }
1932 else
1933 {
1935 }
1936
1937 dimension->SetHeight( vert ? featureLine.x : featureLine.y );
1938 }
1939 else if( isModified( m_editPoints->Point( DIM_START ) ) )
1940 {
1941 dimension->SetStart( m_editedPoint->GetPosition() );
1942 }
1943 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1944 {
1945 dimension->SetEnd( m_editedPoint->GetPosition() );
1946 }
1947 else if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
1948 {
1949 // Force manual mode if we weren't already in it
1950 dimension->SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
1951 dimension->SetTextPos( VECTOR2I( m_editedPoint->GetPosition() ) );
1952 }
1953
1954 dimension->Update();
1955
1956 // After recompute, find the new text position
1957 textPositionUpdater.UpdateTextAfterChange();
1958
1959 break;
1960 }
1961
1962 case PCB_DIM_CENTER_T:
1963 {
1964 PCB_DIM_CENTER* dimension = static_cast<PCB_DIM_CENTER*>( item );
1965
1966 if( isModified( m_editPoints->Point( DIM_START ) ) )
1967 dimension->SetStart( m_editedPoint->GetPosition() );
1968 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1969 dimension->SetEnd( m_editedPoint->GetPosition() );
1970
1971 dimension->Update();
1972
1973 break;
1974 }
1975
1976 case PCB_DIM_RADIAL_T:
1977 {
1978 PCB_DIM_RADIAL* dimension = static_cast<PCB_DIM_RADIAL*>( item );
1979
1980 if( isModified( m_editPoints->Point( DIM_START ) ) )
1981 {
1982 dimension->SetStart( m_editedPoint->GetPosition() );
1983 dimension->Update();
1984
1985 m_editPoints->Point( DIM_KNEE ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_START ),
1986 m_editPoints->Point( DIM_END ) ) );
1987 }
1988 else if( isModified( m_editPoints->Point( DIM_END ) ) )
1989 {
1990 VECTOR2I oldKnee = dimension->GetKnee();
1991
1992 dimension->SetEnd( m_editedPoint->GetPosition() );
1993 dimension->Update();
1994
1995 VECTOR2I kneeDelta = dimension->GetKnee() - oldKnee;
1996 dimension->SetTextPos( dimension->GetTextPos() + kneeDelta );
1997 dimension->Update();
1998
1999 m_editPoints->Point( DIM_KNEE ).SetConstraint( new EC_LINE( m_editPoints->Point( DIM_START ),
2000 m_editPoints->Point( DIM_END ) ) );
2001 }
2002 else if( isModified( m_editPoints->Point( DIM_KNEE ) ) )
2003 {
2004 VECTOR2I oldKnee = dimension->GetKnee();
2005 VECTOR2I arrowVec = m_editPoints->Point( DIM_KNEE ).GetPosition()
2006 - m_editPoints->Point( DIM_END ).GetPosition();
2007
2008 dimension->SetLeaderLength( arrowVec.EuclideanNorm() );
2009 dimension->Update();
2010
2011 VECTOR2I kneeDelta = dimension->GetKnee() - oldKnee;
2012 dimension->SetTextPos( dimension->GetTextPos() + kneeDelta );
2013 dimension->Update();
2014 }
2015 else if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
2016 {
2017 dimension->SetTextPos( m_editedPoint->GetPosition() );
2018 dimension->Update();
2019 }
2020
2021 break;
2022 }
2023
2024 case PCB_DIM_LEADER_T:
2025 {
2026 PCB_DIM_LEADER* dimension = static_cast<PCB_DIM_LEADER*>( item );
2027
2028 if( isModified( m_editPoints->Point( DIM_START ) ) )
2029 {
2030 dimension->SetStart( (VECTOR2I) m_editedPoint->GetPosition() );
2031 }
2032 else if( isModified( m_editPoints->Point( DIM_END ) ) )
2033 {
2034 VECTOR2I newPoint( m_editedPoint->GetPosition() );
2035 VECTOR2I delta = newPoint - dimension->GetEnd();
2036
2037 dimension->SetEnd( newPoint );
2038 dimension->SetTextPos( dimension->GetTextPos() + delta );
2039 }
2040 else if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
2041 {
2042 dimension->SetTextPos( (VECTOR2I) m_editedPoint->GetPosition() );
2043 }
2044
2045 dimension->Update();
2046
2047 break;
2048 }
2049
2050 default:
2051 break;
2052 }
2053
2054 getView()->Update( item );
2055
2056 frame()->SetMsgPanel( item );
2057}
2058
2059
2061{
2062 if( !m_editPoints )
2063 return;
2064
2065 EDA_ITEM* item = m_editPoints->GetParent();
2066
2067 if( !item )
2068 return;
2069
2070 switch( item->Type() )
2071 {
2073 {
2074 const REFERENCE_IMAGE& refImg =
2075 static_cast<const PCB_REFERENCE_IMAGE&>( *item ).GetReferenceImage();
2076 const VECTOR2I topLeft = refImg.GetPosition() - refImg.GetSize() / 2;
2077 const VECTOR2I botRight = refImg.GetPosition() + refImg.GetSize() / 2;
2078
2079 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( topLeft );
2080 m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
2081 m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
2082 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( botRight );
2083
2084 m_editPoints->Point( REFIMG_ORIGIN )
2085 .SetPosition( refImg.GetPosition() + refImg.GetTransformOriginOffset() );
2086
2087 break;
2088 }
2089
2090 case PCB_TEXTBOX_T:
2091 {
2092 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
2093 int target = shape->GetShape() == SHAPE_T::RECTANGLE ? 4 : 0;
2094
2095 // Careful; textbox shape is mutable between cardinal and non-cardinal rotations...
2096 if( int( m_editPoints->PointsSize() ) != target )
2097 {
2098 getView()->Remove( m_editPoints.get() );
2099 m_editedPoint = nullptr;
2100
2101 m_editPoints = makePoints( item );
2102
2103 if( m_editPoints )
2104 getView()->Add( m_editPoints.get() );
2105
2106 break;
2107 }
2108
2109 if( shape->GetShape() == SHAPE_T::RECTANGLE )
2110 {
2111 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shape->GetTopLeft() );
2112 m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( shape->GetBotRight().x,
2113 shape->GetTopLeft().y );
2114 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shape->GetBotRight() );
2115 m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( shape->GetTopLeft().x,
2116 shape->GetBotRight().y );
2117 }
2118 else if( shape->GetShape() == SHAPE_T::POLY )
2119 {
2120 // Not currently editable while rotated.
2121 }
2122
2123 break;
2124 }
2125
2126 case PCB_SHAPE_T:
2127 {
2128 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
2129
2130 switch( shape->GetShape() )
2131 {
2132 case SHAPE_T::SEGMENT:
2133 m_editPoints->Point( SEG_START ).SetPosition( shape->GetStart() );
2134 m_editPoints->Point( SEG_END ).SetPosition( shape->GetEnd() );
2135 break;
2136
2137 case SHAPE_T::RECTANGLE:
2138 {
2139 VECTOR2I topLeft = shape->GetTopLeft();
2140 VECTOR2I botRight = shape->GetBotRight();
2141
2142 m_editPoints->SetSwapX( topLeft.x > botRight.x );
2143 m_editPoints->SetSwapY( topLeft.y > botRight.y );
2144
2145 if( m_editPoints->SwapX() )
2146 std::swap( topLeft.x, botRight.x );
2147
2148 if( m_editPoints->SwapY() )
2149 std::swap( topLeft.y, botRight.y );
2150
2151 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( topLeft );
2152 m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
2153 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( botRight );
2154 m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
2155 break;
2156 }
2157
2158 case SHAPE_T::ARC:
2159 m_editPoints->Point( ARC_CENTER ).SetPosition( shape->GetCenter() );
2160 m_editPoints->Point( ARC_START ).SetPosition( shape->GetStart() );
2161 m_editPoints->Point( ARC_MID ).SetPosition( shape->GetArcMid() );
2162 m_editPoints->Point( ARC_END ).SetPosition( shape->GetEnd() );
2163 break;
2164
2165 case SHAPE_T::CIRCLE:
2166 m_editPoints->Point( CIRC_CENTER ).SetPosition( shape->GetCenter() );
2167 m_editPoints->Point( CIRC_END ).SetPosition( shape->GetEnd() );
2168 break;
2169
2170 case SHAPE_T::POLY:
2171 {
2172 std::vector<VECTOR2I> points;
2173 shape->DupPolyPointsList( points );
2174
2175 if( m_editPoints->PointsSize() != (unsigned) points.size() )
2176 {
2177 getView()->Remove( m_editPoints.get() );
2178 m_editedPoint = nullptr;
2179
2180 m_editPoints = makePoints( item );
2181
2182 if( m_editPoints )
2183 getView()->Add( m_editPoints.get() );
2184 }
2185 else
2186 {
2187 for( unsigned i = 0; i < points.size(); i++ )
2188 m_editPoints->Point( i ).SetPosition( points[i] );
2189 }
2190
2191 break;
2192 }
2193
2194 case SHAPE_T::BEZIER:
2195 m_editPoints->Point( BEZIER_START ).SetPosition( shape->GetStart() );
2196 m_editPoints->Point( BEZIER_CTRL_PT1 ).SetPosition( shape->GetBezierC1() );
2197 m_editPoints->Point( BEZIER_CTRL_PT2 ).SetPosition( shape->GetBezierC2() );
2198 m_editPoints->Point( BEZIER_END ).SetPosition( shape->GetEnd() );
2199 break;
2200
2201 default: // suppress warnings
2202 break;
2203 }
2204
2205 break;
2206 }
2207
2208 case PCB_TABLECELL_T:
2209 {
2210 PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( item );
2211
2212 m_editPoints->Point( 0 ).SetPosition( cell->GetEndX(),
2213 cell->GetEndY() - cell->GetRectangleHeight() / 2 );
2214 m_editPoints->Point( 1 ).SetPosition( cell->GetEndX() - cell->GetRectangleWidth() / 2,
2215 cell->GetEndY() );
2216 break;
2217 }
2218
2219 case PCB_PAD_T:
2220 {
2221 // TODO(JE) padstacks
2222 const PAD* pad = static_cast<const PAD*>( item );
2223 bool locked = pad->GetParent() && pad->IsLocked();
2224 VECTOR2I shapePos = pad->ShapePos( PADSTACK::ALL_LAYERS );
2225 VECTOR2I halfSize( pad->GetSize( PADSTACK::ALL_LAYERS ).x / 2, pad->GetSize( PADSTACK::ALL_LAYERS ).y / 2 );
2226
2227 switch( pad->GetShape( PADSTACK::ALL_LAYERS ) )
2228 {
2229 case PAD_SHAPE::CIRCLE:
2230 {
2231 int target = locked ? 0 : 1;
2232
2233 // Careful; pad shape is mutable...
2234 if( int( m_editPoints->PointsSize() ) != target )
2235 {
2236 getView()->Remove( m_editPoints.get() );
2237 m_editedPoint = nullptr;
2238
2239 m_editPoints = makePoints( item );
2240
2241 if( m_editPoints )
2242 getView()->Add( m_editPoints.get() );
2243 }
2244 else if( target == 1 )
2245 {
2246 shapePos.x += halfSize.x;
2247 m_editPoints->Point( 0 ).SetPosition( shapePos );
2248 }
2249 }
2250 break;
2251
2252 case PAD_SHAPE::OVAL:
2253 case PAD_SHAPE::TRAPEZOID:
2254 case PAD_SHAPE::RECTANGLE:
2255 case PAD_SHAPE::ROUNDRECT:
2256 case PAD_SHAPE::CHAMFERED_RECT:
2257 {
2258 // Careful; pad shape and orientation are mutable...
2259 int target = locked || !pad->GetOrientation().IsCardinal() ? 0 : 4;
2260
2261 if( int( m_editPoints->PointsSize() ) != target )
2262 {
2263 getView()->Remove( m_editPoints.get() );
2264 m_editedPoint = nullptr;
2265
2266 m_editPoints = makePoints( item );
2267
2268 if( m_editPoints )
2269 getView()->Add( m_editPoints.get() );
2270 }
2271 else if( target == 4 )
2272 {
2273 if( pad->GetOrientation() == ANGLE_90 || pad->GetOrientation() == ANGLE_270 )
2274 std::swap( halfSize.x, halfSize.y );
2275
2276 m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( shapePos - halfSize );
2278 .SetPosition(
2279 VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
2280 m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( shapePos + halfSize );
2281 m_editPoints->Point( RECT_BOT_LEFT )
2282 .SetPosition(
2283 VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
2284 }
2285
2286 break;
2287 }
2288
2289 default: // suppress warnings
2290 break;
2291 }
2292 }
2293 break;
2294
2295 case PCB_ZONE_T:
2296 {
2297 ZONE* zone = static_cast<ZONE*>( item );
2298 const SHAPE_POLY_SET* outline = zone->Outline();
2299
2300 if( m_editPoints->PointsSize() != (unsigned) outline->TotalVertices() )
2301 {
2302 getView()->Remove( m_editPoints.get() );
2303 m_editedPoint = nullptr;
2304
2305 m_editPoints = makePoints( item );
2306
2307 if( m_editPoints )
2308 getView()->Add( m_editPoints.get() );
2309 }
2310 else
2311 {
2312 for( int i = 0; i < outline->TotalVertices(); ++i )
2313 m_editPoints->Point( i ).SetPosition( outline->CVertex( i ) );
2314 }
2315
2316 break;
2317 }
2318
2319 case PCB_GENERATOR_T:
2320 {
2321 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( item );
2322 generator->UpdateEditPoints( m_editPoints );
2323 break;
2324 }
2325
2326 case PCB_DIM_ALIGNED_T:
2328 {
2329 const PCB_DIM_ALIGNED* dimension = static_cast<const PCB_DIM_ALIGNED*>( item );
2330
2331 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
2332 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
2333 m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->GetTextPos() );
2334 m_editPoints->Point( DIM_CROSSBARSTART ).SetPosition( dimension->GetCrossbarStart() );
2335 m_editPoints->Point( DIM_CROSSBAREND ).SetPosition( dimension->GetCrossbarEnd() );
2336 break;
2337 }
2338
2339 case PCB_DIM_CENTER_T:
2340 {
2341 const PCB_DIM_CENTER* dimension = static_cast<const PCB_DIM_CENTER*>( item );
2342
2343 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
2344 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
2345 break;
2346 }
2347
2348 case PCB_DIM_RADIAL_T:
2349 {
2350 const PCB_DIM_RADIAL* dimension = static_cast<const PCB_DIM_RADIAL*>( item );
2351
2352 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
2353 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
2354 m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->GetTextPos() );
2355 m_editPoints->Point( DIM_KNEE ).SetPosition( dimension->GetKnee() );
2356 break;
2357 }
2358
2359 case PCB_DIM_LEADER_T:
2360 {
2361 const PCB_DIM_LEADER* dimension = static_cast<const PCB_DIM_LEADER*>( item );
2362
2363 m_editPoints->Point( DIM_START ).SetPosition( dimension->GetStart() );
2364 m_editPoints->Point( DIM_END ).SetPosition( dimension->GetEnd() );
2365 m_editPoints->Point( DIM_TEXT ).SetPosition( dimension->GetTextPos() );
2366 break;
2367 }
2368
2369 default:
2370 break;
2371 }
2372
2373 getView()->Update( m_editPoints.get() );
2374}
2375
2376
2378{
2380
2381 if( aPoint )
2382 {
2383 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2384 controls->ForceCursorPosition( true, aPoint->GetPosition() );
2385 controls->ShowCursor( true );
2386 }
2387 else
2388 {
2389 if( frame()->ToolStackIsEmpty() )
2390 controls->ShowCursor( false );
2391
2392 controls->ForceCursorPosition( false );
2393 }
2394
2395 m_editedPoint = aPoint;
2396}
2397
2398
2400{
2401 if( aEnabled )
2402 {
2403 EDA_ITEM* parent = m_editPoints->GetParent();
2404 EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
2405 bool isPoly;
2406
2407 switch( parent->Type() )
2408 {
2409 case PCB_ZONE_T:
2410 isPoly = true;
2411 break;
2412
2413 case PCB_SHAPE_T:
2414 isPoly = static_cast<PCB_SHAPE*>( parent )->GetShape() == SHAPE_T::POLY;
2415 break;
2416
2417 default:
2418 isPoly = false;
2419 break;
2420 }
2421
2422 if( line && isPoly )
2423 {
2424 EC_CONVERGING* altConstraint = new EC_CONVERGING( *line, *m_editPoints );
2425 m_altConstraint.reset( (EDIT_CONSTRAINT<EDIT_POINT>*) altConstraint );
2426 }
2427 else
2428 {
2429 // Find a proper constraining point for 45 degrees mode
2432 }
2433 }
2434 else
2435 {
2436 m_altConstraint.reset();
2437 }
2438}
2439
2440
2442{
2443 EDA_ITEM* item = m_editPoints->GetParent();
2444
2445 switch( item->Type() )
2446 {
2447 case PCB_SHAPE_T:
2448 switch( static_cast<const PCB_SHAPE*>( item )->GetShape() )
2449 {
2450 case SHAPE_T::SEGMENT:
2451 return *( m_editPoints->Next( *m_editedPoint ) ); // select the other end of line
2452
2453 case SHAPE_T::ARC:
2454 case SHAPE_T::CIRCLE:
2455 return m_editPoints->Point( CIRC_CENTER );
2456
2457 default: // suppress warnings
2458 break;
2459 }
2460
2461 break;
2462
2463 case PCB_DIM_ALIGNED_T:
2464 {
2465 // Constraint for crossbar
2466 if( isModified( m_editPoints->Point( DIM_START ) ) )
2467 return m_editPoints->Point( DIM_END );
2468
2469 else if( isModified( m_editPoints->Point( DIM_END ) ) )
2470 return m_editPoints->Point( DIM_START );
2471
2472 else
2473 return EDIT_POINT( m_editedPoint->GetPosition() ); // no constraint
2474
2475 break;
2476 }
2477
2478 case PCB_DIM_CENTER_T:
2479 {
2480 if( isModified( m_editPoints->Point( DIM_END ) ) )
2481 return m_editPoints->Point( DIM_START );
2482
2483 break;
2484 }
2485
2486 case PCB_DIM_RADIAL_T:
2487 {
2488 if( isModified( m_editPoints->Point( DIM_TEXT ) ) )
2489 return m_editPoints->Point( DIM_KNEE );
2490
2491 break;
2492 }
2493
2494 default:
2495 break;
2496 }
2497
2498 // In any other case we may align item to its original position
2499 return m_original;
2500}
2501
2502
2504{
2505 const auto type = aItem.Type();
2506
2507 // Works only for zones and line segments
2508 if( type == PCB_ZONE_T )
2509 return true;
2510
2511 if( type == PCB_SHAPE_T )
2512 {
2513 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
2514 const SHAPE_T shapeType = shape.GetShape();
2515 return shapeType == SHAPE_T::SEGMENT || shapeType == SHAPE_T::POLY
2516 || shapeType == SHAPE_T::ARC;
2517 }
2518
2519 return false;
2520}
2521
2522
2524{
2525 if( aSelection.Size() != 1 )
2526 return false;
2527
2528 const EDA_ITEM* item = aSelection.Front();
2529
2530 return ( item != nullptr ) && canAddCorner( *item );
2531}
2532
2533
2534// Finds a corresponding vertex in a polygon set
2535static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
2536findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
2537{
2538 for( auto it = aPolySet.IterateWithHoles(); it; ++it )
2539 {
2540 auto vertexIdx = it.GetIndex();
2541
2542 if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
2543 return std::make_pair( true, vertexIdx );
2544 }
2545
2546 return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
2547}
2548
2549
2551{
2552 if( !m_editPoints || !m_editedPoint )
2553 return false;
2554
2555 EDA_ITEM* item = m_editPoints->GetParent();
2556 SHAPE_POLY_SET* polyset = nullptr;
2557
2558 if( !item )
2559 return false;
2560
2561 switch( item->Type() )
2562 {
2563 case PCB_ZONE_T:
2564 polyset = static_cast<ZONE*>( item )->Outline();
2565 break;
2566
2567 case PCB_SHAPE_T:
2568 if( static_cast<PCB_SHAPE*>( item )->GetShape() == SHAPE_T::POLY )
2569 polyset = &static_cast<PCB_SHAPE*>( item )->GetPolyShape();
2570 else
2571 return false;
2572
2573 break;
2574
2575 default:
2576 return false;
2577 }
2578
2579 std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX> vertex = findVertex( *polyset, *m_editedPoint );
2580
2581 if( !vertex.first )
2582 return false;
2583
2584 const SHAPE_POLY_SET::VERTEX_INDEX& vertexIdx = vertex.second;
2585
2586 // Check if there are enough vertices so one can be removed without
2587 // degenerating the polygon.
2588 // The first condition allows one to remove all corners from holes (when
2589 // there are only 2 vertices left, a hole is removed).
2590 if( vertexIdx.m_contour == 0 &&
2591 polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
2592 {
2593 return false;
2594 }
2595
2596 // Remove corner does not work with lines
2597 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2598 return false;
2599
2600 return m_editedPoint != nullptr;
2601}
2602
2603
2605{
2606 if( !m_editPoints )
2607 return 0;
2608
2609 EDA_ITEM* item = m_editPoints->GetParent();
2610 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2611 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
2612
2613 // called without an active edited polygon
2614 if( !item || !canAddCorner( *item ) )
2615 return 0;
2616
2617 PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
2618 BOARD_COMMIT commit( frame );
2619
2620 if( item->Type() == PCB_ZONE_T || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
2621 {
2622 unsigned int nearestIdx = 0;
2623 unsigned int nextNearestIdx = 0;
2624 unsigned int nearestDist = INT_MAX;
2625 unsigned int firstPointInContour = 0;
2626 SHAPE_POLY_SET* zoneOutline;
2627
2628 if( item->Type() == PCB_ZONE_T )
2629 {
2630 ZONE* zone = static_cast<ZONE*>( item );
2631 zoneOutline = zone->Outline();
2632 zone->SetNeedRefill( true );
2633 }
2634 else
2635 {
2636 zoneOutline = &( graphicItem->GetPolyShape() );
2637 }
2638
2639 commit.Modify( item );
2640
2641 // Search the best outline segment to add a new corner
2642 // and therefore break this segment into two segments
2643
2644 // Object to iterate through the corners of the outlines (main contour and its holes)
2645 SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0, zoneOutline->OutlineCount()-1,
2646 /* IterateHoles */ true );
2647 int curr_idx = 0;
2648
2649 // Iterate through all the corners of the outlines and search the best segment
2650 for( ; iterator; iterator++, curr_idx++ )
2651 {
2652 int jj = curr_idx+1;
2653
2654 if( iterator.IsEndContour() )
2655 { // We reach the last point of the current contour (main or hole)
2656 jj = firstPointInContour;
2657 firstPointInContour = curr_idx+1; // Prepare next contour analysis
2658 }
2659
2660 SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
2661
2662 unsigned int distance = curr_segment.Distance( cursorPos );
2663
2664 if( distance < nearestDist )
2665 {
2666 nearestDist = distance;
2667 nearestIdx = curr_idx;
2668 nextNearestIdx = jj;
2669 }
2670 }
2671
2672 // Find the point on the closest segment
2673 const VECTOR2I& sideOrigin = zoneOutline->CVertex( nearestIdx );
2674 const VECTOR2I& sideEnd = zoneOutline->CVertex( nextNearestIdx );
2675 SEG nearestSide( sideOrigin, sideEnd );
2676 VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
2677
2678 // Do not add points that have the same coordinates as ones that already belong to polygon
2679 // instead, add a point in the middle of the side
2680 if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
2681 nearestPoint = ( sideOrigin + sideEnd ) / 2;
2682
2683 zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
2684
2685 // We re-hatch the filled zones but not polygons
2686 if( item->Type() == PCB_ZONE_T )
2687 static_cast<ZONE*>( item )->HatchBorder();
2688
2689 commit.Push( _( "Add Zone Corner" ) );
2690 }
2691 else if( graphicItem )
2692 {
2693 switch( graphicItem->GetShape() )
2694 {
2695 case SHAPE_T::SEGMENT:
2696 {
2697 commit.Modify( graphicItem );
2698
2699 SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
2700 VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
2701
2702 // Move the end of the line to the break point..
2703 graphicItem->SetEnd( nearestPoint );
2704
2705 // and add another one starting from the break point
2706 auto newSegment = std::make_unique<PCB_SHAPE>( *graphicItem );
2707
2708 newSegment->ClearSelected();
2709 newSegment->SetStart( nearestPoint );
2710 newSegment->SetEnd( VECTOR2I( seg.B.x, seg.B.y ) );
2711
2712 commit.Add( newSegment.release() );
2713 commit.Push( _( "Split Segment" ) );
2714 break;
2715 }
2716 case SHAPE_T::ARC:
2717 {
2718 commit.Modify( graphicItem );
2719
2720 const SHAPE_ARC arc( graphicItem->GetStart(), graphicItem->GetArcMid(),
2721 graphicItem->GetEnd(), 0 );
2722 const VECTOR2I nearestPoint = arc.NearestPoint( cursorPos );
2723
2724 // Move the end of the arc to the break point..
2725 graphicItem->SetEnd( nearestPoint );
2726
2727 // and add another one starting from the break point
2728 auto newArc = std::make_unique<PCB_SHAPE>( *graphicItem );
2729
2730 newArc->ClearSelected();
2731 newArc->SetEnd( arc.GetP1() );
2732 newArc->SetStart( nearestPoint );
2733
2734 commit.Add( newArc.release() );
2735 commit.Push( _( "Split Arc" ) );
2736 break;
2737 }
2738 default:
2739 // No split implemented for other shapes
2740 break;
2741 }
2742 }
2743
2744 updatePoints();
2745 return 0;
2746}
2747
2748
2750{
2751 if( !m_editPoints || !m_editedPoint )
2752 return 0;
2753
2754 EDA_ITEM* item = m_editPoints->GetParent();
2755
2756 if( !item )
2757 return 0;
2758
2759 SHAPE_POLY_SET* polygon = nullptr;
2760
2761 if( item->Type() == PCB_ZONE_T )
2762 {
2763 ZONE* zone = static_cast<ZONE*>( item );
2764 polygon = zone->Outline();
2765 zone->SetNeedRefill( true );
2766 }
2767 else if( item->Type() == PCB_SHAPE_T )
2768 {
2769 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2770
2771 if( shape->GetShape() == SHAPE_T::POLY )
2772 polygon = &shape->GetPolyShape();
2773 }
2774
2775 if( !polygon )
2776 return 0;
2777
2778 PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
2779 BOARD_COMMIT commit( frame );
2780 auto vertex = findVertex( *polygon, *m_editedPoint );
2781
2782 if( vertex.first )
2783 {
2784 const auto& vertexIdx = vertex.second;
2785 auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
2786
2787 if( outline.PointCount() > 3 )
2788 {
2789 // the usual case: remove just the corner when there are >3 vertices
2790 commit.Modify( item );
2791 polygon->RemoveVertex( vertexIdx );
2792 }
2793 else
2794 {
2795 // either remove a hole or the polygon when there are <= 3 corners
2796 if( vertexIdx.m_contour > 0 )
2797 {
2798 // remove hole
2799 commit.Modify( item );
2800 polygon->RemoveContour( vertexIdx.m_contour );
2801 }
2802 else
2803 {
2805 commit.Remove( item );
2806 }
2807 }
2808
2809 setEditedPoint( nullptr );
2810
2811 if( item->Type() == PCB_ZONE_T )
2812 commit.Push( _( "Remove Zone Corner" ) );
2813 else
2814 commit.Push( _( "Remove Polygon Corner" ) );
2815
2816 // Refresh zone hatching
2817 if( item->Type() == PCB_ZONE_T )
2818 static_cast<ZONE*>( item )->HatchBorder();
2819
2820 updatePoints();
2821 }
2822
2823 return 0;
2824}
2825
2826
2828{
2829 updatePoints();
2830 return 0;
2831}
2832
2833
2835{
2836 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2837
2839 {
2840 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2842 else
2844
2845 switch( m_arcEditMode )
2846 {
2847 case ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS:
2848 m_arcEditMode = ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION;
2849 break;
2850 case ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION:
2851 m_arcEditMode = ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS;
2852 break;
2853 }
2854 }
2855 else
2856 {
2858 }
2859
2860 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2862 else
2864
2865 return 0;
2866}
2867
2868
2870{
2886}
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:80
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:299
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:258
virtual bool IsLocked() const
Definition: board_item.cpp:75
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:216
constexpr size_type GetWidth() const
Definition: box2.h:214
constexpr size_type GetHeight() const
Definition: box2.h:215
constexpr coord_type GetLeft() const
Definition: box2.h:228
constexpr bool Contains(const Vec &aPoint) const
Definition: box2.h:168
constexpr coord_type GetRight() const
Definition: box2.h:217
constexpr coord_type GetTop() const
Definition: box2.h:229
constexpr coord_type GetBottom() const
Definition: box2.h:222
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.
Class to help update the text position of a dimension when the crossbar changes.
DIM_ALIGNED_TEXT_UPDATER(PCB_DIM_ALIGNED &aDimension)
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
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
virtual EDA_ITEM * Clone() const
Create a duplicate of this item with linked list members set to NULL.
Definition: eda_item.cpp:85
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:527
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:474
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:1302
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:607
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:545
const VECTOR2I & GetTextPos() const
Definition: eda_text.h:253
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:449
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition: eda_text.h:183
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:298
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:284
static const TOOL_EVENT SelectedEvent
Definition: actions.h:271
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:278
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition: actions.h:285
static const TOOL_EVENT PointSelectedEvent
Definition: actions.h:270
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:281
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:272
std::deque< PAD * > & Pads()
Definition: footprint.h:205
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:91
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:36
static LSET AllLayersMask()
Definition: lset.cpp:701
T ToDisplayAbs(const T &aValue) const
T FromDisplayAbs(const T &aValue) const
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:138
Definition: pad.h:54
ARC_EDIT_MODE m_ArcEditMode
static TOOL_ACTION pointEditorArcKeepCenter
Definition: pcb_actions.h:298
static TOOL_ACTION pointEditorMoveMidpoint
Definition: pcb_actions.h:302
static TOOL_ACTION genPushEdit
Definition: pcb_actions.h:288
static TOOL_ACTION genStartEdit
Definition: pcb_actions.h:286
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:68
static TOOL_ACTION pointEditorMoveCorner
Definition: pcb_actions.h:301
static TOOL_ACTION genUpdateEdit
Definition: pcb_actions.h:287
static TOOL_ACTION pointEditorArcKeepEndpoint
Definition: pcb_actions.h:299
static TOOL_ACTION pointEditorRemoveCorner
Definition: pcb_actions.h:296
static TOOL_ACTION pointEditorAddCorner
Definition: pcb_actions.h:295
static TOOL_ACTION reselectItem
Definition: pcb_actions.h:73
static TOOL_ACTION genRevertEdit
Definition: pcb_actions.h:289
Common, abstract interface for edit frames.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
ORIGIN_TRANSFORMS & GetOriginTransforms() override
Return a reference to the default ORIGIN_TRANSFORMS object.
PCBNEW_SETTINGS * GetPcbNewSettings() const
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
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.
REFERENCE_IMAGE & GetReferenceImage()
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:74
bool IsProxyItem() const override
Definition: pcb_shape.h:109
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: pcb_shape.cpp:468
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
T * frame() const
KIGFX::PCB_VIEW * view() const
KIGFX::VIEW_CONTROLS * controls() const
bool m_isFootprintEditor
const PCB_SELECTION & selection() const
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
void SetTransformOriginOffset(const VECTOR2I &aCenter)
VECTOR2I GetTransformOriginOffset() const
Get the center of scaling, etc, relative to the image center (GetPosition()).
VECTOR2I GetPosition() const
VECTOR2I GetSize() const
double GetImageScale() const
void SetImageScale(double aScale)
Set the image "zoom" value.
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
VECTOR2I Center() const
Definition: seg.h:369
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
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition: seg.cpp:371
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
VECTOR2I NearestPoint(const VECTOR2I &aP) const
Definition: shape_arc.cpp:417
const VECTOR2I & GetP1() const
Definition: shape_arc.h:115
Base class for iterating over all vertices in a given SHAPE_POLY_SET.
Represent a set of closed polygons.
ITERATOR IterateWithHoles(int aOutline)
void InsertVertex(int aGlobalIndex, const VECTOR2I &aNewVertex)
Adds a vertex in the globally indexed position aGlobalIndex.
void SetVertex(const VERTEX_INDEX &aIndex, const VECTOR2I &aPos)
Accessor function to set the position of a specific point.
int TotalVertices() const
Return total number of vertices stored in the set.
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
void RemoveVertex(int aGlobalIndex)
Delete the aGlobalIndex-th vertex.
void RemoveContour(int aContourIdx, int aPolygonIdx=-1)
Delete the aContourIdx-th contour of the aPolygonIdx-th polygon in the set.
ITERATOR Iterate(int aFirst, int aLast, bool aIterateHoles=false)
Return an object to iterate through the points of the polygons between aFirst and aLast.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
int OutlineCount() const
Return the number of outlines in the set.
CONST_ITERATOR CIterateWithHoles(int aOutline) const
TOOL_EVENT MakeEvent() const
Return the event associated with the action (i.e.
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h: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
constexpr extended_type Cross(const VECTOR2< T > &aVector) const
Compute cross product of self with aVector.
Definition: vector2d.h:542
static constexpr extended_type ECOORD_MAX
Definition: vector2d.h:76
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:283
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:385
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:226
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:945
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
SHAPE_T
Definition: eda_shape.h:42
@ 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
@ REFIMG_ORIGIN
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
a few functions useful in geometry calculations.
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
bool PointProjectsOntoSegment(const VECTOR2I &aPoint, const SEG &aSeg)
Determine if a point projects onto a segment.
double GetLengthRatioFromStart(const VECTOR2I &aPoint, const SEG &aSeg)
Get the ratio of the vector to a point from the segment's start, compared to the segment's length.
const VECTOR2I & GetNearestEndpoint(const SEG &aSeg, const VECTOR2I &aPoint)
Get the nearest end of a segment to a point.
bool PointIsInDirection(const VECTOR2< T > &aPoint, const VECTOR2< T > &aDirection, const VECTOR2< T > &aFrom)
Definition: vector_utils.h:57
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
REFIMG_POINTS
@ REFIMG_ORIGIN
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)
VECTOR2I v4(1, 1)
VECTOR2I v3(-2, 1)
constexpr int delta
GR_TEXT_H_ALIGN_T
@ 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
VECTOR2I GetRotated(const VECTOR2I &aVector, const EDA_ANGLE &aAngle)
Return a new VECTOR2I that is the result of rotating aVector by aAngle.
Definition: trigo.h:77
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:229
@ 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
constexpr int sign(T val)
Definition: util.h:159
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691
VECTOR2< double > VECTOR2D
Definition: vector2d.h:690
Supplemental functions for working with vectors and simple objects that interact with vectors.