KiCad PCB EDA Suite
Loading...
Searching...
No Matches
ee_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) 2019 CERN
5 * Copyright (C) 2019-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <functional>
26using namespace std::placeholders;
27
28#include "ee_point_editor.h"
29#include <ee_grid_helper.h>
30#include <tool/tool_manager.h>
31#include <sch_commit.h>
32#include <view/view_controls.h>
34#include <geometry/seg.h>
36#include <tools/ee_actions.h>
38#include <sch_edit_frame.h>
39#include <sch_line.h>
40#include <sch_bitmap.h>
41#include <sch_sheet.h>
42#include <sch_textbox.h>
43#include <sch_table.h>
45
46
47static const std::vector<KICAD_T> pointEditorTypes = { SCH_SHAPE_T,
54
55
56// Few constants to avoid using bare numbers for point indices
58{
60};
61
62
64{
66};
67
68
70{
72};
73
74
76{
78};
79
80
82{
84};
85
86
88{
90};
91
93{
95};
96
97
99{
105
106
108{
109public:
110 static std::shared_ptr<EDIT_POINTS> Make( EDA_ITEM* aItem, SCH_BASE_FRAME* frame )
111 {
112 std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
113
114 if( !aItem )
115 return points;
116
117 // Generate list of edit points based on the item type
118 switch( aItem->Type() )
119 {
120 case SCH_SHAPE_T:
121 {
122 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( aItem );
123
124 switch( shape->GetShape() )
125 {
126 case SHAPE_T::ARC:
127 points->AddPoint( shape->GetStart() );
128 points->AddPoint( shape->GetEnd() );
129 points->AddPoint( shape->GetPosition() );
130
131 points->AddIndicatorLine( points->Point( ARC_CENTER ), points->Point( ARC_START ) );
132 points->AddIndicatorLine( points->Point( ARC_CENTER ), points->Point( ARC_END ) );
133 break;
134
135 case SHAPE_T::CIRCLE:
136 points->AddPoint( shape->GetPosition() );
137 points->AddPoint( shape->GetEnd() );
138 break;
139
140 case SHAPE_T::RECTANGLE:
141 {
142 shape->Normalize();
143
144 VECTOR2I topLeft = shape->GetPosition();
145 VECTOR2I botRight = shape->GetEnd();
146
147 points->AddPoint( topLeft );
148 points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
149 points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
150 points->AddPoint( botRight );
151
152 points->AddLine( points->Point( RECT_TOPLEFT ), points->Point( RECT_TOPRIGHT ) );
153 points->Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( points->Line( RECT_TOP ) ) );
154 points->AddLine( points->Point( RECT_TOPRIGHT ), points->Point( RECT_BOTRIGHT ) );
155 points->Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_RIGHT ) ) );
156 points->AddLine( points->Point( RECT_BOTRIGHT ), points->Point( RECT_BOTLEFT ) );
157 points->Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_BOT ) ) );
158 points->AddLine( points->Point( RECT_BOTLEFT ), points->Point( RECT_TOPLEFT ) );
159 points->Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_LEFT ) ) );
160
161 break;
162 }
163
164 case SHAPE_T::POLY:
165 for( const VECTOR2I& pt : shape->GetPolyShape().Outline( 0 ).CPoints() )
166 points->AddPoint( pt );
167
168 break;
169
170 case SHAPE_T::BEZIER:
171 points->AddPoint( shape->GetStart() );
172 points->AddPoint( shape->GetBezierC1() );
173 points->AddPoint( shape->GetBezierC2() );
174 points->AddPoint( shape->GetEnd() );
175
176 points->AddIndicatorLine( points->Point( BEZIER_START ),
177 points->Point( BEZIER_CTRL_PT1 ) );
178 points->AddIndicatorLine( points->Point( BEZIER_END ),
179 points->Point( BEZIER_CTRL_PT2 ) );
180 break;
181
182 default:
184 }
185
186 break;
187 }
188
189 case SCH_RULE_AREA_T:
190 {
191 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( aItem );
192
193 for( const VECTOR2I& pt : shape->GetPolyShape().Outline( 0 ).CPoints() )
194 points->AddPoint( pt );
195
196 break;
197 }
198
199 case SCH_TEXTBOX_T:
200 {
201 SCH_TEXTBOX* textbox = static_cast<SCH_TEXTBOX*>( aItem );
202
203 textbox->Normalize();
204
205 VECTOR2I topLeft = textbox->GetPosition();
206 VECTOR2I botRight = textbox->GetEnd();
207
208 points->AddPoint( topLeft );
209 points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
210 points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
211 points->AddPoint( botRight );
212
213 points->AddLine( points->Point( RECT_TOPLEFT ), points->Point( RECT_TOPRIGHT ) );
214 points->Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( points->Line( RECT_TOP ) ) );
215 points->AddLine( points->Point( RECT_TOPRIGHT ), points->Point( RECT_BOTRIGHT ) );
216 points->Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_RIGHT ) ) );
217 points->AddLine( points->Point( RECT_BOTRIGHT ), points->Point( RECT_BOTLEFT ) );
218 points->Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_BOT ) ) );
219 points->AddLine( points->Point( RECT_BOTLEFT ), points->Point( RECT_TOPLEFT ) );
220 points->Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_LEFT ) ) );
221
222 break;
223 }
224
225 case SCH_TABLECELL_T:
226 {
227 SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( aItem );
228 points->AddPoint( cell->GetEnd() - VECTOR2I( 0, cell->GetRectangleHeight() / 2 ) );
229 points->AddPoint( cell->GetEnd() - VECTOR2I( cell->GetRectangleWidth() / 2, 0 ) );
230 break;
231 }
232
233 case SCH_SHEET_T:
234 {
235 SCH_SHEET* sheet = (SCH_SHEET*) aItem;
236 VECTOR2I topLeft = sheet->GetPosition();
237 VECTOR2I botRight = sheet->GetPosition() + sheet->GetSize();
238
239 points->AddPoint( topLeft );
240 points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
241 points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
242 points->AddPoint( botRight );
243
244 points->AddLine( points->Point( RECT_TOPLEFT ), points->Point( RECT_TOPRIGHT ) );
245 points->Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( points->Line( RECT_TOP ) ) );
246 points->AddLine( points->Point( RECT_TOPRIGHT ), points->Point( RECT_BOTRIGHT ) );
247 points->Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_RIGHT ) ) );
248 points->AddLine( points->Point( RECT_BOTRIGHT ), points->Point( RECT_BOTLEFT ) );
249 points->Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_BOT ) ) );
250 points->AddLine( points->Point( RECT_BOTLEFT ), points->Point( RECT_TOPLEFT ) );
251 points->Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( points->Line( RECT_LEFT ) ) );
252
253 break;
254 }
255
256 case SCH_BITMAP_T:
257 {
258 const SCH_BITMAP& bitmap = static_cast<const SCH_BITMAP&>( *aItem );
259 const REFERENCE_IMAGE& refImage = bitmap.GetReferenceImage();
260 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
261 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
262
263 points->AddPoint( topLeft );
264 points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
265 points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
266 points->AddPoint( botRight );
267
268 points->AddPoint( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
269 break;
270 }
271
272 case SCH_LINE_T:
273 {
274 SCH_LINE* line = (SCH_LINE*) aItem;
275 std::pair<EDA_ITEM*, int> connectedStart = { nullptr, STARTPOINT };
276 std::pair<EDA_ITEM*, int> connectedEnd = { nullptr, STARTPOINT };
277
278 for( SCH_ITEM* test : frame->GetScreen()->Items().OfType( SCH_LINE_T ) )
279 {
280 if( test->GetLayer() != LAYER_NOTES )
281 continue;
282
283 if( test == aItem )
284 continue;
285
286 SCH_LINE* testLine = static_cast<SCH_LINE*>( test );
287
288 if( testLine->GetStartPoint() == line->GetStartPoint() )
289 {
290 connectedStart = { testLine, STARTPOINT };
291 }
292 else if( testLine->GetEndPoint() == line->GetStartPoint() )
293 {
294 connectedStart = { testLine, ENDPOINT };
295 }
296 else if( testLine->GetStartPoint() == line->GetEndPoint() )
297 {
298 connectedEnd = { testLine, STARTPOINT };
299 }
300 else if( testLine->GetEndPoint() == line->GetEndPoint() )
301 {
302 connectedEnd = { testLine, ENDPOINT };
303 }
304 }
305
306 points->AddPoint( line->GetStartPoint(), connectedStart );
307 points->AddPoint( line->GetEndPoint(), connectedEnd );
308 break;
309 }
310
311 default:
312 points.reset();
313 break;
314 }
315
316 return points;
317 }
318
319private:
321};
322
323
325 EE_TOOL_BASE<SCH_BASE_FRAME>( "eeschema.PointEditor" ),
326 m_editedPoint( nullptr ),
327 m_inPointEditor( false )
328{
329}
330
331
333{
334 EE_TOOL_BASE::Reset( aReason );
335
336 m_editPoints.reset();
337 m_editedPoint = nullptr;
338}
339
340
342{
344
345 auto& menu = m_selectionTool->GetToolMenu().GetMenu();
347 std::bind( &EE_POINT_EDITOR::addCornerCondition, this, _1 ) );
349 std::bind( &EE_POINT_EDITOR::removeCornerCondition, this, _1 ) );
350
351 return true;
352}
353
354
356{
357 setEditedPoint( nullptr );
358
359 return 0;
360}
361
362
364{
365 EDIT_POINT* point = m_editedPoint;
366
367 if( !m_editPoints )
368 {
369 point = nullptr;
370 }
371 else if( aEvent.IsMotion() )
372 {
373 point = m_editPoints->FindPoint( aEvent.Position(), getView() );
374 }
375 else if( aEvent.IsDrag( BUT_LEFT ) )
376 {
377 point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
378 }
379 else
380 {
381 point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition( false ), getView() );
382 }
383
384 if( m_editedPoint != point )
385 setEditedPoint( point );
386}
387
388
390{
391 if( !m_selectionTool )
392 return 0;
393
394 if( m_inPointEditor )
395 return 0;
396
398
399 if( m_isSymbolEditor )
400 {
401 SYMBOL_EDIT_FRAME* editor = getEditFrame<SYMBOL_EDIT_FRAME>();
402
403 if( !editor->IsSymbolEditable() || editor->IsSymbolAlias() )
404 return 0;
405 }
406
407 const EE_SELECTION& selection = m_selectionTool->GetSelection();
408
409 if( selection.Size() != 1 || !selection.Front()->IsType( pointEditorTypes ) )
410 {
411 return 0;
412 }
413
414 // Wait till drawing tool is done
415 if( selection.Front()->IsNew() )
416 return 0;
417
418 Activate();
419
422 VECTOR2I cursorPos;
423 KIGFX::VIEW* view = getView();
424 EDA_ITEM* item = selection.Front();
425 SCH_COMMIT commit( m_toolMgr );
426
427 controls->ShowCursor( true );
428
430 view->Add( m_editPoints.get() );
431 setEditedPoint( nullptr );
432 updateEditedPoint( aEvent );
433 bool inDrag = false;
434
435 // Main loop: keep receiving events
436 while( TOOL_EVENT* evt = Wait() )
437 {
438 if( grid )
439 {
440 grid->SetSnap( !evt->Modifier( MD_SHIFT ) );
441 grid->SetUseGrid( getView()->GetGAL()->GetGridSnapping()
442 && !evt->DisableGridSnapping() );
443 }
444 else
445 {
446 // This check is based on the assumption that the grid object must be valid.
447 // If this assumption is wrong, please fix the code above.
448 wxCHECK( false, 0 );
449 }
450
451 if( !m_editPoints || evt->IsSelectionEvent() )
452 break;
453
454 if ( !inDrag )
455 updateEditedPoint( *evt );
456
457 if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
458 {
459 if( !inDrag )
460 {
461 commit.Modify( m_editPoints->GetParent(), m_frame->GetScreen() );
462
463 if( m_editPoints->GetParent()->Type() == SCH_LINE_T )
464 {
465 std::pair<EDA_ITEM*, int> connected = m_editPoints->Point( LINE_START ).GetConnected();
466
467 if( connected.first )
468 commit.Modify( connected.first, m_frame->GetScreen() );
469
470 connected = m_editPoints->Point( LINE_END ).GetConnected();
471
472 if( connected.first )
473 commit.Modify( connected.first, m_frame->GetScreen() );
474 }
475 else if( m_editPoints->GetParent()->Type() == SCH_TABLECELL_T )
476 {
477 SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( m_editPoints->GetParent() );
478 SCH_TABLE* table = static_cast<SCH_TABLE*>( cell->GetParent() );
479
480 commit.Modify( table, m_frame->GetScreen() );
481 }
482
483 inDrag = true;
484 }
485
486 bool snap = !evt->DisableGridSnapping();
487
488 cursorPos =
489 grid->Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_GRAPHICS );
490 controls->ForceCursorPosition( true, cursorPos );
491
492 m_editedPoint->SetPosition( controls->GetCursorPosition( snap ) );
493
494 updateParentItem( snap, commit );
495 updatePoints();
496 }
497 else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
498 {
499 if( !commit.Empty() )
500 commit.Push( _( "Move Point" ) );
501
502 controls->SetAutoPan( false );
503 inDrag = false;
504 }
505 else if( evt->IsCancelInteractive() || evt->IsActivate() )
506 {
507 if( inDrag ) // Restore the last change
508 {
509 // Currently we are manually managing the lifetime of the grid
510 // helpers because there is a bug in the tool stack that adds
511 // the point editor again when commit.Revert() rebuilds the selection.
512 // We remove this grid here so the its destructor is called before it
513 // is added again.
514 if( grid )
515 {
516 delete grid;
517 grid = nullptr;
518 }
519
520 commit.Revert();
521 inDrag = false;
522 break;
523 }
524 else if( evt->IsCancelInteractive() )
525 {
526 break;
527 }
528
529 if( evt->IsActivate() )
530 break;
531 }
532 else
533 {
534 evt->SetPassEvent();
535 }
536
537 controls->SetAutoPan( inDrag );
538 controls->CaptureCursor( inDrag );
539 }
540
541 controls->SetAutoPan( false );
542 controls->CaptureCursor( false );
543 setEditedPoint( nullptr );
544
545 if( m_editPoints )
546 {
547 view->Remove( m_editPoints.get() );
548
549 m_editPoints.reset();
551 }
552
553 delete grid;
554
555 return 0;
556}
557
568void EE_POINT_EDITOR::pinEditedCorner( int minWidth, int minHeight, VECTOR2I& topLeft,
569 VECTOR2I& topRight, VECTOR2I& botLeft, VECTOR2I& botRight,
570 EE_GRID_HELPER* aGrid ) const
571{
572 if( isModified( m_editPoints->Point( RECT_TOPLEFT ) ) )
573 {
574 // pin edited point within opposite corner
575 topLeft.x = std::min( topLeft.x, botRight.x - minWidth );
576 topLeft.y = std::min( topLeft.y, botRight.y - minHeight );
577
578 if( aGrid->GetSnap() )
579 topLeft = aGrid->AlignGrid( topLeft, GRID_HELPER_GRIDS::GRID_GRAPHICS );
580
581 // push edited point edges to adjacent corners
582 topRight.y = topLeft.y;
583 botLeft.x = topLeft.x;
584 }
585 else if( isModified( m_editPoints->Point( RECT_TOPRIGHT ) ) )
586 {
587 // pin edited point within opposite corner
588 topRight.x = std::max( topRight.x, botLeft.x + minWidth );
589 topRight.y = std::min( topRight.y, botLeft.y - minHeight );
590
591 if( aGrid->GetSnap() )
592 topRight = aGrid->AlignGrid( topRight, GRID_HELPER_GRIDS::GRID_GRAPHICS );
593
594 // push edited point edges to adjacent corners
595 topLeft.y = topRight.y;
596 botRight.x = topRight.x;
597 }
598 else if( isModified( m_editPoints->Point( RECT_BOTLEFT ) ) )
599 {
600 // pin edited point within opposite corner
601 botLeft.x = std::min( botLeft.x, topRight.x - minWidth );
602 botLeft.y = std::max( botLeft.y, topRight.y + minHeight );
603
604 if( aGrid->GetSnap() )
605 botLeft = aGrid->AlignGrid( botLeft, GRID_HELPER_GRIDS::GRID_GRAPHICS );
606
607 // push edited point edges to adjacent corners
608 botRight.y = botLeft.y;
609 topLeft.x = botLeft.x;
610 }
611 else if( isModified( m_editPoints->Point( RECT_BOTRIGHT ) ) )
612 {
613 // pin edited point within opposite corner
614 botRight.x = std::max( botRight.x, topLeft.x + minWidth );
615 botRight.y = std::max( botRight.y, topLeft.y + minHeight );
616
617 if( aGrid->GetSnap() )
618 botRight = aGrid->AlignGrid( botRight, GRID_HELPER_GRIDS::GRID_GRAPHICS );
619
620 // push edited point edges to adjacent corners
621 botLeft.y = botRight.y;
622 topRight.x = botRight.x;
623 }
624 else if( isModified( m_editPoints->Line( RECT_TOP ) ) )
625 {
626 topLeft.y = std::min( topLeft.y, botRight.y - minHeight );
627
628 if( aGrid->GetSnap() )
629 topLeft = aGrid->AlignGrid( topLeft, GRID_HELPER_GRIDS::GRID_GRAPHICS );
630 }
631 else if( isModified( m_editPoints->Line( RECT_LEFT ) ) )
632 {
633 topLeft.x = std::min( topLeft.x, botRight.x - minWidth );
634
635 if( aGrid->GetSnap() )
636 topLeft = aGrid->AlignGrid( topLeft, GRID_HELPER_GRIDS::GRID_GRAPHICS );
637 }
638 else if( isModified( m_editPoints->Line( RECT_BOT ) ) )
639 {
640 botRight.y = std::max( botRight.y, topLeft.y + minHeight );
641
642 if( aGrid->GetSnap() )
643 botRight = aGrid->AlignGrid( botRight, GRID_HELPER_GRIDS::GRID_GRAPHICS );
644 }
645 else if( isModified( m_editPoints->Line( RECT_RIGHT ) ) )
646 {
647 botRight.x = std::max( botRight.x, topLeft.x + minWidth );
648
649 if( aGrid->GetSnap() )
650 botRight = aGrid->AlignGrid( botRight, GRID_HELPER_GRIDS::GRID_GRAPHICS );
651 }
652}
653
654
655void EE_POINT_EDITOR::dragPinsOnEdge( const std::vector<SEG>& aOldEdges,
656 const std::vector<VECTOR2I>& aMoveVecs, int aEdgeUnit,
657 SCH_COMMIT& aCommit ) const
658{
659 wxCHECK( aOldEdges.size() == aMoveVecs.size(), /* void */ );
660
661 // This only make sense in the symbol editor
663 return;
664
665 SYMBOL_EDIT_FRAME& editor = static_cast<SYMBOL_EDIT_FRAME&>( *m_frame );
666
667 // And only if the setting is enabled
668 if( !editor.GetSettings()->m_dragPinsAlongWithEdges )
669 return;
670
671 // Adjuting pins on a different unit to a unit-limited shape
672 // seems suspect.
673 wxCHECK( aEdgeUnit == 0 || aEdgeUnit == editor.GetUnit(), /* void */ );
674
675 /*
676 * Get a list of pins on a line segment
677 */
678 const auto getPinsOnSeg = []( LIB_SYMBOL& aSymbol, int aUnit, const SEG& aSeg,
679 bool aIncludeEnds ) -> std::vector<SCH_PIN*>
680 {
681 // const BOX2I segBox = BOX2I::ByCorners( aSeg.A, aSeg.B ).GetInflated( 1 );
682 // const EE_RTREE& rtree = m_frame->GetScreen()->Items().Overlapping( SCH_PIN_T, segBox );
683
684 std::vector<SCH_PIN*> pins;
685
686 for( SCH_PIN* pin : aSymbol.GetPins( aUnit ) )
687 {
688 // Figure out if the pin "connects" to the line
689 const VECTOR2I pinRootPos = pin->GetPinRoot();
690
691 if( aSeg.Contains( pinRootPos ) )
692 {
693 if( aIncludeEnds || ( pinRootPos != aSeg.A && pinRootPos != aSeg.B ) )
694 {
695 pins.push_back( pin );
696 }
697 }
698 }
699
700 return pins;
701 };
702
703 LIB_SYMBOL* const symbol = editor.GetCurSymbol();
704
705 for( std::size_t i = 0; i < aOldEdges.size(); ++i )
706 {
707 if( aMoveVecs[i] == VECTOR2I( 0, 0 ) || !symbol )
708 continue;
709
710 const std::vector<SCH_PIN*> pins = getPinsOnSeg( *symbol, aEdgeUnit, aOldEdges[i], false );
711
712 for( SCH_PIN* pin : pins )
713 {
714 // Move the pin
715 aCommit.Modify( pin, m_frame->GetScreen() );
716 pin->Move( aMoveVecs[i] );
717
718 updateItem( pin, true );
719 }
720 }
721}
722
723
724void EE_POINT_EDITOR::updateParentItem( bool aSnapToGrid, SCH_COMMIT& aCommit ) const
725{
726 EDA_ITEM* item = m_editPoints->GetParent();
727
728 if( !item )
729 return;
730
731 switch( item->Type() )
732 {
733 case SCH_RULE_AREA_T:
734 case SCH_SHAPE_T:
735 {
736 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( item );
737
738 switch( shape->GetShape() )
739 {
740 case SHAPE_T::ARC:
742 {
743 shape->SetEditState( 2 );
744 shape->CalcEdit( m_editPoints->Point( ARC_START ).GetPosition() );
745 }
746 else if( getEditedPointIndex() == ARC_END )
747 {
748 shape->SetEditState( 3 );
749 shape->CalcEdit( m_editPoints->Point( ARC_END ).GetPosition() );
750 }
751 else if( getEditedPointIndex() == ARC_CENTER )
752 {
753 shape->SetEditState( 4 );
754 shape->CalcEdit( m_editPoints->Point( ARC_CENTER ).GetPosition() );
755 }
756 break;
757
758 case SHAPE_T::CIRCLE:
759 shape->SetPosition( m_editPoints->Point( CIRC_CENTER ).GetPosition() );
760 shape->SetEnd( m_editPoints->Point( CIRC_END ).GetPosition() );
761 break;
762
763 case SHAPE_T::POLY:
765 shape->GetPolyShape().NewOutline();
766
767 for( unsigned i = 0; i < m_editPoints->PointsSize(); ++i )
768 {
769 VECTOR2I pt = m_editPoints->Point( i ).GetPosition();
770 shape->GetPolyShape().Append( pt.x, pt.y, -1, -1, true );
771 }
772
773 break;
774
775 case SHAPE_T::RECTANGLE:
776 {
777 EE_GRID_HELPER gridHelper( m_toolMgr );
778 VECTOR2I topLeft = m_editPoints->Point( RECT_TOPLEFT ).GetPosition();
779 VECTOR2I topRight = m_editPoints->Point( RECT_TOPRIGHT ).GetPosition();
780 VECTOR2I botLeft = m_editPoints->Point( RECT_BOTLEFT ).GetPosition();
781 VECTOR2I botRight = m_editPoints->Point( RECT_BOTRIGHT ).GetPosition();
782
783 gridHelper.SetSnap( aSnapToGrid );
784
785 pinEditedCorner( schIUScale.MilsToIU( 1 ), schIUScale.MilsToIU( 1 ), topLeft, topRight,
786 botLeft, botRight, &gridHelper );
787
788 const BOX2I oldBox = BOX2I::ByCorners( shape->GetStart(), shape->GetEnd() );
789 std::vector<SEG> oldSegs;
790 std::vector<VECTOR2I> moveVecs;
791
792 if( isModified( m_editPoints->Point( RECT_TOPLEFT ) )
795 || isModified( m_editPoints->Point( RECT_BOTLEFT ) ) )
796 {
797 // Corner drags don't update pins. Not only is it an escape hatch to avoid
798 // moving pins, it also avoids tricky problems when the pins "fall off"
799 // the ends of one of the two segments and get either left behind or
800 // "swept up" into the corner.
801 shape->SetPosition( topLeft );
802 shape->SetEnd( botRight );
803 }
804 else if( isModified( m_editPoints->Line( RECT_TOP ) ) )
805 {
807 moveVecs.emplace_back( 0, topLeft.y - oldBox.GetTop() );
808 shape->SetStartY( topLeft.y );
809 }
810 else if( isModified( m_editPoints->Line( RECT_LEFT ) ) )
811 {
813 moveVecs.emplace_back( topLeft.x - oldBox.GetLeft(), 0 );
814 shape->SetStartX( topLeft.x );
815 }
816 else if( isModified( m_editPoints->Line( RECT_BOT ) ) )
817 {
819 moveVecs.emplace_back( 0, botRight.y - oldBox.GetBottom() );
820 shape->SetEndY( botRight.y );
821 }
822 else if( isModified( m_editPoints->Line( RECT_RIGHT ) ) )
823 {
825 moveVecs.emplace_back( botRight.x - oldBox.GetRight(), 0 );
826 shape->SetEndX( botRight.x );
827 }
828
829 dragPinsOnEdge( oldSegs, moveVecs, shape->GetUnit(), aCommit );
830
831 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
832 {
833 if( !isModified( m_editPoints->Line( i ) ) )
834 {
835 m_editPoints->Line( i ).SetConstraint(
836 new EC_PERPLINE( m_editPoints->Line( i ) ) );
837 }
838 }
839
840 break;
841 }
842
843 case SHAPE_T::BEZIER:
844 shape->SetStart( m_editPoints->Point( BEZIER_START ).GetPosition() );
845 shape->SetBezierC1( m_editPoints->Point( BEZIER_CTRL_PT1 ).GetPosition() );
846 shape->SetBezierC2( m_editPoints->Point( BEZIER_CTRL_PT2 ).GetPosition() );
847 shape->SetEnd( m_editPoints->Point( BEZIER_END ).GetPosition() );
848
849 shape->RebuildBezierToSegmentsPointsList( shape->GetWidth() / 2 );
850 break;
851
852 default:
854 }
855
856 break;
857 }
858
859 case SCH_TEXTBOX_T:
860 {
861 SCH_TEXTBOX* textbox = static_cast<SCH_TEXTBOX*>( item );
862 EE_GRID_HELPER gridHelper( m_toolMgr );
863 VECTOR2I topLeft = m_editPoints->Point( RECT_TOPLEFT ).GetPosition();
864 VECTOR2I topRight = m_editPoints->Point( RECT_TOPRIGHT ).GetPosition();
865 VECTOR2I botLeft = m_editPoints->Point( RECT_BOTLEFT ).GetPosition();
866 VECTOR2I botRight = m_editPoints->Point( RECT_BOTRIGHT ).GetPosition();
867
868 gridHelper.SetSnap( aSnapToGrid );
869
870 pinEditedCorner( schIUScale.MilsToIU( 1 ), schIUScale.MilsToIU( 1 ), topLeft, topRight,
871 botLeft, botRight, &gridHelper );
872
873 if( isModified( m_editPoints->Point( RECT_TOPLEFT ) )
876 || isModified( m_editPoints->Point( RECT_BOTLEFT ) ) )
877 {
878 textbox->SetPosition( topLeft );
879 textbox->SetEnd( botRight );
880 }
881 else if( isModified( m_editPoints->Line( RECT_TOP ) ) )
882 {
883 textbox->SetStartY( topLeft.y );
884 }
885 else if( isModified( m_editPoints->Line( RECT_LEFT ) ) )
886 {
887 textbox->SetStartX( topLeft.x );
888 }
889 else if( isModified( m_editPoints->Line( RECT_BOT ) ) )
890 {
891 textbox->SetEndY( botRight.y );
892 }
893 else if( isModified( m_editPoints->Line( RECT_RIGHT ) ) )
894 {
895 textbox->SetEndX( botRight.x );
896 }
897
898 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
899 {
900 if( !isModified( m_editPoints->Line( i ) ) )
901 {
902 m_editPoints->Line( i ).SetConstraint(
903 new EC_PERPLINE( m_editPoints->Line( i ) ) );
904 }
905 }
906
907 textbox->ClearRenderCache();
908 break;
909 }
910
911 case SCH_TABLECELL_T:
912 {
913 SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( item );
914 SCH_TABLE* table = static_cast<SCH_TABLE*>( cell->GetParent() );
915
916 if( isModified( m_editPoints->Point( COL_WIDTH ) ) )
917 {
918 cell->SetEnd( VECTOR2I( m_editPoints->Point( 0 ).GetX(), cell->GetEndY() ) );
919
920 int colWidth = cell->GetRectangleWidth();
921
922 for( int ii = 0; ii < cell->GetColSpan() - 1; ++ii )
923 colWidth -= table->GetColWidth( cell->GetColumn() + ii );
924
925 table->SetColWidth( cell->GetColumn() + cell->GetColSpan() - 1, colWidth );
926 table->Normalize();
927 }
928 else if( isModified( m_editPoints->Point( ROW_HEIGHT ) ) )
929 {
930 cell->SetEnd( VECTOR2I( cell->GetEndX(), m_editPoints->Point( 1 ).GetY() ) );
931
932 int rowHeight = cell->GetRectangleHeight();
933
934 for( int ii = 0; ii < cell->GetRowSpan() - 1; ++ii )
935 rowHeight -= table->GetRowHeight( cell->GetRow() + ii );
936
937 table->SetRowHeight( cell->GetRow() + cell->GetRowSpan() - 1, rowHeight );
938 table->Normalize();
939 }
940
941 break;
942 }
943
944 case SCH_BITMAP_T:
945 {
946 SCH_BITMAP& bitmap = static_cast<SCH_BITMAP&>( *item );
947 REFERENCE_IMAGE& refImg = bitmap.GetReferenceImage();
948 const VECTOR2I topLeft = m_editPoints->Point( RECT_TOPLEFT ).GetPosition();
949 const VECTOR2I topRight = m_editPoints->Point( RECT_TOPRIGHT ).GetPosition();
950 const VECTOR2I botLeft = m_editPoints->Point( RECT_BOTLEFT ).GetPosition();
951 const VECTOR2I botRight = m_editPoints->Point( RECT_BOTRIGHT ).GetPosition();
952 const VECTOR2I xfrmOrigin = m_editPoints->Point( REFIMG_ORIGIN ).GetPosition();
953
954 if( isModified( m_editPoints->Point( REFIMG_ORIGIN ) ) )
955 {
956 // Moving the transform origin
957 // As the other points didn't move, we can get the image extent from them
958 const VECTOR2I newOffset = xfrmOrigin - ( topLeft + botRight ) / 2;
959 refImg.SetTransformOriginOffset( newOffset );
960 }
961 else
962 {
963 const VECTOR2I oldOrigin = refImg.GetPosition() + refImg.GetTransformOriginOffset();
964 const VECTOR2I oldSize = refImg.GetSize();
965 const VECTOR2I pos = refImg.GetPosition();
966
967 OPT_VECTOR2I newCorner;
968 VECTOR2I oldCorner = pos;
969
970 if( isModified( m_editPoints->Point( RECT_TOPLEFT ) ) )
971 {
972 newCorner = topLeft;
973 oldCorner -= oldSize / 2;
974 }
975 else if( isModified( m_editPoints->Point( RECT_TOPRIGHT ) ) )
976 {
977 newCorner = topRight;
978 oldCorner -= VECTOR2I( -oldSize.x, oldSize.y ) / 2;
979 }
980 else if( isModified( m_editPoints->Point( RECT_BOTLEFT ) ) )
981 {
982 newCorner = botLeft;
983 oldCorner -= VECTOR2I( oldSize.x, -oldSize.y ) / 2;
984 }
985 else if( isModified( m_editPoints->Point( RECT_BOTRIGHT ) ) )
986 {
987 newCorner = botRight;
988 oldCorner += oldSize / 2;
989 }
990
991 if( newCorner )
992 {
993 // Turn in the respective vectors from the origin
994 *newCorner -= xfrmOrigin;
995 oldCorner -= oldOrigin;
996
997 // If we tried to cross the origin, clamp it to stop it
998 if( sign( newCorner->x ) != sign( oldCorner.x )
999 || sign( newCorner->y ) != sign( oldCorner.y ) )
1000 {
1001 *newCorner = VECTOR2I( 0, 0 );
1002 }
1003
1004 const double newLength = newCorner->EuclideanNorm();
1005 const double oldLength = oldCorner.EuclideanNorm();
1006
1007 double ratio = oldLength > 0 ? ( newLength / oldLength ) : 1.0;
1008
1009 // Clamp the scaling to a minimum of 50 mils
1010 VECTOR2I newSize = oldSize * ratio;
1011 double newWidth = std::max( newSize.x, EDA_UNIT_UTILS::Mils2IU( schIUScale, 50 ) );
1012 double newHeight = std::max( newSize.y, EDA_UNIT_UTILS::Mils2IU( schIUScale, 50 ) );
1013 ratio = std::min( newWidth / oldSize.x, newHeight / oldSize.y );
1014
1015 // Also handles the origin offset
1016 refImg.SetImageScale( refImg.GetImageScale() * ratio );
1017 }
1018 }
1019 break;
1020 }
1021 case SCH_SHEET_T:
1022 {
1023 SCH_SHEET* sheet = (SCH_SHEET*) item;
1024 EE_GRID_HELPER gridHelper( m_toolMgr );
1025 VECTOR2I topLeft = m_editPoints->Point( RECT_TOPLEFT ).GetPosition();
1026 VECTOR2I topRight = m_editPoints->Point( RECT_TOPRIGHT ).GetPosition();
1027 VECTOR2I botLeft = m_editPoints->Point( RECT_BOTLEFT ).GetPosition();
1028 VECTOR2I botRight = m_editPoints->Point( RECT_BOTRIGHT ).GetPosition();
1029 VECTOR2I sheetNewPos = sheet->GetPosition();
1030 VECTOR2I sheetNewSize = sheet->GetSize();
1031
1032 gridHelper.SetSnap( aSnapToGrid );
1033
1034 int edited = getEditedPointIndex();
1035
1036 if( isModified( m_editPoints->Line( RECT_RIGHT ) ) )
1037 edited = RECT_TOPRIGHT;
1038 else if( isModified( m_editPoints->Line( RECT_BOT ) ) )
1039 edited = RECT_BOTLEFT;
1040
1041 gridHelper.SetSnap( aSnapToGrid );
1042
1043 pinEditedCorner( sheet->GetMinWidth( edited == RECT_TOPRIGHT || edited == RECT_BOTRIGHT ),
1044 sheet->GetMinHeight( edited == RECT_BOTLEFT || edited == RECT_BOTRIGHT ),
1045 topLeft, topRight, botLeft, botRight, &gridHelper );
1046
1047 if( isModified( m_editPoints->Point( RECT_TOPLEFT ) )
1048 || isModified( m_editPoints->Point( RECT_TOPRIGHT ) )
1049 || isModified( m_editPoints->Point( RECT_BOTRIGHT ) )
1050 || isModified( m_editPoints->Point( RECT_BOTLEFT ) ) )
1051 {
1052 sheetNewPos = topLeft;
1053 sheetNewSize = VECTOR2I( botRight.x - topLeft.x, botRight.y - topLeft.y );
1054 }
1055 else if( isModified( m_editPoints->Line( RECT_TOP ) ) )
1056 {
1057 sheetNewPos = VECTOR2I( sheet->GetPosition().x, topLeft.y );
1058 sheetNewSize = VECTOR2I( sheet->GetSize().x, botRight.y - topLeft.y );
1059 }
1060 else if( isModified( m_editPoints->Line( RECT_LEFT ) ) )
1061 {
1062 sheetNewPos = VECTOR2I( topLeft.x, sheet->GetPosition().y );
1063 sheetNewSize = VECTOR2I( botRight.x - topLeft.x, sheet->GetSize().y );
1064 }
1065 else if( isModified( m_editPoints->Line( RECT_BOT ) ) )
1066 {
1067 sheetNewSize = VECTOR2I( sheet->GetSize().x, botRight.y - topLeft.y );
1068 }
1069 else if( isModified( m_editPoints->Line( RECT_RIGHT ) ) )
1070 {
1071 sheetNewSize = VECTOR2I( botRight.x - topLeft.x, sheet->GetSize().y );
1072 }
1073
1074 for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
1075 {
1076 if( !isModified( m_editPoints->Line( i ) ) )
1077 {
1078 m_editPoints->Line( i ).SetConstraint(
1079 new EC_PERPLINE( m_editPoints->Line( i ) ) );
1080 }
1081 }
1082
1083 if( sheet->GetPosition() != sheetNewPos )
1084 sheet->SetPositionIgnoringPins( sheetNewPos );
1085
1086 if( sheet->GetSize() != sheetNewSize )
1087 sheet->Resize( sheetNewSize );
1088
1089 break;
1090 }
1091
1092 case SCH_LINE_T:
1093 {
1094 SCH_LINE* line = (SCH_LINE*) item;
1095
1096 line->SetStartPoint( m_editPoints->Point( LINE_START ).GetPosition() );
1097 line->SetEndPoint( m_editPoints->Point( LINE_END ).GetPosition() );
1098
1099 std::pair<EDA_ITEM*, int> connected = m_editPoints->Point( LINE_START ).GetConnected();
1100
1101 if( connected.first )
1102 {
1103 if( connected.second == STARTPOINT )
1104 static_cast<SCH_LINE*>( connected.first )->SetStartPoint( line->GetStartPoint() );
1105 else if( connected.second == ENDPOINT )
1106 static_cast<SCH_LINE*>( connected.first )->SetEndPoint( line->GetStartPoint() );
1107
1108 updateItem( connected.first, true );
1109 }
1110
1111 connected = m_editPoints->Point( LINE_END ).GetConnected();
1112
1113 if( connected.first )
1114 {
1115 if( connected.second == STARTPOINT )
1116 static_cast<SCH_LINE*>( connected.first )->SetStartPoint( line->GetEndPoint() );
1117 else if( connected.second == ENDPOINT )
1118 static_cast<SCH_LINE*>( connected.first )->SetEndPoint( line->GetEndPoint() );
1119
1120 updateItem( connected.first, true );
1121 }
1122
1123 break;
1124 }
1125
1126 default:
1127 break;
1128 }
1129
1130 updateItem( item, true );
1131 m_frame->SetMsgPanel( item );
1132}
1133
1134
1136{
1137 if( !m_editPoints )
1138 return;
1139
1140 EDA_ITEM* item = m_editPoints->GetParent();
1141
1142 if( !item )
1143 return;
1144
1145 switch( item->Type() )
1146 {
1147 case SCH_RULE_AREA_T:
1148 case SCH_SHAPE_T:
1149 {
1150 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( item );
1151
1152 switch( shape->GetShape() )
1153 {
1154 case SHAPE_T::ARC:
1155 m_editPoints->Point( ARC_START ).SetPosition( shape->GetStart() );
1156 m_editPoints->Point( ARC_END ).SetPosition( shape->GetEnd() );
1157 m_editPoints->Point( ARC_CENTER ).SetPosition( shape->GetPosition() );
1158 break;
1159
1160 case SHAPE_T::CIRCLE:
1161 m_editPoints->Point( CIRC_CENTER ).SetPosition( shape->GetPosition() );
1162 m_editPoints->Point( CIRC_END ).SetPosition( shape->GetEnd() );
1163 break;
1164
1165 case SHAPE_T::POLY:
1166 {
1167 if( (int) m_editPoints->PointsSize() != shape->GetPointCount() )
1168 {
1169 getView()->Remove( m_editPoints.get() );
1170 m_editedPoint = nullptr;
1172 getView()->Add( m_editPoints.get() );
1173 }
1174 else
1175 {
1176 int ii = 0;
1177
1178 for( const VECTOR2I& pt : shape->GetPolyShape().Outline( 0 ).CPoints() )
1179 m_editPoints->Point( ii++ ).SetPosition( pt );
1180 }
1181
1182 break;
1183 }
1184
1185 case SHAPE_T::RECTANGLE:
1186 {
1187 // point editor works only with rectangles having width and height > 0
1188 // Some symbols can have rectangles with width or height < 0
1189 // So normalize the size:
1190 BOX2I dummy;
1191 dummy.SetOrigin( shape->GetPosition() );
1192 dummy.SetEnd( shape->GetEnd() );
1193 dummy.Normalize();
1194 VECTOR2I topLeft = dummy.GetPosition();
1195 VECTOR2I botRight = dummy.GetEnd();
1196
1197 m_editPoints->Point( RECT_TOPLEFT ).SetPosition( topLeft );
1198 m_editPoints->Point( RECT_TOPRIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
1199 m_editPoints->Point( RECT_BOTLEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
1200 m_editPoints->Point( RECT_BOTRIGHT ).SetPosition( botRight );
1201 break;
1202 }
1203
1204 case SHAPE_T::BEZIER:
1205 m_editPoints->Point( BEZIER_START ).SetPosition( shape->GetStart() );
1206 m_editPoints->Point( BEZIER_CTRL_PT1 ).SetPosition( shape->GetBezierC1() );
1207 m_editPoints->Point( BEZIER_CTRL_PT2 ).SetPosition( shape->GetBezierC2() );
1208 m_editPoints->Point( BEZIER_END ).SetPosition( shape->GetEnd() );
1209 break;
1210
1211 default:
1213 }
1214
1215 break;
1216 }
1217
1218 case SCH_TEXTBOX_T:
1219 {
1220 SCH_TEXTBOX* textbox = static_cast<SCH_TEXTBOX*>( item );
1221
1222 // point editor works only with rectangles having width and height > 0
1223 // Some symbols can have rectangles with width or height < 0
1224 // So normalize the size:
1225 BOX2I dummy;
1226 dummy.SetOrigin( textbox->GetPosition() );
1227 dummy.SetEnd( textbox->GetEnd() );
1228 dummy.Normalize();
1229 VECTOR2I topLeft = dummy.GetPosition();
1230 VECTOR2I botRight = dummy.GetEnd();
1231
1232 m_editPoints->Point( RECT_TOPLEFT ).SetPosition( topLeft );
1233 m_editPoints->Point( RECT_TOPRIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
1234 m_editPoints->Point( RECT_BOTLEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
1235 m_editPoints->Point( RECT_BOTRIGHT ).SetPosition( botRight );
1236 break;
1237 }
1238
1239 case SCH_TABLECELL_T:
1240 {
1241 SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( item );
1242
1243 m_editPoints->Point( 0 ).SetPosition( cell->GetEndX(),
1244 cell->GetEndY() - cell->GetRectangleHeight() / 2 );
1245 m_editPoints->Point( 1 ).SetPosition( cell->GetEndX() - cell->GetRectangleWidth() / 2,
1246 cell->GetEndY() );
1247 break;
1248 }
1249
1250 case SCH_BITMAP_T:
1251 {
1252 const SCH_BITMAP& bitmap = static_cast<SCH_BITMAP&>( *item );
1253 const REFERENCE_IMAGE& refImage = bitmap.GetReferenceImage();
1254 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
1255 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
1256
1257 m_editPoints->Point( RECT_TOPLEFT ).SetPosition( topLeft );
1258 m_editPoints->Point( RECT_TOPRIGHT ).SetPosition( botRight.x, topLeft.y );
1259 m_editPoints->Point( RECT_BOTLEFT ).SetPosition( topLeft.x, botRight.y );
1260 m_editPoints->Point( RECT_BOTRIGHT ).SetPosition( botRight );
1261
1262 m_editPoints->Point( REFIMG_ORIGIN )
1263 .SetPosition( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
1264 break;
1265 }
1266
1267 case SCH_SHEET_T:
1268 {
1269 SCH_SHEET* sheet = (SCH_SHEET*) item;
1270 VECTOR2I topLeft = sheet->GetPosition();
1271 VECTOR2I botRight = sheet->GetPosition() + sheet->GetSize();
1272
1273 m_editPoints->Point( RECT_TOPLEFT ).SetPosition( topLeft );
1274 m_editPoints->Point( RECT_TOPRIGHT ).SetPosition( botRight.x, topLeft.y );
1275 m_editPoints->Point( RECT_BOTLEFT ).SetPosition( topLeft.x, botRight.y );
1276 m_editPoints->Point( RECT_BOTRIGHT ).SetPosition( botRight );
1277 break;
1278 }
1279
1280 case SCH_LINE_T:
1281 {
1282 SCH_LINE* line = (SCH_LINE*) item;
1283
1284 m_editPoints->Point( LINE_START ).SetPosition( line->GetStartPoint() );
1285 m_editPoints->Point( LINE_END ).SetPosition( line->GetEndPoint() );
1286 break;
1287 }
1288
1289 default:
1290 break;
1291 }
1292
1293 getView()->Update( m_editPoints.get() );
1294}
1295
1296
1298{
1300
1301 if( aPoint )
1302 {
1303 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1304 controls->ForceCursorPosition( true, aPoint->GetPosition() );
1305 controls->ShowCursor( true );
1306 }
1307 else
1308 {
1309 if( m_frame->ToolStackIsEmpty() )
1310 controls->ShowCursor( false );
1311
1312 controls->ForceCursorPosition( false );
1313 }
1314
1315 m_editedPoint = aPoint;
1316}
1317
1318
1320{
1321 bool isRuleArea = false;
1322
1323 if( m_editPoints )
1324 isRuleArea = m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T;
1325
1327 || !( m_editPoints->GetParent()->Type() == SCH_SHAPE_T || isRuleArea ) )
1328 {
1329 return false;
1330 }
1331
1332 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( m_editPoints->GetParent() );
1333
1334 if( shape->GetPolyShape().IsEmpty() )
1335 return false;
1336
1337 SHAPE_LINE_CHAIN& poly = shape->GetPolyShape().Outline( 0 );
1338
1339 if( m_editPoints->GetParent()->Type() == SCH_SHAPE_T && poly.GetPointCount() <= 2 )
1340 return false;
1341 if( m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T && poly.GetPointCount() <= 3 )
1342 return false;
1343
1344 for( const VECTOR2I& pt : poly.CPoints() )
1345 {
1346 if( pt == m_editedPoint->GetPosition() )
1347 return true;
1348 }
1349
1350 return false;
1351}
1352
1353
1355{
1356 if( !m_editPoints
1357 || !( m_editPoints->GetParent()->Type() == SCH_SHAPE_T
1358 || m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T ) )
1359 {
1360 return false;
1361 }
1362
1363 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( m_editPoints->GetParent() );
1364
1365 if( shape->GetShape() != SHAPE_T::POLY )
1366 return false;
1367
1368 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( false );
1369 double threshold = getView()->ToWorld( EDIT_POINT::POINT_SIZE );
1370
1371 return shape->HitTest( cursorPos, (int) threshold );
1372}
1373
1374
1376{
1377 if( !m_editPoints
1378 || !( m_editPoints->GetParent()->Type() == SCH_SHAPE_T
1379 || m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T ) )
1380 return 0;
1381
1382 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( m_editPoints->GetParent() );
1383 SHAPE_LINE_CHAIN& poly = shape->GetPolyShape().Outline( 0 );
1384 SCH_COMMIT commit( m_toolMgr );
1385
1386 commit.Modify( shape, m_frame->GetScreen() );
1387
1389 int currentMinDistance = INT_MAX;
1390 int closestLineStart = 0;
1391 unsigned numPoints = poly.GetPointCount();
1392
1393 if( !shape->IsClosed() )
1394 numPoints -= 1;
1395
1396 for( unsigned i = 0; i < numPoints; ++i )
1397 {
1398 SEG seg = poly.GetSegment( i );
1399 int distance = seg.Distance( cursor );
1400
1401 if( distance < currentMinDistance )
1402 {
1403 currentMinDistance = distance;
1404 closestLineStart = i;
1405 }
1406 }
1407
1408 poly.Insert( closestLineStart + 1, cursor );
1409
1410 updateItem( shape, true );
1411 updatePoints();
1412
1413 commit.Push( _( "Add Corner" ) );
1414 return 0;
1415}
1416
1417
1419{
1421 || !m_editPoints->GetParent()->IsType( { SCH_SHAPE_T, SCH_RULE_AREA_T } ) )
1422 {
1423 return 0;
1424 }
1425
1426 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( m_editPoints->GetParent() );
1427 SHAPE_LINE_CHAIN& poly = shape->GetPolyShape().Outline( 0 );
1428 SCH_COMMIT commit( m_toolMgr );
1429
1430 if( m_editPoints->GetParent()->Type() == SCH_SHAPE_T && poly.GetPointCount() <= 2 )
1431 return 0;
1432 if( m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T && poly.GetPointCount() <= 3 )
1433 return 0;
1434
1435 commit.Modify( shape, m_frame->GetScreen() );
1436
1437 int idx = getEditedPointIndex();
1438 int last = (int) poly.GetPointCount() - 1;
1439
1440 if( idx == 0 && poly.GetPoint( 0 ) == poly.GetPoint( last ) )
1441 {
1442 poly.Remove( idx );
1443 poly.SetPoint( last-1, poly.GetPoint( 0 ) );
1444 }
1445 else
1446 {
1447 poly.Remove( idx );
1448 }
1449
1450 setEditedPoint( nullptr );
1451
1452 updateItem( shape, true );
1453 updatePoints();
1454
1455 commit.Push( _( "Remove Corner" ) );
1456 return 0;
1457}
1458
1459
1461{
1462 updatePoints();
1463 return 0;
1464}
1465
1466
1468{
1476}
1477
1478
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:110
static TOOL_ACTION activatePointEditor
Definition: actions.h:212
static constexpr BOX2< VECTOR2I > ByCorners(const VECTOR2I &aCorner1, const VECTOR2I &aCorner2)
Definition: box2.h:70
constexpr coord_type GetLeft() const
Definition: box2.h:228
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 & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
bool Empty() const
Returns status of an item.
Definition: commit.h:144
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
EDIT_CONSTRAINT for a EDIT_LINE, that constrains the line to move perpendicular to the line itself.
bool IsType(FRAME_T aType) const
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition: eda_item.h:176
EDA_ITEM * GetParent() const
Definition: eda_item.h:103
bool IsNew() const
Definition: eda_item.h:107
void SetStartX(int x)
Definition: eda_shape.h:146
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:206
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:205
void SetEndY(int aY)
Definition: eda_shape.h:177
int GetEndX() const
Definition: eda_shape.h:169
int GetRectangleWidth() const
Definition: eda_shape.cpp:166
void SetStartY(int y)
Definition: eda_shape.h:140
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:279
SHAPE_T GetShape() const
Definition: eda_shape.h:125
int GetEndY() const
Definition: eda_shape.h:168
void SetEndX(int aX)
Definition: eda_shape.h:183
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
int GetPointCount() const
Definition: eda_shape.cpp:1326
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:167
bool IsClosed() const
Definition: eda_shape.cpp:247
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:134
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
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:89
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:203
int GetRectangleHeight() const
Definition: eda_shape.cpp:153
virtual int GetWidth() const
Definition: eda_shape.h:115
virtual void ClearRenderCache()
Definition: eda_text.cpp:529
static std::shared_ptr< EDIT_POINTS > Make(EDA_ITEM *aItem, SCH_BASE_FRAME *frame)
Represent a single point that can be used for modifying items.
Definition: edit_points.h:48
static const int POINT_SIZE
Definition: edit_points.h:190
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
static TOOL_ACTION pointEditorRemoveCorner
Definition: ee_actions.h:151
static TOOL_ACTION pointEditorAddCorner
Definition: ee_actions.h:150
void updatePoints()
Update which point is being edited.
bool m_inPointEditor
Currently available edit points.
void updateParentItem(bool aSnapToGrid, SCH_COMMIT &aCommit) const
< Update item's points with edit points.
int Main(const TOOL_EVENT &aEvent)
void dragPinsOnEdge(const std::vector< SEG > &aOldEdges, const std::vector< VECTOR2I > &aMoveVecs, int aUnit, SCH_COMMIT &aCommit) const
Update edit points with item's points.
void updateEditedPoint(const TOOL_EVENT &aEvent)
Clear references to the points.
void setEditedPoint(EDIT_POINT *aPoint)
Return true if aPoint is the currently modified point.
int modifiedSelection(const TOOL_EVENT &aEvent)
bool Init() override
Init() is called once upon a registration of the tool.
EDIT_POINT * m_editedPoint
< Currently edited point, NULL if there is none.
bool addCornerCondition(const SELECTION &aSelection)
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
void pinEditedCorner(int minWidth, int minHeight, VECTOR2I &topLeft, VECTOR2I &topRight, VECTOR2I &botLeft, VECTOR2I &botRight, EE_GRID_HELPER *aGrid) const
Update the coordinates of 4 corners of a rectangle, according to constraints and the moved corner.
bool isModified(const EDIT_POINT &aPoint) const
int addCorner(const TOOL_EVENT &aEvent)
TOOL_ACTION handlers.
std::shared_ptr< EDIT_POINTS > m_editPoints
bool removeCornerCondition(const SELECTION &aSelection)
int removeCorner(const TOOL_EVENT &aEvent)
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
int getEditedPointIndex() const
int clearEditedPoints(const TOOL_EVENT &aEvent)
Set the current point being edited. NULL means none.
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:238
EE_SELECTION & GetSelection()
A foundation class for a tool operating on a schematic or symbol.
Definition: ee_tool_base.h:48
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
Definition: ee_tool_base.h:84
void updateItem(EDA_ITEM *aItem, bool aUpdateRTree) const
Similar to getView()->Update(), but handles items that are redrawn by their parents and updating the ...
Definition: ee_tool_base.h:109
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:200
bool Init() override
Init() is called once upon a registration of the tool.
Definition: ee_tool_base.h:64
static const TOOL_EVENT ClearedEvent
Definition: actions.h:275
static const TOOL_EVENT SelectedEvent
Definition: actions.h:273
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:280
static const TOOL_EVENT PointSelectedEvent
Definition: actions.h:272
bool GetSnap() const
Definition: grid_helper.h:112
void SetSnap(bool aSnap)
Definition: grid_helper.h:111
virtual VECTOR2I AlignGrid(const VECTOR2I &aPoint, GRID_HELPER_GRIDS aGrid) const
Definition: grid_helper.h:70
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
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 VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:68
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
VECTOR2D ToWorld(const VECTOR2D &aCoord, bool aAbsolute=true) const
Converts a screen space point/vector to a point/vector in world space coordinates.
Definition: view.cpp:484
Define a library symbol object.
Definition: lib_symbol.h:78
std::vector< SCH_PIN * > GetPins(int aUnit=0, int aBodyStyle=0) const
Return a list of pin object pointers from the draw item list.
Definition: lib_symbol.cpp:810
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.
A shim class between EDA_DRAW_FRAME and several derived classes: SYMBOL_EDIT_FRAME,...
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
Object to handle a bitmap image that can be inserted in a schematic.
Definition: sch_bitmap.h:40
REFERENCE_IMAGE & GetReferenceImage()
Definition: sch_bitmap.h:51
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
Definition: sch_commit.cpp:432
virtual void Revert() override
Definition: sch_commit.cpp:510
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:166
int GetUnit() const
Definition: sch_item.h:229
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:41
void SetStartPoint(const VECTOR2I &aPosition)
Definition: sch_line.h:137
VECTOR2I GetEndPoint() const
Definition: sch_line.h:141
VECTOR2I GetStartPoint() const
Definition: sch_line.h:136
void SetEndPoint(const VECTOR2I &aPosition)
Definition: sch_line.h:142
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:108
void SetPosition(const VECTOR2I &aPos) override
Definition: sch_shape.h:71
void SetEditState(int aState)
Definition: sch_shape.h:79
void Normalize()
Definition: sch_shape.cpp:75
void AddPoint(const VECTOR2I &aPosition)
Definition: sch_shape.cpp:576
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition: sch_shape.cpp:114
void CalcEdit(const VECTOR2I &aPosition) override
Calculate the attributes of an item at aPosition when it is being edited.
Definition: sch_shape.h:77
VECTOR2I GetPosition() const override
Definition: sch_shape.h:70
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:57
void SetPositionIgnoringPins(const VECTOR2I &aPosition)
Definition: sch_sheet.cpp:898
int GetMinWidth(bool aFromLeft) const
Return the minimum width of the sheet based on the widths of the sheet pin text.
Definition: sch_sheet.cpp:546
VECTOR2I GetSize() const
Definition: sch_sheet.h:112
VECTOR2I GetPosition() const override
Definition: sch_sheet.h:400
int GetMinHeight(bool aFromTop) const
Return the minimum height that the sheet can be resized based on the sheet pin positions.
Definition: sch_sheet.cpp:580
void Resize(const VECTOR2I &aSize)
Resize this sheet to aSize and adjust all of the labels accordingly.
Definition: sch_sheet.cpp:1011
int GetColSpan() const
Definition: sch_tablecell.h:61
int GetRowSpan() const
Definition: sch_tablecell.h:64
int GetColumn() const
int GetRow() const
void SetRowHeight(int aRow, int aHeight)
Definition: sch_table.h:121
int GetRowHeight(int aRow) const
Definition: sch_table.h:123
void SetColWidth(int aCol, int aWidth)
Definition: sch_table.h:111
int GetColWidth(int aCol) const
Definition: sch_table.h:113
void Normalize()
Definition: sch_table.cpp:135
Definition: seg.h:42
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition: seg.cpp:388
EDA_ITEM * Front() const
Definition: selection.h:172
int Size() const
Returns the number of selected parts.
Definition: selection.h:116
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
virtual const VECTOR2I GetPoint(int aIndex) const override
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
virtual size_t GetPointCount() const override
virtual const SEG GetSegment(int aIndex) const override
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
void Insert(size_t aVertex, const VECTOR2I &aP)
const std::vector< VECTOR2I > & CPoints() const
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
The symbol library editor main window.
bool ToolStackIsEmpty()
Definition: tools_holder.h:125
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 DisableGridSnapping() const
Definition: tool_event.h:363
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
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.
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:283
#define _(s)
#define ENDPOINT
ends. (Used to support dragging.)
#define STARTPOINT
When a line is selected, these flags indicate which.
RECTANGLE_POINTS
@ RECT_BOTLEFT
@ RECT_TOPLEFT
@ RECT_TOPRIGHT
@ RECT_BOTRIGHT
RECTANGLE_LINES
@ RECT_RIGHT
@ RECT_LEFT
@ RECT_BOT
@ RECT_TOP
TABLECELL_POINTS
@ ROW_HEIGHT
@ COL_WIDTH
static const std::vector< KICAD_T > pointEditorTypes
LINE_POINTS
@ LINE_START
@ LINE_END
REFIMAGE_POINTS
@ 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_SCH_SYMBOL_EDITOR
Definition: frame_type.h:35
@ LAYER_NOTES
Definition: layer_ids.h:372
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:96
constexpr int Mils2IU(const EDA_IU_SCALE &aIuScale, int mils)
Definition: eda_units.h:157
std::vector< SEG > GetSegsInDirection(const BOX2I &aBox, DIRECTION_45::Directions aDir)
Get the segments of a box that are in the given direction.
Definition: shape_utils.cpp:83
@ RECT_BOTLEFT
@ RECT_TOPLEFT
@ RECT_TOPRIGHT
@ RECT_BOTRIGHT
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
Utility functions for working with shapes.
std::vector< FAB_LAYER_COLOR > dummy
constexpr int MilsToIU(int mils) const
Definition: base_units.h:93
@ MD_SHIFT
Definition: tool_event.h:142
@ BUT_LEFT
Definition: tool_event.h:131
@ SCH_LINE_T
Definition: typeinfo.h:163
@ SCH_TABLECELL_T
Definition: typeinfo.h:166
@ SCH_SHEET_T
Definition: typeinfo.h:174
@ SCH_SHAPE_T
Definition: typeinfo.h:149
@ SCH_RULE_AREA_T
Definition: typeinfo.h:170
@ SCH_ITEM_LOCATE_GRAPHIC_LINE_T
Definition: typeinfo.h:187
@ SCH_BITMAP_T
Definition: typeinfo.h:164
@ SCH_TEXTBOX_T
Definition: typeinfo.h:152
constexpr int sign(T val)
Definition: util.h:159
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691