KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_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 The 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 "sch_point_editor.h"
26
27#include <algorithm>
28#include <ee_grid_helper.h>
29#include <tool/tool_manager.h>
30#include <sch_commit.h>
31#include <view/view_controls.h>
33#include <geometry/seg.h>
37#include <tools/sch_actions.h>
39#include <sch_edit_frame.h>
40#include <sch_line.h>
41#include <sch_bitmap.h>
42#include <sch_sheet.h>
43#include <sch_textbox.h>
44#include <sch_table.h>
46
47
55
56
57// Few constants to avoid using bare numbers for point indices
62
63
68
69
74
75
80
81
86
91
92
94{
95public:
97 m_line( aLine ),
98 m_screen( aScreen )
99 {
100 }
101
102 void MakePoints( EDIT_POINTS& aPoints ) override
103 {
104 std::pair<EDA_ITEM*, int> connectedStart = { nullptr, STARTPOINT };
105 std::pair<EDA_ITEM*, int> connectedEnd = { nullptr, STARTPOINT };
106
107 for( SCH_ITEM* test : m_screen.Items().OfType( SCH_LINE_T ) )
108 {
109 if( test->GetLayer() != LAYER_NOTES )
110 continue;
111
112 if( test == &m_line )
113 continue;
114
115 SCH_LINE* testLine = static_cast<SCH_LINE*>( test );
116
117 if( testLine->GetStartPoint() == m_line.GetStartPoint() )
118 {
119 connectedStart = { testLine, STARTPOINT };
120 }
121 else if( testLine->GetEndPoint() == m_line.GetStartPoint() )
122 {
123 connectedStart = { testLine, ENDPOINT };
124 }
125 else if( testLine->GetStartPoint() == m_line.GetEndPoint() )
126 {
127 connectedEnd = { testLine, STARTPOINT };
128 }
129 else if( testLine->GetEndPoint() == m_line.GetEndPoint() )
130 {
131 connectedEnd = { testLine, ENDPOINT };
132 }
133 }
134
135 aPoints.AddPoint( m_line.GetStartPoint(), connectedStart );
136 aPoints.AddPoint( m_line.GetEndPoint(), connectedEnd );
137 }
138
139 bool UpdatePoints( EDIT_POINTS& aPoints ) override
140 {
141 aPoints.Point( LINE_START ).SetPosition( m_line.GetStartPoint() );
142 aPoints.Point( LINE_END ).SetPosition( m_line.GetEndPoint() );
143 return true;
144 }
145
146 void UpdateItem( const EDIT_POINT& aEditedPoints, EDIT_POINTS& aPoints, COMMIT& aCommit,
147 std::vector<EDA_ITEM*>& aUpdatedItems ) override
148 {
149 m_line.SetStartPoint( aPoints.Point( LINE_START ).GetPosition() );
150 m_line.SetEndPoint( aPoints.Point( LINE_END ).GetPosition() );
151
152 std::pair<EDA_ITEM*, int> connected = aPoints.Point( LINE_START ).GetConnected();
153
154 if( connected.first )
155 {
156 aCommit.Modify( connected.first, &m_screen );
157 aUpdatedItems.push_back( connected.first );
158
159 if( connected.second == STARTPOINT )
160 static_cast<SCH_LINE*>( connected.first )->SetStartPoint( m_line.GetStartPoint() );
161 else if( connected.second == ENDPOINT )
162 static_cast<SCH_LINE*>( connected.first )->SetEndPoint( m_line.GetStartPoint() );
163 }
164
165 connected = aPoints.Point( LINE_END ).GetConnected();
166
167 if( connected.first )
168 {
169 aCommit.Modify( connected.first, &m_screen );
170 aUpdatedItems.push_back( connected.first );
171
172 if( connected.second == STARTPOINT )
173 static_cast<SCH_LINE*>( connected.first )->SetStartPoint( m_line.GetEndPoint() );
174 else if( connected.second == ENDPOINT )
175 static_cast<SCH_LINE*>( connected.first )->SetEndPoint( m_line.GetEndPoint() );
176 }
177 }
178
179private:
182};
183
185{
186public:
188 m_bitmap( aBitmap )
189 {}
190
191 void MakePoints( EDIT_POINTS& aPoints ) override
192 {
193 const REFERENCE_IMAGE& refImage = m_bitmap.GetReferenceImage();
194 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
195 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
196
197 aPoints.AddPoint( topLeft );
198 aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
199 aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
200 aPoints.AddPoint( botRight );
201
202 aPoints.AddPoint( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
203 }
204
205 bool UpdatePoints( EDIT_POINTS& aPoints ) override
206 {
207 const REFERENCE_IMAGE& refImage = m_bitmap.GetReferenceImage();
208 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
209 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
210
211 aPoints.Point( RECT_TOPLEFT ).SetPosition( topLeft );
212 aPoints.Point( RECT_TOPRIGHT ).SetPosition( botRight.x, topLeft.y );
213 aPoints.Point( RECT_BOTLEFT ).SetPosition( topLeft.x, botRight.y );
214 aPoints.Point( RECT_BOTRIGHT ).SetPosition( botRight );
215
216 aPoints.Point( REFIMG_ORIGIN ).SetPosition( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
217 return true;
218 }
219
220 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
221 std::vector<EDA_ITEM*>& aUpdatedItems ) override
222 {
223 REFERENCE_IMAGE& refImg = m_bitmap.GetReferenceImage();
224 const VECTOR2I topLeft = aPoints.Point( RECT_TOPLEFT ).GetPosition();
225 const VECTOR2I topRight = aPoints.Point( RECT_TOPRIGHT ).GetPosition();
226 const VECTOR2I botLeft = aPoints.Point( RECT_BOTLEFT ).GetPosition();
227 const VECTOR2I botRight = aPoints.Point( RECT_BOTRIGHT ).GetPosition();
228 const VECTOR2I xfrmOrigin = aPoints.Point( REFIMG_ORIGIN ).GetPosition();
229
230 if( isModified( aEditedPoint, aPoints.Point( REFIMG_ORIGIN ) ) )
231 {
232 // Moving the transform origin
233 // As the other points didn't move, we can get the image extent from them
234 const VECTOR2I newOffset = xfrmOrigin - ( topLeft + botRight ) / 2;
235 refImg.SetTransformOriginOffset( newOffset );
236 }
237 else
238 {
239 const VECTOR2I oldOrigin = refImg.GetPosition() + refImg.GetTransformOriginOffset();
240 const VECTOR2I oldSize = refImg.GetSize();
241 const VECTOR2I pos = refImg.GetPosition();
242
243 OPT_VECTOR2I newCorner;
244 VECTOR2I oldCorner = pos;
245
246 if( isModified( aEditedPoint, aPoints.Point( RECT_TOPLEFT ) ) )
247 {
248 newCorner = topLeft;
249 oldCorner -= oldSize / 2;
250 }
251 else if( isModified( aEditedPoint, aPoints.Point( RECT_TOPRIGHT ) ) )
252 {
253 newCorner = topRight;
254 oldCorner -= VECTOR2I( -oldSize.x, oldSize.y ) / 2;
255 }
256 else if( isModified( aEditedPoint, aPoints.Point( RECT_BOTLEFT ) ) )
257 {
258 newCorner = botLeft;
259 oldCorner -= VECTOR2I( oldSize.x, -oldSize.y ) / 2;
260 }
261 else if( isModified( aEditedPoint, aPoints.Point( RECT_BOTRIGHT ) ) )
262 {
263 newCorner = botRight;
264 oldCorner += oldSize / 2;
265 }
266
267 if( newCorner )
268 {
269 // Turn in the respective vectors from the origin
270 *newCorner -= xfrmOrigin;
271 oldCorner -= oldOrigin;
272
273 // If we tried to cross the origin, clamp it to stop it
274 if( sign( newCorner->x ) != sign( oldCorner.x )
275 || sign( newCorner->y ) != sign( oldCorner.y ) )
276 {
277 *newCorner = VECTOR2I( 0, 0 );
278 }
279
280 const double newLength = newCorner->EuclideanNorm();
281 const double oldLength = oldCorner.EuclideanNorm();
282
283 double ratio = oldLength > 0 ? ( newLength / oldLength ) : 1.0;
284
285 // Clamp the scaling to a minimum of 50 mils
286 VECTOR2I newSize = oldSize * ratio;
287 double newWidth = std::max( newSize.x, EDA_UNIT_UTILS::Mils2IU( schIUScale, 50 ) );
288 double newHeight = std::max( newSize.y, EDA_UNIT_UTILS::Mils2IU( schIUScale, 50 ) );
289 ratio = std::min( newWidth / oldSize.x, newHeight / oldSize.y );
290
291 // Also handles the origin offset
292 refImg.SetImageScale( refImg.GetImageScale() * ratio );
293 }
294 }
295 aUpdatedItems.push_back( &m_bitmap );
296 }
297
298private:
300};
301
302
304{
305public:
308 m_cell( aCell ),
309 m_screen( aScreen )
310 {
311 }
312
313 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
314 std::vector<EDA_ITEM*>& aUpdatedItems ) override
315 {
316 SCH_TABLE& table = static_cast<SCH_TABLE&>( *m_cell.GetParent() );
317 bool rotated = !m_cell.GetTextAngle().IsHorizontal();
318
319 aCommit.Modify( &table, &m_screen );
320 aUpdatedItems.push_back( &table );
321
322 if( rotated )
323 {
324 if( isModified( aEditedPoint, aPoints.Point( ROW_HEIGHT ) ) )
325 {
326 m_cell.SetEnd( VECTOR2I( m_cell.GetEndX(), aPoints.Point( ROW_HEIGHT ).GetY() ) );
327
328 int colWidth = std::abs( m_cell.GetRectangleHeight() );
329
330 for( int ii = 0; ii < m_cell.GetColSpan() - 1; ++ii )
331 colWidth -= table.GetColWidth( m_cell.GetColumn() + ii );
332
333 table.SetColWidth( m_cell.GetColumn() + m_cell.GetColSpan() - 1, colWidth );
334 }
335 else if( isModified( aEditedPoint, aPoints.Point( COL_WIDTH ) ) )
336 {
337 m_cell.SetEnd( VECTOR2I( aPoints.Point( COL_WIDTH ).GetX(), m_cell.GetEndY() ) );
338
339 int rowHeight = m_cell.GetRectangleWidth();
340
341 for( int ii = 0; ii < m_cell.GetRowSpan() - 1; ++ii )
342 rowHeight -= table.GetRowHeight( m_cell.GetRow() + ii );
343
344 table.SetRowHeight( m_cell.GetRow() + m_cell.GetRowSpan() - 1, rowHeight );
345 }
346 }
347 else
348 {
349 if( isModified( aEditedPoint, aPoints.Point( COL_WIDTH ) ) )
350 {
351 m_cell.SetEnd( VECTOR2I( aPoints.Point( COL_WIDTH ).GetX(), m_cell.GetEndY() ) );
352
353 int colWidth = m_cell.GetRectangleWidth();
354
355 for( int ii = 0; ii < m_cell.GetColSpan() - 1; ++ii )
356 colWidth -= table.GetColWidth( m_cell.GetColumn() + ii );
357
358 table.SetColWidth( m_cell.GetColumn() + m_cell.GetColSpan() - 1, colWidth );
359 }
360 else if( isModified( aEditedPoint, aPoints.Point( ROW_HEIGHT ) ) )
361 {
362 m_cell.SetEnd( VECTOR2I( m_cell.GetEndX(), aPoints.Point( ROW_HEIGHT ).GetY() ) );
363
364 int rowHeight = m_cell.GetRectangleHeight();
365
366 for( int ii = 0; ii < m_cell.GetRowSpan() - 1; ++ii )
367 rowHeight -= table.GetRowHeight( m_cell.GetRow() + ii );
368
369 table.SetRowHeight( m_cell.GetRow() + m_cell.GetRowSpan() - 1, rowHeight );
370 }
371 }
372
373 table.Normalize();
374 }
375
376private:
379};
380
381
383{
384public:
386 m_rect( aRect ),
387 m_frame( aFrame )
388 {
389 }
390
391 static void MakePoints( SCH_SHAPE& aRect, EDIT_POINTS& aPoints )
392 {
393 VECTOR2I topLeft = aRect.GetPosition();
394 VECTOR2I botRight = aRect.GetEnd();
395
396 aPoints.AddPoint( topLeft );
397 aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
398 aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
399 aPoints.AddPoint( botRight );
400 aPoints.AddPoint( aRect.GetCenter() );
401 aPoints.AddPoint( VECTOR2I( botRight.x - aRect.GetCornerRadius(), topLeft.y ) );
402 aPoints.Point( RECT_RADIUS ).SetDrawCircle();
403
404 aPoints.AddLine( aPoints.Point( RECT_TOPLEFT ), aPoints.Point( RECT_TOPRIGHT ) );
405 aPoints.Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_TOP ) ) );
406 aPoints.AddLine( aPoints.Point( RECT_TOPRIGHT ), aPoints.Point( RECT_BOTRIGHT ) );
407 aPoints.Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_RIGHT ) ) );
408 aPoints.AddLine( aPoints.Point( RECT_BOTRIGHT ), aPoints.Point( RECT_BOTLEFT ) );
409 aPoints.Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_BOT ) ) );
410 aPoints.AddLine( aPoints.Point( RECT_BOTLEFT ), aPoints.Point( RECT_TOPLEFT ) );
411 aPoints.Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_LEFT ) ) );
412 }
413
414 static void UpdatePoints( SCH_SHAPE& aRect, EDIT_POINTS& aPoints )
415 {
416 VECTOR2I topLeft = aRect.GetPosition();
417 VECTOR2I botRight = aRect.GetEnd();
418
419 aPoints.Point( RECT_TOPLEFT ).SetPosition( topLeft );
420 aPoints.Point( RECT_RADIUS ).SetPosition( VECTOR2I( botRight.x - aRect.GetCornerRadius(), topLeft.y ) );
421 aPoints.Point( RECT_TOPRIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
422 aPoints.Point( RECT_BOTLEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
423 aPoints.Point( RECT_BOTRIGHT ).SetPosition( botRight );
424 aPoints.Point( RECT_CENTER ).SetPosition( aRect.GetCenter() );
425 }
426
437 static void PinEditedCorner( const EDIT_POINT& aEditedPoint, const EDIT_POINTS& aPoints,
438 int minWidth, int minHeight, VECTOR2I& topLeft, VECTOR2I& topRight,
439 VECTOR2I& botLeft, VECTOR2I& botRight )
440 {
441 if( isModified( aEditedPoint, aPoints.Point( RECT_TOPLEFT ) ) )
442 {
443 // pin edited point within opposite corner
444 topLeft.x = std::min( topLeft.x, botRight.x - minWidth );
445 topLeft.y = std::min( topLeft.y, botRight.y - minHeight );
446
447 // push edited point edges to adjacent corners
448 topRight.y = topLeft.y;
449 botLeft.x = topLeft.x;
450 }
451 else if( isModified( aEditedPoint, aPoints.Point( RECT_TOPRIGHT ) ) )
452 {
453 // pin edited point within opposite corner
454 topRight.x = std::max( topRight.x, botLeft.x + minWidth );
455 topRight.y = std::min( topRight.y, botLeft.y - minHeight );
456
457 // push edited point edges to adjacent corners
458 topLeft.y = topRight.y;
459 botRight.x = topRight.x;
460 }
461 else if( isModified( aEditedPoint, aPoints.Point( RECT_BOTLEFT ) ) )
462 {
463 // pin edited point within opposite corner
464 botLeft.x = std::min( botLeft.x, topRight.x - minWidth );
465 botLeft.y = std::max( botLeft.y, topRight.y + minHeight );
466
467 // push edited point edges to adjacent corners
468 botRight.y = botLeft.y;
469 topLeft.x = botLeft.x;
470 }
471 else if( isModified( aEditedPoint, aPoints.Point( RECT_BOTRIGHT ) ) )
472 {
473 // pin edited point within opposite corner
474 botRight.x = std::max( botRight.x, topLeft.x + minWidth );
475 botRight.y = std::max( botRight.y, topLeft.y + minHeight );
476
477 // push edited point edges to adjacent corners
478 botLeft.y = botRight.y;
479 topRight.x = botRight.x;
480 }
481 else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
482 {
483 topLeft.y = std::min( topLeft.y, botRight.y - minHeight );
484 }
485 else if( isModified( aEditedPoint, aPoints.Line( RECT_LEFT ) ) )
486 {
487 topLeft.x = std::min( topLeft.x, botRight.x - minWidth );
488 }
489 else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
490 {
491 botRight.y = std::max( botRight.y, topLeft.y + minHeight );
492 }
493 else if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
494 {
495 botRight.x = std::max( botRight.x, topLeft.x + minWidth );
496 }
497 }
498
499 static void UpdateItem( SCH_SHAPE& aRect, const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints )
500 {
501 VECTOR2I topLeft = aPoints.Point( RECT_TOPLEFT ).GetPosition();
502 VECTOR2I topRight = aPoints.Point( RECT_TOPRIGHT ).GetPosition();
503 VECTOR2I botLeft = aPoints.Point( RECT_BOTLEFT ).GetPosition();
504 VECTOR2I botRight = aPoints.Point( RECT_BOTRIGHT ).GetPosition();
505
506 PinEditedCorner( aEditedPoint, aPoints, schIUScale.MilsToIU( 1 ), schIUScale.MilsToIU( 1 ),
507 topLeft, topRight, botLeft, botRight );
508
509 if( isModified( aEditedPoint, aPoints.Point( RECT_TOPLEFT ) )
510 || isModified( aEditedPoint, aPoints.Point( RECT_TOPRIGHT ) )
511 || isModified( aEditedPoint, aPoints.Point( RECT_BOTRIGHT ) )
512 || isModified( aEditedPoint, aPoints.Point( RECT_BOTLEFT ) ) )
513 {
514 aRect.SetPosition( topLeft );
515 aRect.SetEnd( botRight );
516 }
517 else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
518 {
519 aRect.SetStartY( topLeft.y );
520 }
521 else if( isModified( aEditedPoint, aPoints.Line( RECT_LEFT ) ) )
522 {
523 aRect.SetStartX( topLeft.x );
524 }
525 else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
526 {
527 aRect.SetEndY( botRight.y );
528 }
529 else if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
530 {
531 aRect.SetEndX( botRight.x );
532 }
533
534 for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
535 {
536 if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
537 {
538 aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
539 }
540 }
541 }
542
543 void MakePoints( EDIT_POINTS& aPoints ) override
544 {
545 m_rect.Normalize();
546 MakePoints( m_rect, aPoints );
547 }
548
549 bool UpdatePoints( EDIT_POINTS& aPoints ) override
550 {
551 m_rect.Normalize();
552 UpdatePoints( m_rect, aPoints );
553 return true;
554 }
555
556 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
557 std::vector<EDA_ITEM*>& aUpdatedItems ) override
558 {
559 VECTOR2I topLeft = aPoints.Point( RECT_TOPLEFT ).GetPosition();
560 VECTOR2I topRight = aPoints.Point( RECT_TOPRIGHT ).GetPosition();
561 VECTOR2I botLeft = aPoints.Point( RECT_BOTLEFT ).GetPosition();
562 VECTOR2I botRight = aPoints.Point( RECT_BOTRIGHT ).GetPosition();
563
564 PinEditedCorner( aEditedPoint, aPoints, schIUScale.MilsToIU( 1 ), schIUScale.MilsToIU( 1 ),
565 topLeft, topRight, botLeft, botRight );
566
567 const BOX2I oldBox = BOX2I::ByCorners( m_rect.GetStart(), m_rect.GetEnd() );
568 std::vector<SEG> oldSegs;
569 std::vector<VECTOR2I> moveVecs;
570
571 if( isModified( aEditedPoint, aPoints.Point( RECT_TOPLEFT ) )
572 || isModified( aEditedPoint, aPoints.Point( RECT_TOPRIGHT ) )
573 || isModified( aEditedPoint, aPoints.Point( RECT_BOTRIGHT ) )
574 || isModified( aEditedPoint, aPoints.Point( RECT_BOTLEFT ) ) )
575 {
576 // Corner drags don't update pins. Not only is it an escape hatch to avoid
577 // moving pins, it also avoids tricky problems when the pins "fall off"
578 // the ends of one of the two segments and get either left behind or
579 // "swept up" into the corner.
580 m_rect.SetPosition( topLeft );
581 m_rect.SetEnd( botRight );
582 }
583 else if( isModified( aEditedPoint, aPoints.Point( RECT_CENTER ) ) )
584 {
585 VECTOR2I moveVec = aPoints.Point( RECT_CENTER ).GetPosition() - oldBox.GetCenter();
586 m_rect.Move( moveVec );
587 }
588 else if( isModified( aEditedPoint, aPoints.Point( RECT_RADIUS ) ) )
589 {
590 int width = std::abs( botRight.x - topLeft.x );
591 int height = std::abs( botRight.y - topLeft.y );
592 int maxRadius = std::min( width, height ) / 2;
593 int x = aPoints.Point( RECT_RADIUS ).GetX();
594 x = std::clamp( x, botRight.x - maxRadius, botRight.x );
595 aPoints.Point( RECT_RADIUS ).SetPosition( VECTOR2I( x, topLeft.y ) );
596 m_rect.SetCornerRadius( botRight.x - x );
597 }
598 else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
599 {
601 moveVecs.emplace_back( 0, topLeft.y - oldBox.GetTop() );
602 m_rect.SetStartY( topLeft.y );
603 }
604 else if( isModified( aEditedPoint, aPoints.Line( RECT_LEFT ) ) )
605 {
607 moveVecs.emplace_back( topLeft.x - oldBox.GetLeft(), 0 );
608 m_rect.SetStartX( topLeft.x );
609 }
610 else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
611 {
613 moveVecs.emplace_back( 0, botRight.y - oldBox.GetBottom() );
614 m_rect.SetEndY( botRight.y );
615 }
616 else if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
617 {
619 moveVecs.emplace_back( botRight.x - oldBox.GetRight(), 0 );
620 m_rect.SetEndX( botRight.x );
621 }
622
623 dragPinsOnEdge( oldSegs, moveVecs, m_rect.GetUnit(), aCommit, aUpdatedItems );
624
625 for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
626 {
627 if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
628 {
629 aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
630 }
631 }
632 }
633
634private:
635 void dragPinsOnEdge( const std::vector<SEG>& aOldEdges, const std::vector<VECTOR2I>& aMoveVecs,
636 int aEdgeUnit, COMMIT& aCommit, std::vector<EDA_ITEM*>& aUpdatedItems ) const
637 {
638 wxCHECK( aOldEdges.size() == aMoveVecs.size(), /* void */ );
639
640 // This only make sense in the symbol editor
641 if( !m_frame.IsType( FRAME_SCH_SYMBOL_EDITOR ) )
642 return;
643
645
646 // And only if the setting is enabled
647 if( !editor.GetSettings()->m_dragPinsAlongWithEdges )
648 return;
649
650 // Adjuting pins on a different unit to a unit-limited shape
651 // seems suspect.
652 wxCHECK( aEdgeUnit == 0 || aEdgeUnit == editor.GetUnit(), /* void */ );
653
654 /*
655 * Get a list of pins on a line segment
656 */
657 const auto getPinsOnSeg =
658 []( LIB_SYMBOL& aSymbol, int aUnit, const SEG& aSeg,
659 bool aIncludeEnds ) -> std::vector<SCH_PIN*>
660 {
661 std::vector<SCH_PIN*> pins;
662
663 for( SCH_PIN* pin : aSymbol.GetGraphicalPins( aUnit, 0 ) )
664 {
665 // Figure out if the pin "connects" to the line
666 const VECTOR2I pinRootPos = pin->GetPinRoot();
667
668 if( aSeg.Contains( pinRootPos ) )
669 {
670 if( aIncludeEnds || ( pinRootPos != aSeg.A && pinRootPos != aSeg.B ) )
671 {
672 pins.push_back( pin );
673 }
674 }
675 }
676
677 return pins;
678 };
679
680 LIB_SYMBOL* const symbol = editor.GetCurSymbol();
681
682 for( std::size_t i = 0; i < aOldEdges.size(); ++i )
683 {
684 if( aMoveVecs[i] == VECTOR2I( 0, 0 ) || !symbol )
685 continue;
686
687 const std::vector<SCH_PIN*> pins = getPinsOnSeg( *symbol, aEdgeUnit, aOldEdges[i], false );
688
689 for( SCH_PIN* pin : pins )
690 {
691 aCommit.Modify( pin, editor.GetScreen() );
692 aUpdatedItems.push_back( pin );
693
694 // Move the pin
695 pin->Move( aMoveVecs[i] );
696 }
697 }
698 }
699
700private:
703};
704
705
707{
708public:
710 m_textbox( aTextbox )
711 {}
712
713 void MakePoints( EDIT_POINTS& aPoints ) override
714 {
715 m_textbox.Normalize();
717 }
718
719 bool UpdatePoints( EDIT_POINTS& aPoints ) override
720 {
721 // point editor works only with rectangles having width and height > 0
722 // Some symbols can have rectangles with width or height < 0
723 // So normalize the size:
724 m_textbox.Normalize();
726 return true;
727 }
728
729 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
730 std::vector<EDA_ITEM*>& aUpdatedItems ) override
731 {
733 m_textbox.ClearRenderCache();
734 }
735
736private:
738};
739
740
742{
743public:
745 m_sheet( aSheet )
746 {}
747
748 void MakePoints( EDIT_POINTS& aPoints ) override
749 {
750 VECTOR2I topLeft = m_sheet.GetPosition();
751 VECTOR2I botRight = m_sheet.GetPosition() + m_sheet.GetSize();
752
753 aPoints.AddPoint( topLeft );
754 aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
755 aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
756 aPoints.AddPoint( botRight );
757
758 aPoints.AddLine( aPoints.Point( RECT_TOPLEFT ), aPoints.Point( RECT_TOPRIGHT ) );
759 aPoints.Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_TOP ) ) );
760 aPoints.AddLine( aPoints.Point( RECT_TOPRIGHT ), aPoints.Point( RECT_BOTRIGHT ) );
761 aPoints.Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_RIGHT ) ) );
762 aPoints.AddLine( aPoints.Point( RECT_BOTRIGHT ), aPoints.Point( RECT_BOTLEFT ) );
763 aPoints.Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_BOT ) ) );
764 aPoints.AddLine( aPoints.Point( RECT_BOTLEFT ), aPoints.Point( RECT_TOPLEFT ) );
765 aPoints.Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_LEFT ) ) );
766 }
767
768 bool UpdatePoints( EDIT_POINTS& aPoints ) override
769 {
770 VECTOR2I topLeft = m_sheet.GetPosition();
771 VECTOR2I botRight = m_sheet.GetPosition() + m_sheet.GetSize();
772
773 aPoints.Point( RECT_TOPLEFT ).SetPosition( topLeft );
774 aPoints.Point( RECT_TOPRIGHT ).SetPosition( botRight.x, topLeft.y );
775 aPoints.Point( RECT_BOTLEFT ).SetPosition( topLeft.x, botRight.y );
776 aPoints.Point( RECT_BOTRIGHT ).SetPosition( botRight );
777 return true;
778 }
779
780 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
781 std::vector<EDA_ITEM*>& aUpdatedItems ) override
782 {
783 VECTOR2I topLeft = aPoints.Point( RECT_TOPLEFT ).GetPosition();
784 VECTOR2I topRight = aPoints.Point( RECT_TOPRIGHT ).GetPosition();
785 VECTOR2I botLeft = aPoints.Point( RECT_BOTLEFT ).GetPosition();
786 VECTOR2I botRight = aPoints.Point( RECT_BOTRIGHT ).GetPosition();
787 VECTOR2I sheetNewPos = m_sheet.GetPosition();
788 VECTOR2I sheetNewSize = m_sheet.GetSize();
789
790 bool editedTopRight = isModified( aEditedPoint, aPoints.Point( RECT_TOPRIGHT ) );
791 bool editedBotLeft = isModified( aEditedPoint, aPoints.Point( RECT_BOTLEFT ) );
792 bool editedBotRight = isModified( aEditedPoint, aPoints.Point( RECT_BOTRIGHT ) );
793
794 if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
795 editedTopRight = true;
796 else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
797 editedBotLeft = true;
798
800 aEditedPoint, aPoints, m_sheet.GetMinWidth( editedTopRight || editedBotRight ),
801 m_sheet.GetMinHeight( editedBotLeft || editedBotRight ), topLeft, topRight, botLeft,
802 botRight );
803
804 if( isModified( aEditedPoint, aPoints.Point( RECT_TOPLEFT ) )
805 || isModified( aEditedPoint, aPoints.Point( RECT_TOPRIGHT ) )
806 || isModified( aEditedPoint, aPoints.Point( RECT_BOTRIGHT ) )
807 || isModified( aEditedPoint, aPoints.Point( RECT_BOTLEFT ) ) )
808 {
809 sheetNewPos = topLeft;
810 sheetNewSize = VECTOR2I( botRight.x - topLeft.x, botRight.y - topLeft.y );
811 }
812 else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
813 {
814 sheetNewPos = VECTOR2I( m_sheet.GetPosition().x, topLeft.y );
815 sheetNewSize = VECTOR2I( m_sheet.GetSize().x, botRight.y - topLeft.y );
816 }
817 else if( isModified( aEditedPoint, aPoints.Line( RECT_LEFT ) ) )
818 {
819 sheetNewPos = VECTOR2I( topLeft.x, m_sheet.GetPosition().y );
820 sheetNewSize = VECTOR2I( botRight.x - topLeft.x, m_sheet.GetSize().y );
821 }
822 else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
823 {
824 sheetNewSize = VECTOR2I( m_sheet.GetSize().x, botRight.y - topLeft.y );
825 }
826 else if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
827 {
828 sheetNewSize = VECTOR2I( botRight.x - topLeft.x, m_sheet.GetSize().y );
829 }
830
831 for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
832 {
833 if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
834 {
835 aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
836 }
837 }
838
839 if( m_sheet.GetPosition() != sheetNewPos )
840 m_sheet.SetPositionIgnoringPins( sheetNewPos );
841
842 if( m_sheet.GetSize() != sheetNewSize )
843 m_sheet.Resize( sheetNewSize );
844 }
845
846private:
848};
849
850
852{
853 m_editBehavior = nullptr;
854 m_editPoints = std::make_shared<EDIT_POINTS>( aItem );
855
856 if( !aItem )
857 return;
858
859 // Generate list of edit points based on the item type
860 switch( aItem->Type() )
861 {
862 case SCH_SHAPE_T:
863 {
864 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( aItem );
865
866 switch( shape->GetShape() )
867 {
868 case SHAPE_T::ARC:
869 m_editBehavior = std::make_unique<EDA_ARC_POINT_EDIT_BEHAVIOR>(
870 *shape, m_arcEditMode, *getViewControls() );
871 break;
872 case SHAPE_T::CIRCLE:
873 m_editBehavior = std::make_unique<EDA_CIRCLE_POINT_EDIT_BEHAVIOR>( *shape );
874 break;
876 m_editBehavior = std::make_unique<RECTANGLE_POINT_EDIT_BEHAVIOR>( *shape, *m_frame );
877 break;
878 case SHAPE_T::POLY:
879 m_editBehavior = std::make_unique<EDA_POLYGON_POINT_EDIT_BEHAVIOR>( *shape );
880 break;
881 case SHAPE_T::BEZIER:
882 {
883 int maxError = schIUScale.mmToIU( ARC_LOW_DEF_MM );
884
885 if( SCHEMATIC* schematic = shape->Schematic() )
886 maxError = schematic->Settings().m_MaxError;
887
888 m_editBehavior = std::make_unique<EDA_BEZIER_POINT_EDIT_BEHAVIOR>( *shape, maxError );
889 break;
890 }
891 default:
893 }
894
895 break;
896 }
897 case SCH_RULE_AREA_T:
898 {
899 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( aItem );
900 // Implemented directly as a polygon
901 m_editBehavior = std::make_unique<EDA_POLYGON_POINT_EDIT_BEHAVIOR>( *shape );
902 break;
903 }
904 case SCH_TEXTBOX_T:
905 {
906 SCH_TEXTBOX* textbox = static_cast<SCH_TEXTBOX*>( aItem );
907 m_editBehavior = std::make_unique<TEXTBOX_POINT_EDIT_BEHAVIOR>( *textbox );
908 break;
909 }
910 case SCH_TABLECELL_T:
911 {
912 SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( aItem );
913 m_editBehavior = std::make_unique<SCH_TABLECELL_POINT_EDIT_BEHAVIOR>( *cell, *m_frame->GetScreen() );
914 break;
915 }
916 case SCH_SHEET_T:
917 {
918 SCH_SHEET& sheet = static_cast<SCH_SHEET&>( *aItem );
919 m_editBehavior = std::make_unique<SHEET_POINT_EDIT_BEHAVIOR>( sheet );
920 break;
921 }
922 case SCH_BITMAP_T:
923 {
924 SCH_BITMAP& bitmap = static_cast<SCH_BITMAP&>( *aItem );
925 m_editBehavior = std::make_unique<BITMAP_POINT_EDIT_BEHAVIOR>( bitmap );
926 break;
927 }
928 case SCH_LINE_T:
929 {
930 SCH_LINE& line = static_cast<SCH_LINE&>( *aItem );
931 m_editBehavior = std::make_unique<LINE_POINT_EDIT_BEHAVIOR>( line, *m_frame->GetScreen() );
932 break;
933 }
934 default:
935 {
936 m_editPoints.reset();
937 break;
938 }
939 }
940
941 // If we got a behavior, generate the points
942 if( m_editBehavior )
943 {
944 wxCHECK( m_editPoints, /* void */ );
945 m_editBehavior->MakePoints( *m_editPoints );
946 }
947}
948
949
957
958
960{
961 SCH_TOOL_BASE::Reset( aReason );
962
963 m_editPoints.reset();
964 m_editedPoint = nullptr;
965}
966
967
969{
971
973
974 const auto addCornerCondition = [&]( const SELECTION& aSelection ) -> bool
975 {
976 return SCH_POINT_EDITOR::addCornerCondition( aSelection );
977 };
978
979 const auto removeCornerCondition = [&]( const SELECTION& aSelection ) -> bool
980 {
981 return SCH_POINT_EDITOR::removeCornerCondition( aSelection );
982 };
983
984 const auto arcIsEdited = [&]( const SELECTION& aSelection ) -> bool
985 {
986 const EDA_ITEM* item = aSelection.Front();
987 return ( item != nullptr ) && ( item->Type() == SCH_SHAPE_T )
988 && static_cast<const SCH_SHAPE*>( item )->GetShape() == SHAPE_T::ARC;
989 };
990
991 auto& menu = m_selectionTool->GetToolMenu().GetMenu();
992
993 // clang-format off
996 menu.AddItem( ACTIONS::cycleArcEditMode, S_C::Count( 1 ) && arcIsEdited );
997 // clang-format on
998
999 return true;
1000}
1001
1002
1004{
1005 setEditedPoint( nullptr );
1006
1007 return 0;
1008}
1009
1010
1012{
1013 EDIT_POINT* point = m_editedPoint;
1014
1015 if( !m_editPoints )
1016 {
1017 point = nullptr;
1018 }
1019 else if( aEvent.IsMotion() )
1020 {
1021 point = m_editPoints->FindPoint( aEvent.Position(), getView() );
1022 }
1023 else if( aEvent.IsDrag( BUT_LEFT ) )
1024 {
1025 point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
1026 }
1027 else
1028 {
1029 point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition( false ), getView() );
1030 }
1031
1032 if( m_editedPoint != point )
1033 setEditedPoint( point );
1034}
1035
1036
1038{
1039 if( !m_selectionTool )
1040 return 0;
1041
1042 if( m_inPointEditor )
1043 return 0;
1044
1046
1047 if( m_isSymbolEditor )
1048 {
1050
1051 if( !editor->IsSymbolEditable() || editor->IsSymbolAlias() )
1052 return 0;
1053 }
1054
1055 const SCH_SELECTION& selection = m_selectionTool->GetSelection();
1056
1057 if( selection.Size() != 1 || !selection.Front()->IsType( pointEditorTypes ) )
1058 return 0;
1059
1060 // Wait till drawing tool is done
1061 if( selection.Front()->IsNew() )
1062 return 0;
1063
1064 Activate();
1065
1068 VECTOR2I cursorPos;
1069 KIGFX::VIEW* view = getView();
1070 EDA_ITEM* item = selection.Front();
1071 SCH_COMMIT commit( m_toolMgr );
1072
1073 controls->ShowCursor( true );
1074
1075 makePointsAndBehavior( item );
1076 m_angleItem = std::make_unique<KIGFX::PREVIEW::ANGLE_ITEM>( m_editPoints.get() );
1077 view->Add( m_editPoints.get() );
1078 view->Add( m_angleItem.get() );
1079 setEditedPoint( nullptr );
1080 updateEditedPoint( aEvent );
1081 bool inDrag = false;
1082
1083 // Main loop: keep receiving events
1084 while( TOOL_EVENT* evt = Wait() )
1085 {
1086 if( grid )
1087 {
1088 grid->SetSnap( !evt->Modifier( MD_SHIFT ) );
1089 grid->SetUseGrid( getView()->GetGAL()->GetGridSnapping()
1090 && !evt->DisableGridSnapping() );
1091 }
1092 else
1093 {
1094 // This check is based on the assumption that the grid object must be valid.
1095 // If this assumption is wrong, please fix the code above.
1096 wxCHECK( false, 0 );
1097 }
1098
1099 if( !m_editPoints || evt->IsSelectionEvent() )
1100 break;
1101
1102 if ( !inDrag )
1103 updateEditedPoint( *evt );
1104
1105 if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
1106 {
1107 if( !inDrag )
1108 {
1109 commit.Modify( m_editPoints->GetParent(), m_frame->GetScreen() );
1110
1111 if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item ) )
1112 {
1113 shape->SetFlags( IS_MOVING );
1114 shape->SetHatchingDirty();
1115 shape->UpdateHatching();
1116 }
1117
1118 inDrag = true;
1119 }
1120
1121 bool snap = !evt->DisableGridSnapping();
1122
1123 cursorPos = grid->Align( controls->GetMousePosition(),
1125 controls->ForceCursorPosition( true, cursorPos );
1126
1127 m_editedPoint->SetPosition( controls->GetCursorPosition( snap ) );
1128
1129 updateParentItem( snap, commit );
1130 updatePoints();
1131 }
1132 else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
1133 {
1134 if( !commit.Empty() )
1135 commit.Push( _( "Move Point" ) );
1136
1137 controls->SetAutoPan( false );
1138
1139 if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item ) )
1140 {
1141 shape->ClearFlags( IS_MOVING );
1142 shape->SetHatchingDirty();
1143 shape->UpdateHatching();
1144 }
1145
1146 inDrag = false;
1147 }
1148 else if( evt->IsCancelInteractive() || evt->IsActivate() )
1149 {
1150 if( inDrag ) // Restore the last change
1151 {
1152 // Currently we are manually managing the lifetime of the grid
1153 // helpers because there is a bug in the tool stack that adds
1154 // the point editor again when commit.Revert() rebuilds the selection.
1155 // We remove this grid here so the its destructor is called before it
1156 // is added again.
1157 if( grid )
1158 {
1159 delete grid;
1160 grid = nullptr;
1161 }
1162
1163 commit.Revert();
1164 inDrag = false;
1165 break;
1166 }
1167 else if( evt->IsCancelInteractive() )
1168 {
1169 break;
1170 }
1171
1172 if( evt->IsActivate() )
1173 break;
1174 }
1175 else
1176 {
1177 evt->SetPassEvent();
1178 }
1179
1180 controls->SetAutoPan( inDrag );
1181 controls->CaptureCursor( inDrag );
1182 }
1183
1184 if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item ) )
1185 {
1186 shape->ClearFlags( IS_MOVING );
1187 shape->SetHatchingDirty();
1188 shape->UpdateHatching();
1189 }
1190
1191 controls->SetAutoPan( false );
1192 controls->CaptureCursor( false );
1193 setEditedPoint( nullptr );
1194
1195 if( m_editPoints )
1196 {
1197 view->Remove( m_editPoints.get() );
1198 view->Remove( m_angleItem.get() );
1199
1200 m_editPoints.reset();
1201 m_angleItem.reset();
1202 m_frame->GetCanvas()->Refresh();
1203 }
1204
1205 delete grid;
1206
1207 return 0;
1208}
1209
1210
1211void SCH_POINT_EDITOR::updateParentItem( bool aSnapToGrid, SCH_COMMIT& aCommit ) const
1212{
1213 EDA_ITEM* item = m_editPoints->GetParent();
1214
1215 if( !item )
1216 return;
1217
1218 if( !m_editBehavior )
1219 return;
1220
1221 std::vector<EDA_ITEM*> updatedItems;
1222 m_editBehavior->UpdateItem( *m_editedPoint, *m_editPoints, aCommit, updatedItems );
1223
1224 for( EDA_ITEM* updatedItem : updatedItems )
1225 updateItem( updatedItem, true );
1226
1227 m_frame->SetMsgPanel( item );
1228}
1229
1230
1232{
1233 if( !m_editPoints || !m_editBehavior )
1234 return;
1235
1236 m_editBehavior->UpdatePoints( *m_editPoints );
1237 getView()->Update( m_editPoints.get() );
1238 getView()->Update( m_angleItem.get() );
1239}
1240
1241
1243{
1245
1246 if( aPoint )
1247 {
1248 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1249 controls->ForceCursorPosition( true, aPoint->GetPosition() );
1250 controls->ShowCursor( true );
1251 }
1252 else
1253 {
1254 if( m_frame->ToolStackIsEmpty() )
1255 controls->ShowCursor( false );
1256
1257 controls->ForceCursorPosition( false );
1258 }
1259
1260 m_editedPoint = aPoint;
1261}
1262
1263
1265{
1266 bool isRuleArea = false;
1267
1268 if( m_editPoints )
1269 isRuleArea = m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T;
1270
1272 || !( m_editPoints->GetParent()->Type() == SCH_SHAPE_T || isRuleArea ) )
1273 {
1274 return false;
1275 }
1276
1277 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( m_editPoints->GetParent() );
1278
1279 if( shape->GetPolyShape().IsEmpty() )
1280 return false;
1281
1282 SHAPE_LINE_CHAIN& poly = shape->GetPolyShape().Outline( 0 );
1283
1284 if( m_editPoints->GetParent()->Type() == SCH_SHAPE_T && poly.GetPointCount() <= 2 )
1285 return false;
1286 if( m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T && poly.GetPointCount() <= 3 )
1287 return false;
1288
1289 for( const VECTOR2I& pt : poly.CPoints() )
1290 {
1291 if( pt == m_editedPoint->GetPosition() )
1292 return true;
1293 }
1294
1295 return false;
1296}
1297
1298
1300{
1301 if( !m_editPoints
1302 || !( m_editPoints->GetParent()->Type() == SCH_SHAPE_T
1303 || m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T ) )
1304 {
1305 return false;
1306 }
1307
1308 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( m_editPoints->GetParent() );
1309
1310 if( shape->GetShape() != SHAPE_T::POLY )
1311 return false;
1312
1313 VECTOR2I cursorPos = getViewControls()->GetCursorPosition( false );
1314 double threshold = getView()->ToWorld( EDIT_POINT::POINT_SIZE );
1315
1316 return shape->HitTest( cursorPos, (int) threshold );
1317}
1318
1319
1321{
1322 if( !m_editPoints
1323 || !( m_editPoints->GetParent()->Type() == SCH_SHAPE_T
1324 || m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T ) )
1325 {
1326 return 0;
1327 }
1328
1329 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( m_editPoints->GetParent() );
1330 SHAPE_LINE_CHAIN& poly = shape->GetPolyShape().Outline( 0 );
1331 SCH_COMMIT commit( m_toolMgr );
1332
1333 commit.Modify( shape, m_frame->GetScreen() );
1334
1336 int currentMinDistance = INT_MAX;
1337 int closestLineStart = 0;
1338 unsigned numPoints = poly.GetPointCount();
1339
1340 if( !shape->IsClosed() )
1341 numPoints -= 1;
1342
1343 for( unsigned i = 0; i < numPoints; ++i )
1344 {
1345 SEG seg = poly.GetSegment( i );
1346 int distance = seg.Distance( cursor );
1347
1348 if( distance < currentMinDistance )
1349 {
1350 currentMinDistance = distance;
1351 closestLineStart = i;
1352 }
1353 }
1354
1355 poly.Insert( closestLineStart + 1, cursor );
1356
1357 updateItem( shape, true );
1358 updatePoints();
1359
1360 commit.Push( _( "Add Corner" ) );
1361 return 0;
1362}
1363
1364
1366{
1368 || !m_editPoints->GetParent()->IsType( { SCH_SHAPE_T, SCH_RULE_AREA_T } ) )
1369 {
1370 return 0;
1371 }
1372
1373 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( m_editPoints->GetParent() );
1374 SHAPE_LINE_CHAIN& poly = shape->GetPolyShape().Outline( 0 );
1375 SCH_COMMIT commit( m_toolMgr );
1376
1377 if( m_editPoints->GetParent()->Type() == SCH_SHAPE_T && poly.GetPointCount() <= 2 )
1378 return 0;
1379 if( m_editPoints->GetParent()->Type() == SCH_RULE_AREA_T && poly.GetPointCount() <= 3 )
1380 return 0;
1381
1382 commit.Modify( shape, m_frame->GetScreen() );
1383
1384 int idx = getEditedPointIndex();
1385 int last = (int) poly.GetPointCount() - 1;
1386
1387 if( idx == 0 && poly.GetPoint( 0 ) == poly.GetPoint( last ) )
1388 {
1389 poly.Remove( idx );
1390 poly.SetPoint( last-1, poly.GetPoint( 0 ) );
1391 }
1392 else
1393 {
1394 poly.Remove( idx );
1395 }
1396
1397 shape->SetHatchingDirty();
1398
1399 setEditedPoint( nullptr );
1400
1401 updateItem( shape, true );
1402 updatePoints();
1403
1404 commit.Push( _( "Remove Corner" ) );
1405 return 0;
1406}
1407
1408
1410{
1411 if( aEvent.Matches( ACTIONS::cycleArcEditMode.MakeEvent() ) )
1412 {
1413 m_arcEditMode = m_frame->eeconfig()->m_Drawing.arc_edit_mode;
1415 }
1416 else
1417 {
1419 }
1420
1421 m_frame->eeconfig()->m_Drawing.arc_edit_mode = m_arcEditMode;
1422
1423 return 0;
1424}
1425
1426
1428{
1429 updatePoints();
1430 return 0;
1431}
1432
1433
ARC_EDIT_MODE
Settings for arc editing.
@ KEEP_CENTER_ADJUST_ANGLE_RADIUS
When editing endpoints, the angle and radius are adjusted.
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
constexpr double ARC_LOW_DEF_MM
Definition base_units.h:118
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
static TOOL_ACTION cycleArcEditMode
Definition actions.h:273
static TOOL_ACTION pointEditorArcKeepCenter
Definition actions.h:274
static TOOL_ACTION pointEditorArcKeepRadius
Definition actions.h:276
static TOOL_ACTION activatePointEditor
Definition actions.h:272
static TOOL_ACTION pointEditorArcKeepEndpoint
Definition actions.h:275
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
BITMAP_POINT_EDIT_BEHAVIOR(SCH_BITMAP &aBitmap)
static constexpr BOX2< VECTOR2I > ByCorners(const VECTOR2I &aCorner1, const VECTOR2I &aCorner2)
Definition box2.h:70
constexpr const Vec GetCenter() const
Definition box2.h:230
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
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition commit.h:72
bool Empty() const
Definition commit.h:137
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:106
EDIT_CONSTRAINT for a EDIT_LINE, that constrains the line to move perpendicular to the line itself.
The base class for create windows for drawing purpose.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:192
bool IsNew() const
Definition eda_item.h:124
void SetStartX(int x)
Definition eda_shape.h:191
void SetEndY(int aY)
Definition eda_shape.h:226
void SetStartY(int y)
Definition eda_shape.h:184
SHAPE_POLY_SET & GetPolyShape()
Definition eda_shape.h:337
SHAPE_T GetShape() const
Definition eda_shape.h:168
void SetEndX(int aX)
Definition eda_shape.h:233
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:215
bool IsClosed() const
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:219
wxString SHAPE_T_asString() const
void SetHatchingDirty()
Definition eda_shape.h:147
int GetCornerRadius() const
void SetConstraint(EDIT_CONSTRAINT< EDIT_LINE > *aConstraint)
Set a constraint for and EDIT_POINT.
EDIT_POINTS is a VIEW_ITEM that manages EDIT_POINTs and EDIT_LINEs and draws them.
void AddPoint(const EDIT_POINT &aPoint)
Add an EDIT_POINT.
EDIT_LINE & Line(unsigned int aIndex)
unsigned int LinesSize() const
Return number of stored EDIT_LINEs.
EDIT_POINT & Point(unsigned int aIndex)
void AddLine(const EDIT_LINE &aLine)
Adds an EDIT_LINE.
Represent a single point that can be used for modifying items.
Definition edit_points.h:48
virtual std::pair< EDA_ITEM *, int > GetConnected() const
Return a connected item record comprising an EDA_ITEM* and a STARTPOINT/ENDPOINT flag.
Definition edit_points.h:80
int GetY() const
Return Y coordinate of an EDIT_POINT.
Definition edit_points.h:96
static const int POINT_SIZE
Single point size in pixels.
virtual void SetPosition(const VECTOR2I &aPosition)
Set new coordinates for an EDIT_POINT.
virtual VECTOR2I GetPosition() const
Return coordinates of an EDIT_POINT.
Definition edit_points.h:72
int GetX() const
Return X coordinate of an EDIT_POINT.
Definition edit_points.h:88
void SetDrawCircle(bool aDrawCircle=true)
static const TOOL_EVENT ClearedEvent
Definition actions.h:348
static const TOOL_EVENT SelectedEvent
Definition actions.h:346
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:353
static const TOOL_EVENT PointSelectedEvent
Definition actions.h:345
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:66
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:298
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:341
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:1685
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:467
Define a library symbol object.
Definition lib_symbol.h:87
std::vector< SCH_PIN * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
void UpdateItem(const EDIT_POINT &aEditedPoints, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
LINE_POINT_EDIT_BEHAVIOR(SCH_LINE &aLine, SCH_SCREEN &aScreen)
A helper class interface to manage the edit points for a single item.
static bool isModified(const EDIT_POINT &aEditedPoint, const EDIT_POINT &aPoint)
Checks if two points are the same instance - which means the point is being edited.
static void UpdateItem(SCH_SHAPE &aRect, const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints)
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
static void UpdatePoints(SCH_SHAPE &aRect, EDIT_POINTS &aPoints)
static void PinEditedCorner(const EDIT_POINT &aEditedPoint, const EDIT_POINTS &aPoints, int minWidth, int minHeight, VECTOR2I &topLeft, VECTOR2I &topRight, VECTOR2I &botLeft, VECTOR2I &botRight)
Update the coordinates of 4 corners of a rectangle, according to constraints and the moved corner.
static void MakePoints(SCH_SHAPE &aRect, EDIT_POINTS &aPoints)
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
RECTANGLE_POINT_EDIT_BEHAVIOR(SCH_SHAPE &aRect, EDA_DRAW_FRAME &aFrame)
void dragPinsOnEdge(const std::vector< SEG > &aOldEdges, const std::vector< VECTOR2I > &aMoveVecs, int aEdgeUnit, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) 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.
Holds all the data relating to one schematic.
Definition schematic.h:88
static TOOL_ACTION pointEditorAddCorner
static TOOL_ACTION pointEditorRemoveCorner
A shim class between EDA_DRAW_FRAME and several derived classes: SYMBOL_EDIT_FRAME,...
Object to handle a bitmap image that can be inserted in a schematic.
Definition sch_bitmap.h:40
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
virtual void Revert() override
Revert the commit by restoring the modified items state.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:244
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
VECTOR2I GetEndPoint() const
Definition sch_line.h:148
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
void updatePoints()
Update which point is being edited.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
void setEditedPoint(EDIT_POINT *aPoint)
Return true if aPoint is the currently modified point.
bool Init() override
Init() is called once upon a registration of the tool.
ARC_EDIT_MODE m_arcEditMode
Re-entrancy guards.
void updateParentItem(bool aSnapToGrid, SCH_COMMIT &aCommit) const
< Update item's points with edit points.
void updateEditedPoint(const TOOL_EVENT &aEvent)
Clear references to the points.
bool addCornerCondition(const SELECTION &aSelection)
std::unique_ptr< POINT_EDIT_BEHAVIOR > m_editBehavior
EDIT_POINT * m_editedPoint
int modifiedSelection(const TOOL_EVENT &aEvent)
int Main(const TOOL_EVENT &aEvent)
int getEditedPointIndex() const
std::shared_ptr< EDIT_POINTS > m_editPoints
int clearEditedPoints(const TOOL_EVENT &aEvent)
Set the current point being edited. NULL means none.
bool removeCornerCondition(const SELECTION &aSelection)
bool m_inPointEditor
Currently available edit points.
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
void makePointsAndBehavior(EDA_ITEM *aItem)
Currently edited point, NULL if there is none.
std::unique_ptr< KIGFX::PREVIEW::ANGLE_ITEM > m_angleItem
Current item-specific edit behavior.
int changeArcEditMode(const TOOL_EVENT &aEvent)
int removeCorner(const TOOL_EVENT &aEvent)
int addCorner(const TOOL_EVENT &aEvent)
TOOL_ACTION handlers.
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:86
VECTOR2I GetCenter() const
Definition sch_shape.h:88
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
VECTOR2I GetPosition() const override
Definition sch_shape.h:85
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:47
SCH_TABLECELL_POINT_EDIT_BEHAVIOR(SCH_TABLECELL &aCell, SCH_SCREEN &aScreen)
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
void updateItem(EDA_ITEM *aItem, bool aUpdateRTree) const
bool Init() override
Init() is called once upon a registration of the tool.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
SCH_TOOL_BASE(const std::string &aName)
SCH_SELECTION_TOOL * m_selectionTool
Definition seg.h:42
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:673
Class that groups generic conditions for selected items.
static SELECTION_CONDITION Count(int aNumber)
Create a functor that tests if the number of selected items is equal to the value given as parameter.
EDA_ITEM * Front() const
Definition selection.h:177
int Size() const
Returns the number of selected parts.
Definition selection.h:121
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
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHEET_POINT_EDIT_BEHAVIOR(SCH_SHEET &aSheet)
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
The symbol library editor main window.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
TEXTBOX_POINT_EDIT_BEHAVIOR(SCH_TEXTBOX &aTextbox)
SCH_BASE_FRAME * getEditFrame() const
Definition tool_base.h:186
KIGFX::VIEW_CONTROLS * getViewControls() const
Definition tool_base.cpp:44
KIGFX::VIEW * getView() const
Definition tool_base.cpp:38
Generic, UI-independent tool event.
Definition tool_event.h:171
bool DisableGridSnapping() const
Definition tool_event.h:371
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition tool_event.h:392
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:293
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition tool_event.h:315
const VECTOR2D DragOrigin() const
Return the point where dragging has started.
Definition tool_event.h:299
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:473
bool IsMotion() const
Definition tool_event.h:330
void Go(int(SCH_BASE_FRAME::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:283
@ ARROW
Definition cursors.h:46
#define _(s)
#define ENDPOINT
ends. (Used to support dragging.)
#define IS_MOVING
Item being moved.
#define STARTPOINT
When a line is selected, these flags indicate which.
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
@ FRAME_SCH_SYMBOL_EDITOR
Definition frame_type.h:35
@ GRID_GRAPHICS
Definition grid_helper.h:52
@ LAYER_NOTES
Definition layer_ids.h:467
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:96
constexpr int Mils2IU(const EDA_IU_SCALE &aIuScale, int mils)
Definition eda_units.h:166
std::vector< SEG > GetSegsInDirection(const BOX2I &aBox, DIRECTION_45::Directions aDir)
Get the segments of a box that are in the given direction.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ RECT_RIGHT
@ RECT_LEFT
@ RECT_CENTER
@ RECT_RADIUS
ARC_EDIT_MODE IncrementArcEditMode(ARC_EDIT_MODE aMode)
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
SCH_CONDITIONS S_C
RECTANGLE_POINTS
@ RECT_BOTLEFT
@ RECT_TOPLEFT
@ RECT_TOPRIGHT
@ RECT_CENTER
@ RECT_BOTRIGHT
@ RECT_RADIUS
RECTANGLE_LINES
@ RECT_RIGHT
@ RECT_LEFT
@ RECT_BOT
@ RECT_TOP
TABLECELL_POINTS
@ ROW_HEIGHT
@ COL_WIDTH
static const std::vector< KICAD_T > pointEditorTypes
@ LINE_START
@ LINE_END
REFIMAGE_POINTS
@ REFIMG_ORIGIN
@ ARC_START
@ ARC_END
@ ARC_CENTER
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:39
Utility functions for working with shapes.
@ MD_SHIFT
Definition tool_event.h:143
@ BUT_LEFT
Definition tool_event.h:132
@ SCH_LINE_T
Definition typeinfo.h:167
@ SCH_TABLECELL_T
Definition typeinfo.h:170
@ SCH_SHEET_T
Definition typeinfo.h:179
@ SCH_SHAPE_T
Definition typeinfo.h:153
@ SCH_RULE_AREA_T
Definition typeinfo.h:174
@ SCH_ITEM_LOCATE_GRAPHIC_LINE_T
Definition typeinfo.h:192
@ SCH_BITMAP_T
Definition typeinfo.h:168
@ SCH_TEXTBOX_T
Definition typeinfo.h:156
constexpr int sign(T val)
Definition util.h:145
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695