KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_point_editor.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2013-2021 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <functional>
27#include <memory>
28#include <algorithm>
29
30using namespace std::placeholders;
31#include <advanced_config.h>
32#include <kiplatform/ui.h>
33#include <view/view_controls.h>
37#include <geometry/seg.h>
39#include <math/util.h>
40#include <confirm.h>
41#include <tool/tool_manager.h>
45#include <tools/pcb_actions.h>
51#include <board_commit.h>
52#include <pcb_edit_frame.h>
53#include <pcb_reference_image.h>
54#include <pcb_generator.h>
55#include <pcb_group.h>
56#include <pcb_dimension.h>
57#include <pcb_textbox.h>
58#include <pcb_tablecell.h>
59#include <pcb_table.h>
60#include <pad.h>
61#include <zone.h>
62#include <footprint.h>
65#include <progress_reporter.h>
66#include <layer_ids.h>
68
69const unsigned int PCB_POINT_EDITOR::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
70
71// Few constants to avoid using bare numbers for point indices
83
84
89
90
105
106
113
114
116{
117public:
118 RECT_RADIUS_TEXT_ITEM( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits ) :
120 m_iuScale( aIuScale ),
121 m_units( aUnits ),
122 m_radius( 0 ),
123 m_corner(),
124 m_quadrant( -1, 1 ),
125 m_visible( false )
126 {
127 }
128
129 const BOX2I ViewBBox() const override
130 {
131 BOX2I tmp;
132 tmp.SetMaximum();
133 return tmp;
134 }
135
136 std::vector<int> ViewGetLayers() const override
137 {
139 }
140
141 void ViewDraw( int aLayer, KIGFX::VIEW* aView ) const override
142 {
143 if( !m_visible )
144 return;
145
146 wxArrayString strings;
147 strings.push_back( KIGFX::PREVIEW::DimensionLabel( "r", m_radius, m_iuScale, m_units ) );
149 aLayer == LAYER_SELECT_OVERLAY );
150 }
151
152 void Set( int aRadius, const VECTOR2I& aCorner, const VECTOR2I& aQuadrant, EDA_UNITS aUnits )
153 {
154 m_radius = aRadius;
155 m_corner = aCorner;
156 m_quadrant = aQuadrant;
157 m_units = aUnits;
158 m_visible = true;
159 }
160
161 void Hide()
162 {
163 m_visible = false;
164 }
165
166 wxString GetClass() const override
167 {
168 return wxT( "RECT_RADIUS_TEXT_ITEM" );
169 }
170
171private:
178};
179
180
182{
183public:
185 m_rectangle( aRectangle )
186 {
187 wxASSERT( m_rectangle.GetShape() == SHAPE_T::RECTANGLE );
188 }
189
194 static void MakePoints( const PCB_SHAPE& aRectangle, EDIT_POINTS& aPoints )
195 {
196 wxCHECK( aRectangle.GetShape() == SHAPE_T::RECTANGLE, /* void */ );
197
198 VECTOR2I topLeft = aRectangle.GetTopLeft();
199 VECTOR2I botRight = aRectangle.GetBotRight();
200
201 aPoints.SetSwapX( topLeft.x > botRight.x );
202 aPoints.SetSwapY( topLeft.y > botRight.y );
203
204 if( aPoints.SwapX() )
205 std::swap( topLeft.x, botRight.x );
206
207 if( aPoints.SwapY() )
208 std::swap( topLeft.y, botRight.y );
209
210 aPoints.AddPoint( topLeft );
211 aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
212 aPoints.AddPoint( botRight );
213 aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
214 aPoints.AddPoint( aRectangle.GetCenter() );
215 aPoints.AddPoint( VECTOR2I( botRight.x - aRectangle.GetCornerRadius(), topLeft.y ) );
216 aPoints.Point( RECT_RADIUS ).SetDrawCircle();
217
218 aPoints.AddLine( aPoints.Point( RECT_TOP_LEFT ), aPoints.Point( RECT_TOP_RIGHT ) );
219 aPoints.Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_TOP ) ) );
220 aPoints.AddLine( aPoints.Point( RECT_TOP_RIGHT ), aPoints.Point( RECT_BOT_RIGHT ) );
221 aPoints.Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_RIGHT ) ) );
222 aPoints.AddLine( aPoints.Point( RECT_BOT_RIGHT ), aPoints.Point( RECT_BOT_LEFT ) );
223 aPoints.Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_BOT ) ) );
224 aPoints.AddLine( aPoints.Point( RECT_BOT_LEFT ), aPoints.Point( RECT_TOP_LEFT ) );
225 aPoints.Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_LEFT ) ) );
226 }
227
228 static void UpdateItem( PCB_SHAPE& aRectangle, const EDIT_POINT& aEditedPoint,
229 EDIT_POINTS& aPoints )
230 {
231 // You can have more points if your item wants to have more points
232 // (this class assumes the rect points come first, but that can be changed)
234
235 auto setLeft =
236 [&]( int left )
237 {
238 aPoints.SwapX() ? aRectangle.SetRight( left ) : aRectangle.SetLeft( left );
239 };
240 auto setRight =
241 [&]( int right )
242 {
243 aPoints.SwapX() ? aRectangle.SetLeft( right ) : aRectangle.SetRight( right );
244 };
245 auto setTop =
246 [&]( int top )
247 {
248 aPoints.SwapY() ? aRectangle.SetBottom( top ) : aRectangle.SetTop( top );
249 };
250 auto setBottom =
251 [&]( int bottom )
252 {
253 aPoints.SwapY() ? aRectangle.SetTop( bottom ) : aRectangle.SetBottom( bottom );
254 };
255
256 VECTOR2I topLeft = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
257 VECTOR2I topRight = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
258 VECTOR2I botLeft = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
259 VECTOR2I botRight = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
260
261 PinEditedCorner( aEditedPoint, aPoints, topLeft, topRight, botLeft, botRight );
262
263 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) )
264 || isModified( aEditedPoint, aPoints.Point( RECT_TOP_RIGHT ) )
265 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) )
266 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_LEFT ) ) )
267 {
268 setTop( topLeft.y );
269 setLeft( topLeft.x );
270 setRight( botRight.x );
271 setBottom( botRight.y );
272 }
273 else if( isModified( aEditedPoint, aPoints.Point( RECT_CENTER ) ) )
274 {
275 const VECTOR2I moveVector =
276 aPoints.Point( RECT_CENTER ).GetPosition() - aRectangle.GetCenter();
277 aRectangle.Move( moveVector );
278 }
279 else if( isModified( aEditedPoint, aPoints.Point( RECT_RADIUS ) ) )
280 {
281 int width = std::abs( botRight.x - topLeft.x );
282 int height = std::abs( botRight.y - topLeft.y );
283 int maxRadius = std::min( width, height ) / 2;
284 int x = aPoints.Point( RECT_RADIUS ).GetX();
285 x = std::clamp( x, botRight.x - maxRadius, botRight.x );
286 aPoints.Point( RECT_RADIUS ).SetPosition( x, topLeft.y );
287 aRectangle.SetCornerRadius( botRight.x - x );
288 }
289 else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
290 {
291 setTop( topLeft.y );
292 }
293 else if( isModified( aEditedPoint, aPoints.Line( RECT_LEFT ) ) )
294 {
295 setLeft( topLeft.x );
296 }
297 else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
298 {
299 setBottom( botRight.y );
300 }
301 else if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
302 {
303 setRight( botRight.x );
304 }
305
306 for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
307 {
308 if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
309 {
310 aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
311 }
312 }
313 }
314
315 static void UpdatePoints( PCB_SHAPE& aRectangle, EDIT_POINTS& aPoints )
316 {
317 wxCHECK( aPoints.PointsSize() >= RECT_MAX_POINTS, /* void */ );
318
319 VECTOR2I topLeft = aRectangle.GetTopLeft();
320 VECTOR2I botRight = aRectangle.GetBotRight();
321
322 aPoints.SetSwapX( topLeft.x > botRight.x );
323 aPoints.SetSwapY( topLeft.y > botRight.y );
324
325 if( aPoints.SwapX() )
326 std::swap( topLeft.x, botRight.x );
327
328 if( aPoints.SwapY() )
329 std::swap( topLeft.y, botRight.y );
330
331 aPoints.Point( RECT_TOP_LEFT ).SetPosition( topLeft );
332 aPoints.Point( RECT_RADIUS ).SetPosition( botRight.x - aRectangle.GetCornerRadius(), topLeft.y );
333 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
334 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( botRight );
335 aPoints.Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
336 aPoints.Point( RECT_CENTER ).SetPosition( aRectangle.GetCenter() );
337 }
338
339 void MakePoints( EDIT_POINTS& aPoints ) override
340 {
341 // Just call the static helper
342 MakePoints( m_rectangle, aPoints );
343 }
344
345 void UpdatePoints( EDIT_POINTS& aPoints ) override
346 {
347 UpdatePoints( m_rectangle, aPoints );
348 }
349
350 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
351 std::vector<EDA_ITEM*>& aUpdatedItems ) override
352 {
353 UpdateItem( m_rectangle, aEditedPoint, aPoints );
354 }
355
369 static void PinEditedCorner( const EDIT_POINT& aEditedPoint, const EDIT_POINTS& aEditPoints,
370 VECTOR2I& aTopLeft, VECTOR2I& aTopRight, VECTOR2I& aBotLeft,
371 VECTOR2I& aBotRight, const VECTOR2I& aHole = { 0, 0 },
372 const VECTOR2I& aHoleSize = { 0, 0 } )
373 {
374 int minWidth = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
375 int minHeight = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
376
377 if( isModified( aEditedPoint, aEditPoints.Point( RECT_TOP_LEFT ) ) )
378 {
379 if( aHoleSize.x )
380 {
381 // pin edited point to the top/left of the hole
382 aTopLeft.x = std::min( aTopLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
383 aTopLeft.y = std::min( aTopLeft.y, aHole.y - aHoleSize.y / 2 - minHeight );
384 }
385 else
386 {
387 // pin edited point within opposite corner
388 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
389 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
390 }
391
392 // push edited point edges to adjacent corners
393 aTopRight.y = aTopLeft.y;
394 aBotLeft.x = aTopLeft.x;
395 }
396 else if( isModified( aEditedPoint, aEditPoints.Point( RECT_TOP_RIGHT ) ) )
397 {
398 if( aHoleSize.x )
399 {
400 // pin edited point to the top/right of the hole
401 aTopRight.x = std::max( aTopRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
402 aTopRight.y = std::min( aTopRight.y, aHole.y - aHoleSize.y / 2 - minHeight );
403 }
404 else
405 {
406 // pin edited point within opposite corner
407 aTopRight.x = std::max( aTopRight.x, aBotLeft.x + minWidth );
408 aTopRight.y = std::min( aTopRight.y, aBotLeft.y - minHeight );
409 }
410
411 // push edited point edges to adjacent corners
412 aTopLeft.y = aTopRight.y;
413 aBotRight.x = aTopRight.x;
414 }
415 else if( isModified( aEditedPoint, aEditPoints.Point( RECT_BOT_LEFT ) ) )
416 {
417 if( aHoleSize.x )
418 {
419 // pin edited point to the bottom/left of the hole
420 aBotLeft.x = std::min( aBotLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
421 aBotLeft.y = std::max( aBotLeft.y, aHole.y + aHoleSize.y / 2 + minHeight );
422 }
423 else
424 {
425 // pin edited point within opposite corner
426 aBotLeft.x = std::min( aBotLeft.x, aTopRight.x - minWidth );
427 aBotLeft.y = std::max( aBotLeft.y, aTopRight.y + minHeight );
428 }
429
430 // push edited point edges to adjacent corners
431 aBotRight.y = aBotLeft.y;
432 aTopLeft.x = aBotLeft.x;
433 }
434 else if( isModified( aEditedPoint, aEditPoints.Point( RECT_BOT_RIGHT ) ) )
435 {
436 if( aHoleSize.x )
437 {
438 // pin edited point to the bottom/right of the hole
439 aBotRight.x = std::max( aBotRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
440 aBotRight.y = std::max( aBotRight.y, aHole.y + aHoleSize.y / 2 + minHeight );
441 }
442 else
443 {
444 // pin edited point within opposite corner
445 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
446 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
447 }
448
449 // push edited point edges to adjacent corners
450 aBotLeft.y = aBotRight.y;
451 aTopRight.x = aBotRight.x;
452 }
453 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_TOP ) ) )
454 {
455 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
456 }
457 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_LEFT ) ) )
458 {
459 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
460 }
461 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_BOT ) ) )
462 {
463 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
464 }
465 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_RIGHT ) ) )
466 {
467 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
468 }
469 }
470
471private:
473};
474
475
477{
478public:
480 POLYGON_POINT_EDIT_BEHAVIOR( *aZone.Outline() ),
481 m_zone( aZone )
482 {
483 }
484
485 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
486 std::vector<EDA_ITEM*>& aUpdatedItems ) override
487 {
488 m_zone.UnFill();
489
490 // Defer to the base class to update the polygon
491 POLYGON_POINT_EDIT_BEHAVIOR::UpdateItem( aEditedPoint, aPoints, aCommit, aUpdatedItems );
492
493 m_zone.HatchBorder();
494 }
495
496private:
498};
499
500
502{
504 {
505 REFIMG_ORIGIN = RECT_CENTER, // Reuse the center point fo rthe transform origin
506
508 };
509
510public:
512 m_refImage( aRefImage )
513 {
514 }
515
516 void MakePoints( EDIT_POINTS& aPoints ) override
517 {
518 REFERENCE_IMAGE& refImage = m_refImage.GetReferenceImage();
519
520 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
521 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
522
523 aPoints.AddPoint( topLeft );
524 aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
525 aPoints.AddPoint( botRight );
526 aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
527
528 aPoints.AddPoint( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
529 }
530
531 void UpdatePoints( EDIT_POINTS& aPoints ) override
532 {
534
535 REFERENCE_IMAGE& refImage = m_refImage.GetReferenceImage();
536
537 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
538 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
539
540 aPoints.Point( RECT_TOP_LEFT ).SetPosition( topLeft );
541 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
542 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( botRight );
543 aPoints.Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
544
545 aPoints.Point( REFIMG_ORIGIN ).SetPosition( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
546 }
547
548 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
549 std::vector<EDA_ITEM*>& aUpdatedItems ) override
550 {
552
553 REFERENCE_IMAGE& refImage = m_refImage.GetReferenceImage();
554
555 const VECTOR2I topLeft = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
556 const VECTOR2I topRight = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
557 const VECTOR2I botRight = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
558 const VECTOR2I botLeft = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
559 const VECTOR2I xfrmOrigin = aPoints.Point( REFIMG_ORIGIN ).GetPosition();
560
561 if( isModified( aEditedPoint, aPoints.Point( REFIMG_ORIGIN ) ) )
562 {
563 // Moving the transform origin
564 // As the other points didn't move, we can get the image extent from them
565 const VECTOR2I newOffset = xfrmOrigin - ( topLeft + botRight ) / 2;
566 refImage.SetTransformOriginOffset( newOffset );
567 }
568 else
569 {
570 const VECTOR2I oldOrigin = m_refImage.GetPosition() + refImage.GetTransformOriginOffset();
571 const VECTOR2I oldSize = refImage.GetSize();
572 const VECTOR2I pos = refImage.GetPosition();
573
574 OPT_VECTOR2I newCorner;
575 VECTOR2I oldCorner = pos;
576
577 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) ) )
578 {
579 newCorner = topLeft;
580 oldCorner -= oldSize / 2;
581 }
582 else if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_RIGHT ) ) )
583 {
584 newCorner = topRight;
585 oldCorner -= VECTOR2I( -oldSize.x, oldSize.y ) / 2;
586 }
587 else if( isModified( aEditedPoint, aPoints.Point( RECT_BOT_LEFT ) ) )
588 {
589 newCorner = botLeft;
590 oldCorner -= VECTOR2I( oldSize.x, -oldSize.y ) / 2;
591 }
592 else if( isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) ) )
593 {
594 newCorner = botRight;
595 oldCorner += oldSize / 2;
596 }
597
598 if( newCorner )
599 {
600 // Turn in the respective vectors from the origin
601 *newCorner -= xfrmOrigin;
602 oldCorner -= oldOrigin;
603
604 // If we tried to cross the origin, clamp it to stop it
605 if( sign( newCorner->x ) != sign( oldCorner.x )
606 || sign( newCorner->y ) != sign( oldCorner.y ) )
607 {
608 *newCorner = VECTOR2I( 0, 0 );
609 }
610
611 const double newLength = newCorner->EuclideanNorm();
612 const double oldLength = oldCorner.EuclideanNorm();
613
614 double ratio = oldLength > 0 ? ( newLength / oldLength ) : 1.0;
615
616 // Clamp the scaling to a minimum of 50 mils
617 VECTOR2I newSize = oldSize * ratio;
618 double newWidth = std::max( newSize.x, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
619 double newHeight = std::max( newSize.y, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
620 ratio = std::min( newWidth / oldSize.x, newHeight / oldSize.y );
621
622 // Also handles the origin offset
623 refImage.SetImageScale( refImage.GetImageScale() * ratio );
624 }
625 }
626 }
627
628private:
630};
631
632
634{
635public:
641
642 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
643 std::vector<EDA_ITEM*>& aUpdatedItems ) override
644 {
646
647 PCB_TABLE& table = static_cast<PCB_TABLE&>( *m_cell.GetParent() );
648 aCommit.Modify( &table );
649 aUpdatedItems.push_back( &table );
650
651 if( !m_cell.GetTextAngle().IsHorizontal() )
652 {
653 if( isModified( aEditedPoint, aPoints.Point( ROW_HEIGHT ) ) )
654 {
655 m_cell.SetEnd( VECTOR2I( m_cell.GetEndX(), aPoints.Point( ROW_HEIGHT ).GetY() ) );
656
657 int colWidth = std::abs( m_cell.GetRectangleHeight() );
658
659 for( int ii = 0; ii < m_cell.GetColSpan() - 1; ++ii )
660 colWidth -= table.GetColWidth( m_cell.GetColumn() + ii );
661
662 table.SetColWidth( m_cell.GetColumn() + m_cell.GetColSpan() - 1, colWidth );
663 }
664 else if( isModified( aEditedPoint, aPoints.Point( COL_WIDTH ) ) )
665 {
666 m_cell.SetEnd( VECTOR2I( aPoints.Point( COL_WIDTH ).GetX(), m_cell.GetEndY() ) );
667
668 int rowHeight = m_cell.GetRectangleWidth();
669
670 for( int ii = 0; ii < m_cell.GetRowSpan() - 1; ++ii )
671 rowHeight -= table.GetRowHeight( m_cell.GetRow() + ii );
672
673 table.SetRowHeight( m_cell.GetRow() + m_cell.GetRowSpan() - 1, rowHeight );
674 }
675 }
676 else
677 {
678 if( isModified( aEditedPoint, aPoints.Point( COL_WIDTH ) ) )
679 {
680 m_cell.SetEnd( VECTOR2I( aPoints.Point( COL_WIDTH ).GetX(), m_cell.GetEndY() ) );
681
682 int colWidth = m_cell.GetRectangleWidth();
683
684 for( int ii = 0; ii < m_cell.GetColSpan() - 1; ++ii )
685 colWidth -= table.GetColWidth( m_cell.GetColumn() + ii );
686
687 table.SetColWidth( m_cell.GetColumn() + m_cell.GetColSpan() - 1, colWidth );
688 }
689 else if( isModified( aEditedPoint, aPoints.Point( ROW_HEIGHT ) ) )
690 {
691 m_cell.SetEnd( VECTOR2I( m_cell.GetEndX(), aPoints.Point( ROW_HEIGHT ).GetY() ) );
692
693 int rowHeight = m_cell.GetRectangleHeight();
694
695 for( int ii = 0; ii < m_cell.GetRowSpan() - 1; ++ii )
696 rowHeight -= table.GetRowHeight( m_cell.GetRow() + ii );
697
698 table.SetRowHeight( m_cell.GetRow() + m_cell.GetRowSpan() - 1, rowHeight );
699 }
700 }
701
702 table.Normalize();
703 }
704
705private:
707};
708
709
711{
712public:
714 m_pad( aPad ),
715 m_layer( aLayer )
716 {}
717
718 void MakePoints( EDIT_POINTS& aPoints ) override
719 {
720 VECTOR2I shapePos = m_pad.ShapePos( m_layer );
721 VECTOR2I halfSize( m_pad.GetSize( m_layer ).x / 2, m_pad.GetSize( m_layer ).y / 2 );
722
723 if( m_pad.IsLocked() )
724 return;
725
726 switch( m_pad.GetShape( m_layer ) )
727 {
729 aPoints.AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y ) );
730 break;
731
732 case PAD_SHAPE::OVAL:
737 {
738 if( !m_pad.GetOrientation().IsCardinal() )
739 break;
740
741 if( m_pad.GetOrientation() == ANGLE_90 || m_pad.GetOrientation() == ANGLE_270 )
742 std::swap( halfSize.x, halfSize.y );
743
744 // It's important to fill these according to the RECT indices
745 aPoints.AddPoint( shapePos - halfSize );
746 aPoints.AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
747 aPoints.AddPoint( shapePos + halfSize );
748 aPoints.AddPoint( VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
749 }
750 break;
751
752 default: // suppress warnings
753 break;
754 }
755 }
756
757 void UpdatePoints( EDIT_POINTS& aPoints ) override
758 {
759 bool locked = m_pad.GetParent() && m_pad.IsLocked();
760 VECTOR2I shapePos = m_pad.ShapePos( m_layer );
761 VECTOR2I halfSize( m_pad.GetSize( m_layer ).x / 2, m_pad.GetSize( m_layer ).y / 2 );
762
763 switch( m_pad.GetShape( m_layer ) )
764 {
766 {
767 int target = locked ? 0 : 1;
768
769 // Careful; pad shape is mutable...
770 if( int( aPoints.PointsSize() ) != target )
771 {
772 aPoints.Clear();
773 MakePoints( aPoints );
774 }
775 else if( target == 1 )
776 {
777 shapePos.x += halfSize.x;
778 aPoints.Point( 0 ).SetPosition( shapePos );
779 }
780 }
781 break;
782
783 case PAD_SHAPE::OVAL:
788 {
789 // Careful; pad shape and orientation are mutable...
790 int target = locked || !m_pad.GetOrientation().IsCardinal() ? 0 : 4;
791
792 if( int( aPoints.PointsSize() ) != target )
793 {
794 aPoints.Clear();
795 MakePoints( aPoints );
796 }
797 else if( target == 4 )
798 {
799 if( m_pad.GetOrientation() == ANGLE_90 || m_pad.GetOrientation() == ANGLE_270 )
800 std::swap( halfSize.x, halfSize.y );
801
802 aPoints.Point( RECT_TOP_LEFT ).SetPosition( shapePos - halfSize );
803 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( VECTOR2I( shapePos.x + halfSize.x,
804 shapePos.y - halfSize.y ) );
805 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( shapePos + halfSize );
806 aPoints.Point( RECT_BOT_LEFT ).SetPosition( VECTOR2I( shapePos.x - halfSize.x,
807 shapePos.y + halfSize.y ) );
808 }
809
810 break;
811 }
812
813 default: // suppress warnings
814 break;
815 }
816 }
817
818 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
819 std::vector<EDA_ITEM*>& aUpdatedItems ) override
820 {
821 switch( m_pad.GetShape( m_layer ) )
822 {
824 {
825 VECTOR2I end = aPoints.Point( 0 ).GetPosition();
826 int diameter = 2 * ( end - m_pad.GetPosition() ).EuclideanNorm();
827
828 m_pad.SetSize( m_layer, VECTOR2I( diameter, diameter ) );
829 break;
830 }
831
832 case PAD_SHAPE::OVAL:
837 {
838 VECTOR2I topLeft = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
839 VECTOR2I topRight = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
840 VECTOR2I botLeft = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
841 VECTOR2I botRight = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
842 VECTOR2I holeCenter = m_pad.GetPosition();
843 VECTOR2I holeSize = m_pad.GetDrillSize();
844
845 RECTANGLE_POINT_EDIT_BEHAVIOR::PinEditedCorner( aEditedPoint, aPoints, topLeft, topRight,
846 botLeft, botRight, holeCenter, holeSize );
847
848 if( ( m_pad.GetOffset( m_layer ).x || m_pad.GetOffset( m_layer ).y )
849 || ( m_pad.GetDrillSize().x && m_pad.GetDrillSize().y ) )
850 {
851 // Keep hole pinned at the current location; adjust the pad around the hole
852
853 VECTOR2I center = m_pad.GetPosition();
854 int dist[4];
855
856 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) )
857 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) ) )
858 {
859 dist[0] = center.x - topLeft.x;
860 dist[1] = center.y - topLeft.y;
861 dist[2] = botRight.x - center.x;
862 dist[3] = botRight.y - center.y;
863 }
864 else
865 {
866 dist[0] = center.x - botLeft.x;
867 dist[1] = center.y - topRight.y;
868 dist[2] = topRight.x - center.x;
869 dist[3] = botLeft.y - center.y;
870 }
871
872 VECTOR2I padSize( dist[0] + dist[2], dist[1] + dist[3] );
873 VECTOR2I deltaOffset( padSize.x / 2 - dist[2], padSize.y / 2 - dist[3] );
874
875 if( m_pad.GetOrientation() == ANGLE_90 || m_pad.GetOrientation() == ANGLE_270 )
876 std::swap( padSize.x, padSize.y );
877
878 RotatePoint( deltaOffset, -m_pad.GetOrientation() );
879
880 m_pad.SetSize( m_layer, padSize );
881 m_pad.SetOffset( m_layer, -deltaOffset );
882 }
883 else
884 {
885 // Keep pad position at the center of the pad shape
886
887 int left, top, right, bottom;
888
889 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) )
890 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) ) )
891 {
892 left = topLeft.x;
893 top = topLeft.y;
894 right = botRight.x;
895 bottom = botRight.y;
896 }
897 else
898 {
899 left = botLeft.x;
900 top = topRight.y;
901 right = topRight.x;
902 bottom = botLeft.y;
903 }
904
905 VECTOR2I padSize( abs( right - left ), abs( bottom - top ) );
906
907 if( m_pad.GetOrientation() == ANGLE_90 || m_pad.GetOrientation() == ANGLE_270 )
908 std::swap( padSize.x, padSize.y );
909
910 m_pad.SetSize( m_layer, padSize );
911 m_pad.SetPosition( VECTOR2I( ( left + right ) / 2, ( top + bottom ) / 2 ) );
912 }
913 break;
914 }
915 default: // suppress warnings
916 break;
917 }
918 }
919
920private:
923};
924
925
932{
933public:
935 m_generator( aGenerator )
936 {}
937
938 void MakePoints( EDIT_POINTS& aPoints ) override
939 {
940 m_generator.MakeEditPoints( aPoints );
941 }
942
943 void UpdatePoints( EDIT_POINTS& aPoints ) override
944 {
945 m_generator.UpdateEditPoints( aPoints );
946 }
947
948 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
949 std::vector<EDA_ITEM*>& aUpdatedItems ) override
950 {
951 m_generator.UpdateFromEditPoints( aPoints );
952 }
953
954private:
956};
957
958
968{
969public:
971 m_dimension( aDimension ),
972 m_originalTextPos( aDimension.GetTextPos() ),
973 m_oldCrossBar( SEG{ aDimension.GetCrossbarStart(), aDimension.GetCrossbarEnd() } )
974 {
975 }
976
978 {
979 const SEG newCrossBar{ m_dimension.GetCrossbarStart(), m_dimension.GetCrossbarEnd() };
980
981 if( newCrossBar == m_oldCrossBar )
982 {
983 // Crossbar didn't change, text doesn't need to change
984 return;
985 }
986
987 const VECTOR2I newTextPos = getDimensionNewTextPosition();
988 m_dimension.SetTextPos( newTextPos );
989
990 const GR_TEXT_H_ALIGN_T oldJustify = m_dimension.GetHorizJustify();
991
992 // We may need to update the justification if we go past vertical.
995 {
996 const VECTOR2I oldProject = m_oldCrossBar.LineProject( m_originalTextPos );
997 const VECTOR2I newProject = newCrossBar.LineProject( newTextPos );
998
999 const VECTOR2I oldProjectedOffset =
1000 oldProject - m_oldCrossBar.NearestPoint( oldProject );
1001 const VECTOR2I newProjectedOffset = newProject - newCrossBar.NearestPoint( newProject );
1002
1003 const bool textWasLeftOf = oldProjectedOffset.x < 0
1004 || ( oldProjectedOffset.x == 0 && oldProjectedOffset.y > 0 );
1005 const bool textIsLeftOf = newProjectedOffset.x < 0
1006 || ( newProjectedOffset.x == 0 && newProjectedOffset.y > 0 );
1007
1008 if( textWasLeftOf != textIsLeftOf )
1009 {
1010 // Flip whatever the user had set
1011 m_dimension.SetHorizJustify( ( oldJustify == GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_LEFT )
1014 }
1015 }
1016
1017 // Update the dimension (again) to ensure the text knockouts are correct
1018 m_dimension.Update();
1019 }
1020
1021private:
1023 {
1024 const SEG newCrossBar{ m_dimension.GetCrossbarStart(), m_dimension.GetCrossbarEnd() };
1025
1026 const EDA_ANGLE oldAngle = EDA_ANGLE( m_oldCrossBar.B - m_oldCrossBar.A );
1027 const EDA_ANGLE newAngle = EDA_ANGLE( newCrossBar.B - newCrossBar.A );
1028 const EDA_ANGLE rotation = oldAngle - newAngle;
1029
1030 // There are two modes - when the text is between the crossbar points, and when it's not.
1032 {
1034 const VECTOR2I rotTextOffsetFromCbCenter = GetRotated( m_originalTextPos - m_oldCrossBar.Center(),
1035 rotation );
1036 const VECTOR2I rotTextOffsetFromCbEnd = GetRotated( m_originalTextPos - cbNearestEndToText, rotation );
1037
1038 // Which of the two crossbar points is now in the right direction? They could be swapped over now.
1039 // If zero-length, doesn't matter, they're the same thing
1040 const bool startIsInOffsetDirection = KIGEOM::PointIsInDirection( m_dimension.GetCrossbarStart(),
1041 rotTextOffsetFromCbCenter,
1042 newCrossBar.Center() );
1043
1044 const VECTOR2I& newCbRefPt = startIsInOffsetDirection ? m_dimension.GetCrossbarStart()
1045 : m_dimension.GetCrossbarEnd();
1046
1047 // Apply the new offset to the correct crossbar point
1048 return newCbRefPt + rotTextOffsetFromCbEnd;
1049 }
1050
1051 // If the text was between the crossbar points, it should stay there, but we need to find a
1052 // good place for it. Keep it the same distance from the crossbar line, but rotated as needed.
1053
1054 const VECTOR2I origTextPointProjected = m_oldCrossBar.NearestPoint( m_originalTextPos );
1055 const double oldRatio = KIGEOM::GetLengthRatioFromStart( origTextPointProjected, m_oldCrossBar );
1056
1057 // Perpendicular from the crossbar line to the text position
1058 // We need to keep this length constant
1059 const VECTOR2I rotCbNormalToText = GetRotated( m_originalTextPos - origTextPointProjected, rotation );
1060
1061 const VECTOR2I newProjected = newCrossBar.A + ( newCrossBar.B - newCrossBar.A ) * oldRatio;
1062 return newProjected + rotCbNormalToText;
1063 }
1064
1068};
1069
1070
1075{
1076public:
1078 m_dimension( aDimension )
1079 {
1080 }
1081
1082 void MakePoints( EDIT_POINTS& aPoints ) override
1083 {
1084 aPoints.AddPoint( m_dimension.GetStart() );
1085 aPoints.AddPoint( m_dimension.GetEnd() );
1086 aPoints.AddPoint( m_dimension.GetTextPos() );
1087 aPoints.AddPoint( m_dimension.GetCrossbarStart() );
1088 aPoints.AddPoint( m_dimension.GetCrossbarEnd() );
1089
1092
1093 if( m_dimension.Type() == PCB_DIM_ALIGNED_T )
1094 {
1095 // Dimension height setting - edit points should move only along the feature lines
1097 aPoints.Point( DIM_START ) ) );
1098 aPoints.Point( DIM_CROSSBAREND ).SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBAREND ),
1099 aPoints.Point( DIM_END ) ) );
1100 }
1101 }
1102
1103 void UpdatePoints( EDIT_POINTS& aPoints ) override
1104 {
1106
1107 aPoints.Point( DIM_START ).SetPosition( m_dimension.GetStart() );
1108 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1109 aPoints.Point( DIM_TEXT ).SetPosition( m_dimension.GetTextPos() );
1110 aPoints.Point( DIM_CROSSBARSTART ).SetPosition( m_dimension.GetCrossbarStart() );
1111 aPoints.Point( DIM_CROSSBAREND ).SetPosition( m_dimension.GetCrossbarEnd() );
1112 }
1113
1114 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1115 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1116 {
1118
1119 if( m_dimension.Type() == PCB_DIM_ALIGNED_T )
1120 updateAlignedDimension( aEditedPoint, aPoints );
1121 else
1122 updateOrthogonalDimension( aEditedPoint, aPoints );
1123 }
1124
1125 OPT_VECTOR2I Get45DegreeConstrainer( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints ) const override
1126 {
1127 // Constraint for crossbar
1128 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1129 return aPoints.Point( DIM_END ).GetPosition();
1130
1131 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1132 return aPoints.Point( DIM_START ).GetPosition();
1133
1134 // No constraint
1135 return aEditedPoint.GetPosition();
1136 }
1137
1138private:
1142 void updateAlignedDimension( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints )
1143 {
1144 DIM_ALIGNED_TEXT_UPDATER textPositionUpdater( m_dimension );
1145
1146 // Check which point is currently modified and updated dimension's points respectively
1147 if( isModified( aEditedPoint, aPoints.Point( DIM_CROSSBARSTART ) ) )
1148 {
1149 VECTOR2D featureLine( aEditedPoint.GetPosition() - m_dimension.GetStart() );
1150 VECTOR2D crossBar( m_dimension.GetEnd() - m_dimension.GetStart() );
1151
1152 if( featureLine.Cross( crossBar ) > 0 )
1153 m_dimension.SetHeight( -featureLine.EuclideanNorm() );
1154 else
1155 m_dimension.SetHeight( featureLine.EuclideanNorm() );
1156
1157 m_dimension.Update();
1158 }
1159 else if( isModified( aEditedPoint, aPoints.Point( DIM_CROSSBAREND ) ) )
1160 {
1161 VECTOR2D featureLine( aEditedPoint.GetPosition() - m_dimension.GetEnd() );
1162 VECTOR2D crossBar( m_dimension.GetEnd() - m_dimension.GetStart() );
1163
1164 if( featureLine.Cross( crossBar ) > 0 )
1165 m_dimension.SetHeight( -featureLine.EuclideanNorm() );
1166 else
1167 m_dimension.SetHeight( featureLine.EuclideanNorm() );
1168
1169 m_dimension.Update();
1170 }
1171 else if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1172 {
1173 m_dimension.SetStart( aEditedPoint.GetPosition() );
1174 m_dimension.Update();
1175
1177 aPoints.Point( DIM_START ) ) );
1178 aPoints.Point( DIM_CROSSBAREND ).SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBAREND ),
1179 aPoints.Point( DIM_END ) ) );
1180 }
1181 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1182 {
1183 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1184 m_dimension.Update();
1185
1187 aPoints.Point( DIM_START ) ) );
1188 aPoints.Point( DIM_CROSSBAREND ).SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBAREND ),
1189 aPoints.Point( DIM_END ) ) );
1190 }
1191 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1192 {
1193 // Force manual mode if we weren't already in it
1194 m_dimension.SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
1195 m_dimension.SetTextPos( aEditedPoint.GetPosition() );
1196 m_dimension.Update();
1197 }
1198
1199 textPositionUpdater.UpdateTextAfterChange();
1200 }
1201
1205 void updateOrthogonalDimension( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints )
1206 {
1207 DIM_ALIGNED_TEXT_UPDATER textPositionUpdater( m_dimension );
1208 PCB_DIM_ORTHOGONAL& orthDimension = static_cast<PCB_DIM_ORTHOGONAL&>( m_dimension );
1209
1210 if( isModified( aEditedPoint, aPoints.Point( DIM_CROSSBARSTART ) )
1211 || isModified( aEditedPoint, aPoints.Point( DIM_CROSSBAREND ) ) )
1212 {
1213 BOX2I bounds( m_dimension.GetStart(), m_dimension.GetEnd() - m_dimension.GetStart() );
1214
1215 const VECTOR2I& cursorPos = aEditedPoint.GetPosition();
1216
1217 // Find vector from nearest dimension point to edit position
1218 VECTOR2I directionA( cursorPos - m_dimension.GetStart() );
1219 VECTOR2I directionB( cursorPos - m_dimension.GetEnd() );
1220 VECTOR2I direction = ( directionA < directionB ) ? directionA : directionB;
1221
1222 bool vert;
1223 VECTOR2D featureLine( cursorPos - m_dimension.GetStart() );
1224
1225 // Only change the orientation when we move outside the bounds
1226 if( !bounds.Contains( cursorPos ) )
1227 {
1228 // If the dimension is horizontal or vertical, set correct orientation
1229 // otherwise, test if we're left/right of the bounding box or above/below it
1230 if( bounds.GetWidth() == 0 )
1231 vert = true;
1232 else if( bounds.GetHeight() == 0 )
1233 vert = false;
1234 else if( cursorPos.x > bounds.GetLeft() && cursorPos.x < bounds.GetRight() )
1235 vert = false;
1236 else if( cursorPos.y > bounds.GetTop() && cursorPos.y < bounds.GetBottom() )
1237 vert = true;
1238 else
1239 vert = std::abs( direction.y ) < std::abs( direction.x );
1240
1243 }
1244 else
1245 {
1246 vert = orthDimension.GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1247 }
1248
1249 m_dimension.SetHeight( vert ? featureLine.x : featureLine.y );
1250 }
1251 else if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1252 {
1253 m_dimension.SetStart( aEditedPoint.GetPosition() );
1254 }
1255 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1256 {
1257 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1258 }
1259 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1260 {
1261 // Force manual mode if we weren't already in it
1262 m_dimension.SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
1263 m_dimension.SetTextPos( VECTOR2I( aEditedPoint.GetPosition() ) );
1264 }
1265
1266 m_dimension.Update();
1267
1268 // After recompute, find the new text position
1269 textPositionUpdater.UpdateTextAfterChange();
1270 }
1271
1273};
1274
1275
1277{
1278public:
1280 m_dimension( aDimension )
1281 {}
1282
1283 void MakePoints( EDIT_POINTS& aPoints ) override
1284 {
1285 aPoints.AddPoint( m_dimension.GetStart() );
1286 aPoints.AddPoint( m_dimension.GetEnd() );
1287
1289
1290 aPoints.Point( DIM_END ).SetConstraint(new EC_45DEGREE( aPoints.Point( DIM_END ),
1291 aPoints.Point( DIM_START ) ) );
1293 }
1294
1295 void UpdatePoints( EDIT_POINTS& aPoints ) override
1296 {
1298
1299 aPoints.Point( DIM_START ).SetPosition( m_dimension.GetStart() );
1300 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1301 }
1302
1303 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1304 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1305 {
1307
1308 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1309 m_dimension.SetStart( aEditedPoint.GetPosition() );
1310 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1311 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1312
1313 m_dimension.Update();
1314 }
1315
1316 OPT_VECTOR2I Get45DegreeConstrainer( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints ) const override
1317 {
1318 if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1319 return aPoints.Point( DIM_START ).GetPosition();
1320
1321 return std::nullopt;
1322 }
1323
1324private:
1326};
1327
1328
1330{
1331public:
1333 m_dimension( aDimension )
1334 {}
1335
1336 void MakePoints( EDIT_POINTS& aPoints ) override
1337 {
1338 aPoints.AddPoint( m_dimension.GetStart() );
1339 aPoints.AddPoint( m_dimension.GetEnd() );
1340 aPoints.AddPoint( m_dimension.GetTextPos() );
1341 aPoints.AddPoint( m_dimension.GetKnee() );
1342
1345
1346 aPoints.Point( DIM_KNEE ).SetConstraint( new EC_LINE( aPoints.Point( DIM_START ),
1347 aPoints.Point( DIM_END ) ) );
1349
1350 aPoints.Point( DIM_TEXT ).SetConstraint( new EC_45DEGREE( aPoints.Point( DIM_TEXT ),
1351 aPoints.Point( DIM_KNEE ) ) );
1353 }
1354
1355 void UpdatePoints( EDIT_POINTS& aPoints ) override
1356 {
1358
1359 aPoints.Point( DIM_START ).SetPosition( m_dimension.GetStart() );
1360 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1361 aPoints.Point( DIM_TEXT ).SetPosition( m_dimension.GetTextPos() );
1362 aPoints.Point( DIM_KNEE ).SetPosition( m_dimension.GetKnee() );
1363 }
1364
1365 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1366 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1367 {
1369
1370 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1371 {
1372 m_dimension.SetStart( aEditedPoint.GetPosition() );
1373 m_dimension.Update();
1374
1375 aPoints.Point( DIM_KNEE ).SetConstraint( new EC_LINE( aPoints.Point( DIM_START ),
1376 aPoints.Point( DIM_END ) ) );
1377 }
1378 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1379 {
1380 VECTOR2I oldKnee = m_dimension.GetKnee();
1381
1382 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1383 m_dimension.Update();
1384
1385 VECTOR2I kneeDelta = m_dimension.GetKnee() - oldKnee;
1386 m_dimension.SetTextPos( m_dimension.GetTextPos() + kneeDelta );
1387 m_dimension.Update();
1388
1389 aPoints.Point( DIM_KNEE ).SetConstraint( new EC_LINE( aPoints.Point( DIM_START ),
1390 aPoints.Point( DIM_END ) ) );
1391 }
1392 else if( isModified( aEditedPoint, aPoints.Point( DIM_KNEE ) ) )
1393 {
1394 VECTOR2I oldKnee = m_dimension.GetKnee();
1395 VECTOR2I arrowVec = aPoints.Point( DIM_KNEE ).GetPosition() - aPoints.Point( DIM_END ).GetPosition();
1396
1397 m_dimension.SetLeaderLength( arrowVec.EuclideanNorm() );
1398 m_dimension.Update();
1399
1400 VECTOR2I kneeDelta = m_dimension.GetKnee() - oldKnee;
1401 m_dimension.SetTextPos( m_dimension.GetTextPos() + kneeDelta );
1402 m_dimension.Update();
1403 }
1404 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1405 {
1406 m_dimension.SetTextPos( aEditedPoint.GetPosition() );
1407 m_dimension.Update();
1408 }
1409 }
1410
1411 OPT_VECTOR2I Get45DegreeConstrainer( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints ) const override
1412 {
1413 if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1414 return aPoints.Point( DIM_KNEE ).GetPosition();
1415
1416 return std::nullopt;
1417 }
1418
1419private:
1421};
1422
1423
1425{
1426public:
1428 m_dimension( aDimension )
1429 {}
1430
1431 void MakePoints( EDIT_POINTS& aPoints ) override
1432 {
1433 aPoints.AddPoint( m_dimension.GetStart() );
1434 aPoints.AddPoint( m_dimension.GetEnd() );
1435 aPoints.AddPoint( m_dimension.GetTextPos() );
1436
1439
1440 aPoints.Point( DIM_TEXT ).SetConstraint( new EC_45DEGREE( aPoints.Point( DIM_TEXT ),
1441 aPoints.Point( DIM_END ) ) );
1443 }
1444
1445 void UpdatePoints( EDIT_POINTS& aPoints ) override
1446 {
1448
1449 aPoints.Point( DIM_START ).SetPosition( m_dimension.GetStart() );
1450 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1451 aPoints.Point( DIM_TEXT ).SetPosition( m_dimension.GetTextPos() );
1452 }
1453
1454 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1455 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1456 {
1458
1459 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1460 {
1461 m_dimension.SetStart( aEditedPoint.GetPosition() );
1462 }
1463 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1464 {
1465 const VECTOR2I newPoint( aEditedPoint.GetPosition() );
1466 const VECTOR2I delta = newPoint - m_dimension.GetEnd();
1467
1468 m_dimension.SetEnd( newPoint );
1469 m_dimension.SetTextPos( m_dimension.GetTextPos() + delta );
1470 }
1471 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1472 {
1473 m_dimension.SetTextPos( aEditedPoint.GetPosition() );
1474 }
1475
1476 m_dimension.Update();
1477 }
1478
1479private:
1481};
1482
1483
1488{
1489public:
1491 m_textbox( aTextbox )
1492 {}
1493
1494 void MakePoints( EDIT_POINTS& aPoints ) override
1495 {
1496 if( m_textbox.GetShape() == SHAPE_T::RECTANGLE )
1497 {
1499 }
1500 else
1501 {
1502 // Rotated textboxes are implemented as polygons and these
1503 // aren't currently editable.
1504 }
1505 }
1506
1507 void UpdatePoints( EDIT_POINTS& aPoints ) override
1508 {
1509 // When textboxes are rotated, they act as polygons, not rectangles
1510 const unsigned target = m_textbox.GetShape() == SHAPE_T::RECTANGLE ? TEXTBOX_POINT_COUNT::WHEN_RECTANGLE
1512
1513 // Careful; textbox shape is mutable between cardinal and non-cardinal rotations...
1514 if( aPoints.PointsSize() != target )
1515 {
1516 aPoints.Clear();
1517 MakePoints( aPoints );
1518 return;
1519 }
1520
1521 if( m_textbox.GetShape() == SHAPE_T::RECTANGLE )
1522 {
1523 // Dispatch to the rectangle behavior
1525 }
1526 else if( m_textbox.GetShape() == SHAPE_T::POLY )
1527 {
1528 // Not currently editable while rotated.
1529 }
1530 }
1531
1532 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1533 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1534 {
1535 if( m_textbox.GetShape() == SHAPE_T::RECTANGLE )
1536 {
1537 RECTANGLE_POINT_EDIT_BEHAVIOR::UpdateItem( m_textbox, aEditedPoint, aPoints );
1538 }
1539 }
1540
1541private:
1543};
1544
1546{
1547public:
1549 m_group( aGroup )
1550 {
1551 // Store original line widths as doubles for better scaling precision
1552 for( BOARD_ITEM* item : m_group.GetBoardItems() )
1553 {
1554 if( item->Type() == PCB_SHAPE_T )
1555 {
1556 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1557 m_originalWidths[shape] = static_cast<double>( shape->GetWidth() );
1558 }
1559 }
1560 }
1561
1562 void MakePoints( EDIT_POINTS& aPoints ) override
1563 {
1564 BOX2I bbox = m_group.GetBoundingBox();
1565 VECTOR2I tl = bbox.GetOrigin();
1566 VECTOR2I br = bbox.GetEnd();
1567
1568 aPoints.AddPoint( tl );
1569 aPoints.AddPoint( VECTOR2I( br.x, tl.y ) );
1570 aPoints.AddPoint( br );
1571 aPoints.AddPoint( VECTOR2I( tl.x, br.y ) );
1572 aPoints.AddPoint( bbox.Centre() );
1573
1574 aPoints.AddIndicatorLine( aPoints.Point( RECT_TOP_LEFT ), aPoints.Point( RECT_TOP_RIGHT ) );
1575 aPoints.AddIndicatorLine( aPoints.Point( RECT_TOP_RIGHT ), aPoints.Point( RECT_BOT_RIGHT ) );
1576 aPoints.AddIndicatorLine( aPoints.Point( RECT_BOT_RIGHT ), aPoints.Point( RECT_BOT_LEFT ) );
1577 aPoints.AddIndicatorLine( aPoints.Point( RECT_BOT_LEFT ), aPoints.Point( RECT_TOP_LEFT ) );
1578 }
1579
1580 void UpdatePoints( EDIT_POINTS& aPoints ) override
1581 {
1582 BOX2I bbox = m_group.GetBoundingBox();
1583 VECTOR2I tl = bbox.GetOrigin();
1584 VECTOR2I br = bbox.GetEnd();
1585
1586 aPoints.Point( RECT_TOP_LEFT ).SetPosition( tl );
1587 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( br.x, tl.y );
1588 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( br );
1589 aPoints.Point( RECT_BOT_LEFT ).SetPosition( tl.x, br.y );
1590 aPoints.Point( RECT_CENTER ).SetPosition( bbox.Centre() );
1591 }
1592
1593 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1594 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1595 {
1596 BOX2I oldBox = m_group.GetBoundingBox();
1597 VECTOR2I oldCenter = oldBox.Centre();
1598
1599 if( isModified( aEditedPoint, aPoints.Point( RECT_CENTER ) ) )
1600 {
1601 VECTOR2I delta = aPoints.Point( RECT_CENTER ).GetPosition() - oldCenter;
1602
1603 aCommit.Modify( &m_group, nullptr, RECURSE_MODE::RECURSE );
1604 m_group.Move( delta );
1605
1606 for( BOARD_ITEM* item : m_group.GetBoardItems() )
1607 aUpdatedItems.push_back( item );
1608
1609 UpdatePoints( aPoints );
1610 return;
1611 }
1612
1613 VECTOR2I tl = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
1614 VECTOR2I tr = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
1615 VECTOR2I bl = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
1616 VECTOR2I br = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
1617
1618 RECTANGLE_POINT_EDIT_BEHAVIOR::PinEditedCorner( aEditedPoint, aPoints, tl, tr, bl, br );
1619
1620 double sx = static_cast<double>( br.x - tl.x ) / static_cast<double>( oldBox.GetWidth() );
1621 double sy = static_cast<double>( br.y - tl.y ) / static_cast<double>( oldBox.GetHeight() );
1622 double scale = ( sx + sy ) / 2.0;
1623
1624 for( BOARD_ITEM* item : m_group.GetBoardItems() )
1625 {
1626 if( item->Type() != PCB_SHAPE_T )
1627 continue;
1628
1629 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1630 aCommit.Modify( shape );
1631 shape->Move( -oldCenter );
1632 shape->Scale( scale );
1633 shape->Move( oldCenter );
1634
1635 if( auto shapeIt = m_originalWidths.find( shape ); shapeIt != m_originalWidths.end() )
1636 {
1637 shapeIt->second = shapeIt->second * scale;
1638 shape->SetWidth( KiROUND( shapeIt->second ) );
1639 }
1640 else
1641 {
1642 // Shouldn't happen, but just in case
1643 shape->SetWidth( KiROUND( shape->GetWidth() * scale ) );
1644 }
1645
1646 aUpdatedItems.push_back( shape );
1647 }
1648
1649 UpdatePoints( aPoints );
1650 }
1651
1652private:
1654 std::unordered_map<PCB_SHAPE*, double> m_originalWidths;
1655};
1656
1658 PCB_TOOL_BASE( "pcbnew.PointEditor" ),
1659 m_frame( nullptr ),
1660 m_selectionTool( nullptr ),
1661 m_editedPoint( nullptr ),
1662 m_hoveredPoint( nullptr ),
1663 m_original( VECTOR2I( 0, 0 ) ),
1665 m_altConstrainer( VECTOR2I( 0, 0 ) ),
1666 m_inPointEditorTool( false ),
1667 m_angleSnapPos( VECTOR2I( 0, 0 ) ),
1668 m_stickyDisplacement( VECTOR2I( 0, 0 ) ),
1669 m_angleSnapActive( false )
1670{
1671}
1672
1673
1675{
1677 m_editPoints.reset();
1678 m_altConstraint.reset();
1679 getViewControls()->SetAutoPan( false );
1680 m_angleSnapActive = false;
1682}
1683
1684
1688static bool canAddCorner( const EDA_ITEM& aItem )
1689{
1690 const KICAD_T type = aItem.Type();
1691
1692 if( type == PCB_ZONE_T )
1693 return true;
1694
1695 if( type == PCB_SHAPE_T )
1696 {
1697 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
1698 const SHAPE_T shapeType = shape.GetShape();
1699 return shapeType == SHAPE_T::SEGMENT || shapeType == SHAPE_T::POLY || shapeType == SHAPE_T::ARC;
1700 }
1701
1702 return false;
1703}
1704
1709static bool canChamferCorner( const EDA_ITEM& aItem )
1710{
1711 const auto type = aItem.Type();
1712
1713 // Works only for zones and polygons
1714 if( type == PCB_ZONE_T )
1715 return true;
1716
1717 if( type == PCB_SHAPE_T )
1718 {
1719 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
1720 const SHAPE_T shapeType = shape.GetShape();
1721 return shapeType == SHAPE_T::POLY;
1722 }
1723
1724 return false;
1725}
1726
1727static VECTOR2I snapCorner( const VECTOR2I& aPrev, const VECTOR2I& aNext, const VECTOR2I& aGuess,
1728 double aAngleDeg )
1729{
1730 double angleRad = aAngleDeg * M_PI / 180.0;
1731 VECTOR2D prev( aPrev );
1732 VECTOR2D next( aNext );
1733 double chord = ( next - prev ).EuclideanNorm();
1734 double sinA = sin( angleRad );
1735
1736 if( chord == 0.0 || fabs( sinA ) < 1e-9 )
1737 return aGuess;
1738
1739 double radius = chord / ( 2.0 * sinA );
1740 VECTOR2D mid = ( prev + next ) / 2.0;
1741 VECTOR2D dir = next - prev;
1742 VECTOR2D normal( -dir.y, dir.x );
1743 normal = normal.Resize( 1 );
1744 double h_sq = radius * radius - ( chord * chord ) / 4.0;
1745 double h = h_sq > 0.0 ? sqrt( h_sq ) : 0.0;
1746
1747 VECTOR2D center1 = mid + normal * h;
1748 VECTOR2D center2 = mid - normal * h;
1749
1750 auto project = [&]( const VECTOR2D& center )
1751 {
1752 VECTOR2D v = VECTOR2D( aGuess ) - center;
1753
1754 if( v.EuclideanNorm() == 0.0 )
1755 v = prev - center;
1756
1757 v = v.Resize( 1 );
1758 VECTOR2D p = center + v * radius;
1759 return VECTOR2I( KiROUND( p.x ), KiROUND( p.y ) );
1760 };
1761
1762 VECTOR2I p1 = project( center1 );
1763 VECTOR2I p2 = project( center2 );
1764
1765 double d1 = ( VECTOR2D( aGuess ) - VECTOR2D( p1 ) ).EuclideanNorm();
1766 double d2 = ( VECTOR2D( aGuess ) - VECTOR2D( p2 ) ).EuclideanNorm();
1767
1768 return d1 < d2 ? p1 : p2;
1769}
1770
1771
1773{
1774 // Find the selection tool, so they can cooperate
1776
1777 wxASSERT_MSG( m_selectionTool, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
1778
1779 const auto addCornerCondition =
1780 []( const SELECTION& aSelection ) -> bool
1781 {
1782 const EDA_ITEM* item = aSelection.Front();
1783 return ( item != nullptr ) && canAddCorner( *item );
1784 };
1785
1786 const auto addChamferCondition =
1787 []( const SELECTION& aSelection ) -> bool
1788 {
1789 const EDA_ITEM* item = aSelection.Front();
1790 return ( item != nullptr ) && canChamferCorner( *item );
1791 };
1792
1793 const auto removeCornerCondition =
1794 [this]( const SELECTION& aSelection ) -> bool
1795 {
1796 return PCB_POINT_EDITOR::removeCornerCondition( aSelection );
1797 };
1798
1799 const auto arcIsEdited =
1800 []( const SELECTION& aSelection ) -> bool
1801 {
1802 const EDA_ITEM* item = aSelection.Front();
1803 return ( item != nullptr ) && ( item->Type() == PCB_SHAPE_T )
1804 && static_cast<const PCB_SHAPE*>( item )->GetShape() == SHAPE_T::ARC;
1805 };
1806
1807 using S_C = SELECTION_CONDITIONS;
1808
1809 auto& menu = m_selectionTool->GetToolMenu().GetMenu();
1810
1811 // clang-format off
1812 menu.AddItem( PCB_ACTIONS::pointEditorAddCorner, S_C::Count( 1 ) && addCornerCondition );
1814 menu.AddItem( PCB_ACTIONS::pointEditorChamferCorner, S_C::Count( 1 ) && addChamferCondition );
1815 menu.AddItem( PCB_ACTIONS::cycleArcEditMode, S_C::Count( 1 ) && arcIsEdited );
1816 // clang-format on
1817
1818 return true;
1819}
1820
1821
1822std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
1823{
1824 std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
1825
1826 if( !aItem )
1827 return points;
1828
1829 // Reset the behaviour and we'll make a new one
1830 m_editorBehavior = nullptr;
1831
1832 switch( aItem->Type() )
1833 {
1835 {
1836 PCB_REFERENCE_IMAGE& refImage = static_cast<PCB_REFERENCE_IMAGE&>( *aItem );
1837 m_editorBehavior = std::make_unique<REFERENCE_IMAGE_POINT_EDIT_BEHAVIOR>( refImage );
1838 break;
1839 }
1840 case PCB_TEXTBOX_T:
1841 {
1842 PCB_TEXTBOX& textbox = static_cast<PCB_TEXTBOX&>( *aItem );
1843 m_editorBehavior = std::make_unique<TEXTBOX_POINT_EDIT_BEHAVIOR>( textbox );
1844 break;
1845 }
1846 case PCB_SHAPE_T:
1847 {
1848 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
1849
1850 switch( shape->GetShape() )
1851 {
1852 case SHAPE_T::SEGMENT:
1853 m_editorBehavior = std::make_unique<EDA_SEGMENT_POINT_EDIT_BEHAVIOR>( *shape );
1854 break;
1855
1856 case SHAPE_T::RECTANGLE:
1857 m_editorBehavior = std::make_unique<RECTANGLE_POINT_EDIT_BEHAVIOR>( *shape );
1858 break;
1859
1860 case SHAPE_T::ARC:
1861 m_editorBehavior = std::make_unique<EDA_ARC_POINT_EDIT_BEHAVIOR>(
1862 *shape, m_arcEditMode, *getViewControls() );
1863 break;
1864
1865 case SHAPE_T::CIRCLE:
1866 m_editorBehavior = std::make_unique<EDA_CIRCLE_POINT_EDIT_BEHAVIOR>( *shape );
1867 break;
1868
1869 case SHAPE_T::POLY:
1870 m_editorBehavior = std::make_unique<EDA_POLYGON_POINT_EDIT_BEHAVIOR>( *shape );
1871 break;
1872
1873 case SHAPE_T::BEZIER:
1874 m_editorBehavior = std::make_unique<EDA_BEZIER_POINT_EDIT_BEHAVIOR>(
1875 *shape, board()->GetDesignSettings().m_MaxError );
1876 break;
1877
1878 default: // suppress warnings
1879 break;
1880 }
1881
1882 break;
1883 }
1884
1885 case PCB_GROUP_T:
1886 {
1887 PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
1888 bool shapesOnly = true;
1889
1890 for( BOARD_ITEM* child : group->GetBoardItems() )
1891 {
1892 if( child->Type() != PCB_SHAPE_T )
1893 {
1894 shapesOnly = false;
1895 break;
1896 }
1897 }
1898
1899 if( shapesOnly )
1900 m_editorBehavior = std::make_unique<SHAPE_GROUP_POINT_EDIT_BEHAVIOR>( *group );
1901 else
1902 points.reset();
1903
1904 break;
1905 }
1906
1907 case PCB_TABLECELL_T:
1908 {
1909 PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( aItem );
1910
1911 // No support for point-editing of a rotated table
1912 if( cell->GetShape() == SHAPE_T::RECTANGLE )
1913 m_editorBehavior = std::make_unique<PCB_TABLECELL_POINT_EDIT_BEHAVIOR>( *cell );
1914
1915 break;
1916 }
1917
1918 case PCB_PAD_T:
1919 {
1920 // Pad edit only for the footprint editor
1922 {
1923 PAD& pad = static_cast<PAD&>( *aItem );
1924 PCB_LAYER_ID activeLayer = m_frame ? m_frame->GetActiveLayer() : PADSTACK::ALL_LAYERS;
1925
1926 // Point editor only handles copper shape changes
1927 if( !IsCopperLayer( activeLayer ) )
1928 activeLayer = IsFrontLayer( activeLayer ) ? F_Cu : B_Cu;
1929
1930 m_editorBehavior = std::make_unique<PAD_POINT_EDIT_BEHAVIOR>( pad, activeLayer );
1931 }
1932 break;
1933 }
1934
1935 case PCB_ZONE_T:
1936 {
1937 ZONE& zone = static_cast<ZONE&>( *aItem );
1938 m_editorBehavior = std::make_unique<ZONE_POINT_EDIT_BEHAVIOR>( zone );
1939 break;
1940 }
1941
1942 case PCB_GENERATOR_T:
1943 {
1944 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( aItem );
1945 m_editorBehavior = std::make_unique<GENERATOR_POINT_EDIT_BEHAVIOR>( *generator );
1946 break;
1947 }
1948
1949 case PCB_DIM_ALIGNED_T:
1951 {
1952 PCB_DIM_ALIGNED& dimension = static_cast<PCB_DIM_ALIGNED&>( *aItem );
1953 m_editorBehavior = std::make_unique<ALIGNED_DIMENSION_POINT_EDIT_BEHAVIOR>( dimension );
1954 break;
1955 }
1956
1957 case PCB_DIM_CENTER_T:
1958 {
1959 PCB_DIM_CENTER& dimension = static_cast<PCB_DIM_CENTER&>( *aItem );
1960 m_editorBehavior = std::make_unique<DIM_CENTER_POINT_EDIT_BEHAVIOR>( dimension );
1961 break;
1962 }
1963
1964 case PCB_DIM_RADIAL_T:
1965 {
1966 PCB_DIM_RADIAL& dimension = static_cast<PCB_DIM_RADIAL&>( *aItem );
1967 m_editorBehavior = std::make_unique<DIM_RADIAL_POINT_EDIT_BEHAVIOR>( dimension );
1968 break;
1969 }
1970
1971 case PCB_DIM_LEADER_T:
1972 {
1973 PCB_DIM_LEADER& dimension = static_cast<PCB_DIM_LEADER&>( *aItem );
1974 m_editorBehavior = std::make_unique<DIM_LEADER_POINT_EDIT_BEHAVIOR>( dimension );
1975 break;
1976 }
1977
1978 default:
1979 points.reset();
1980 break;
1981 }
1982
1983 if( m_editorBehavior )
1984 m_editorBehavior->MakePoints( *points );
1985
1986 return points;
1987}
1988
1989
1991{
1992 EDIT_POINT* point;
1993 EDIT_POINT* hovered = nullptr;
1994
1995 if( aEvent.IsMotion() )
1996 {
1997 point = m_editPoints->FindPoint( aEvent.Position(), getView() );
1998 hovered = point;
1999 }
2000 else if( aEvent.IsDrag( BUT_LEFT ) )
2001 {
2002 point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
2003 }
2004 else
2005 {
2006 point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
2007 }
2008
2009 if( hovered )
2010 {
2011 if( m_hoveredPoint != hovered )
2012 {
2013 if( m_hoveredPoint )
2014 m_hoveredPoint->SetHover( false );
2015
2016 m_hoveredPoint = hovered;
2017 m_hoveredPoint->SetHover();
2018 }
2019 }
2020 else if( m_hoveredPoint )
2021 {
2022 m_hoveredPoint->SetHover( false );
2023 m_hoveredPoint = nullptr;
2024 }
2025
2026 if( m_editedPoint != point )
2027 setEditedPoint( point );
2028}
2029
2030
2032{
2034 return 0;
2035
2037 return 0;
2038
2040
2042 const PCB_SELECTION& selection = m_selectionTool->GetSelection();
2043
2044 if( selection.Size() != 1 || selection.Front()->GetEditFlags() || !selection.Front()->IsBOARD_ITEM() )
2045 return 0;
2046
2047 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
2048
2049 if( !item || item->IsLocked() )
2050 return 0;
2051
2052 Activate();
2053 // Must be done after Activate() so that it gets set into the correct context
2054 getViewControls()->ShowCursor( true );
2055
2057
2058 // Use the original object as a construction item
2059 std::vector<std::unique_ptr<BOARD_ITEM>> clones;
2060
2061 m_editorBehavior.reset();
2062 // Will also make the edit behavior if supported
2063 m_editPoints = makePoints( item );
2064
2065 if( !m_editPoints )
2066 return 0;
2067
2068 PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
2069
2070 // Only add the angle_item if we are editing a polygon or zone
2071 if( item->Type() == PCB_ZONE_T || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
2072 {
2073 m_angleItem = std::make_unique<KIGFX::PREVIEW::ANGLE_ITEM>( m_editPoints.get() );
2074 }
2075
2076 m_preview.FreeItems();
2077 getView()->Add( &m_preview );
2078
2079 RECT_RADIUS_TEXT_ITEM* radiusHelper = new RECT_RADIUS_TEXT_ITEM( pcbIUScale, editFrame->GetUserUnits() );
2080 m_preview.Add( radiusHelper );
2081
2082 getView()->Add( m_editPoints.get() );
2083
2084 if( m_angleItem )
2085 getView()->Add( m_angleItem.get() );
2086
2087 setEditedPoint( nullptr );
2088 updateEditedPoint( aEvent );
2089 bool inDrag = false;
2090 bool isConstrained = false;
2091
2092 BOARD_COMMIT commit( editFrame );
2093
2094 // Main loop: keep receiving events
2095 while( TOOL_EVENT* evt = Wait() )
2096 {
2097 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2098 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2099
2100 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2102 else
2104
2105 if( !m_editPoints || evt->IsSelectionEvent() || evt->Matches( EVENTS::InhibitSelectionEditing ) )
2106 {
2107 break;
2108 }
2109
2110 EDIT_POINT* prevHover = m_hoveredPoint;
2111
2112 if( !inDrag )
2113 updateEditedPoint( *evt );
2114
2115 if( prevHover != m_hoveredPoint )
2116 {
2117 getView()->Update( m_editPoints.get() );
2118
2119 if( m_angleItem )
2120 getView()->Update( m_angleItem.get() );
2121 }
2122
2123 if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
2124 {
2125 if( !inDrag )
2126 {
2127 frame()->UndoRedoBlock( true );
2128
2129 if( item->Type() == PCB_GENERATOR_T )
2130 {
2131 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genStartEdit, &commit,
2132 static_cast<PCB_GENERATOR*>( item ) );
2133 }
2134
2136 m_original = *m_editedPoint; // Save the original position
2137 getViewControls()->SetAutoPan( true );
2138 inDrag = true;
2139
2140 if( m_editedPoint->GetGridConstraint() != SNAP_BY_GRID )
2141 grid.SetAuxAxes( true, m_original.GetPosition() );
2142
2143 m_editedPoint->SetActive();
2144
2145 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2146 {
2147 EDIT_POINT& point = m_editPoints->Point( ii );
2148
2149 if( &point != m_editedPoint )
2150 point.SetActive( false );
2151 }
2152
2153 // When we start dragging, create a clone of the item to use as the original
2154 // reference geometry (e.g. for intersections and extensions)
2155 BOARD_ITEM* clone = static_cast<BOARD_ITEM*>( item->Clone() );
2156 clone->SetParent( nullptr );
2157
2158 if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
2159 {
2160 shape->SetFlags( IS_MOVING );
2161 shape->UpdateHatching();
2162
2163 static_cast<PCB_SHAPE*>( clone )->SetFillMode( FILL_T::NO_FILL );
2164 }
2165
2166 clones.emplace_back( clone );
2167 grid.AddConstructionItems( { clone }, false, true );
2168 }
2169
2170 bool need_constraint = Is45Limited() || Is90Limited();
2171
2172 if( isConstrained != need_constraint )
2173 {
2174 setAltConstraint( need_constraint );
2175 isConstrained = need_constraint;
2176 }
2177
2178 // Keep point inside of limits with some padding
2179 VECTOR2I pos = GetClampedCoords<double, int>( evt->Position(), COORDS_PADDING );
2180 LSET snapLayers;
2181
2182 switch( m_editedPoint->GetSnapConstraint() )
2183 {
2184 case IGNORE_SNAPS: break;
2185 case OBJECT_LAYERS: snapLayers = item->GetLayerSet(); break;
2186 case ALL_LAYERS: snapLayers = LSET::AllLayersMask(); break;
2187 }
2188
2189 if( m_editedPoint->GetGridConstraint() == SNAP_BY_GRID )
2190 {
2191 if( grid.GetUseGrid() )
2192 {
2193 VECTOR2I gridPt = grid.BestSnapAnchor( pos, {}, grid.GetItemGrid( item ), { item } );
2194
2195 VECTOR2I last = m_editedPoint->GetPosition();
2196 VECTOR2I delta = pos - last;
2197 VECTOR2I deltaGrid = gridPt - grid.BestSnapAnchor( last, {}, grid.GetItemGrid( item ),
2198 { item } );
2199
2200 if( abs( delta.x ) > grid.GetGrid().x / 2 )
2201 pos.x = last.x + deltaGrid.x;
2202 else
2203 pos.x = last.x;
2204
2205 if( abs( delta.y ) > grid.GetGrid().y / 2 )
2206 pos.y = last.y + deltaGrid.y;
2207 else
2208 pos.y = last.y;
2209 }
2210 }
2211
2212 if( m_angleSnapActive )
2213 {
2214 m_stickyDisplacement = evt->Position() - m_angleSnapPos;
2215 int stickyLimit = KiROUND( getView()->ToWorld( 5 ) );
2216
2217 if( m_stickyDisplacement.EuclideanNorm() > stickyLimit || evt->Modifier( MD_SHIFT ) )
2218 {
2219 m_angleSnapActive = false;
2220 }
2221 else
2222 {
2223 pos = m_angleSnapPos;
2224 }
2225 }
2226
2227 if( !m_angleSnapActive && m_editPoints->PointsSize() > 2 && !evt->Modifier( MD_SHIFT ) )
2228 {
2229 int idx = getEditedPointIndex();
2230
2231 if( idx != wxNOT_FOUND )
2232 {
2233 int prevIdx = ( idx + m_editPoints->PointsSize() - 1 ) % m_editPoints->PointsSize();
2234 int nextIdx = ( idx + 1 ) % m_editPoints->PointsSize();
2235 VECTOR2I prev = m_editPoints->Point( prevIdx ).GetPosition();
2236 VECTOR2I next = m_editPoints->Point( nextIdx ).GetPosition();
2237 SEG segA( pos, prev );
2238 SEG segB( pos, next );
2239 double ang = segA.Angle( segB ).AsDegrees();
2240 double snapAng = 45.0 * std::round( ang / 45.0 );
2241
2242 if( std::abs( ang - snapAng ) < 2.0 )
2243 {
2244 m_angleSnapPos = snapCorner( prev, next, pos, snapAng );
2245 m_angleSnapActive = true;
2246 m_stickyDisplacement = evt->Position() - m_angleSnapPos;
2247 pos = m_angleSnapPos;
2248 }
2249 }
2250 }
2251
2252 // Apply 45 degree or other constraints
2254 {
2255 m_editedPoint->SetPosition( pos );
2256 m_altConstraint->Apply( grid );
2257 }
2258 else if( !m_angleSnapActive && m_editedPoint->IsConstrained() )
2259 {
2260 m_editedPoint->SetPosition( pos );
2261 m_editedPoint->ApplyConstraint( grid );
2262 }
2263 else if( !m_angleSnapActive && m_editedPoint->GetGridConstraint() == SNAP_TO_GRID )
2264 {
2265 m_editedPoint->SetPosition( grid.BestSnapAnchor( pos, snapLayers, grid.GetItemGrid( item ),
2266 { item } ) );
2267 }
2268 else
2269 {
2270 m_editedPoint->SetPosition( pos );
2271 }
2272
2273 updateItem( commit );
2274 getViewControls()->ForceCursorPosition( true, m_editedPoint->GetPosition() );
2275 updatePoints();
2276
2277 if( m_editPoints->PointsSize() > RECT_RADIUS
2278 && m_editedPoint == &m_editPoints->Point( RECT_RADIUS ) )
2279 {
2280 if( PCB_SHAPE* rect = dynamic_cast<PCB_SHAPE*>( item ) )
2281 {
2282 int radius = rect->GetCornerRadius();
2283 int offset = radius - M_SQRT1_2 * radius;
2284 VECTOR2I topLeft = rect->GetTopLeft();
2285 VECTOR2I botRight = rect->GetBotRight();
2286 VECTOR2I topRight( botRight.x, topLeft.y );
2287 VECTOR2I center( topRight.x - offset, topRight.y + offset );
2288 radiusHelper->Set( radius, center, VECTOR2I( 1, -1 ), editFrame->GetUserUnits() );
2289 }
2290 }
2291 else
2292 {
2293 radiusHelper->Hide();
2294 }
2295
2296 getView()->Update( &m_preview );
2297 }
2298 else if( m_editedPoint && evt->Action() == TA_MOUSE_DOWN && evt->Buttons() == BUT_LEFT )
2299 {
2300 m_editedPoint->SetActive();
2301
2302 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2303 {
2304 EDIT_POINT& point = m_editPoints->Point( ii );
2305
2306 if( &point != m_editedPoint )
2307 point.SetActive( false );
2308 }
2309
2310 getView()->Update( m_editPoints.get() );
2311
2312 if( m_angleItem )
2313 getView()->Update( m_angleItem.get() );
2314 }
2315 else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
2316 {
2317 if( m_editedPoint )
2318 {
2319 m_editedPoint->SetActive( false );
2320 getView()->Update( m_editPoints.get() );
2321
2322 if( m_angleItem )
2323 getView()->Update( m_angleItem.get() );
2324 }
2325
2326 radiusHelper->Hide();
2327 getView()->Update( &m_preview );
2328
2329 getViewControls()->SetAutoPan( false );
2330 setAltConstraint( false );
2331
2332 if( m_editorBehavior )
2333 m_editorBehavior->FinalizeItem( *m_editPoints, commit );
2334
2335 if( item->Type() == PCB_GENERATOR_T )
2336 {
2337 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( item );
2338
2339 m_preview.FreeItems();
2340 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genFinishEdit, &commit, generator );
2341
2342 commit.Push( generator->GetCommitMessage() );
2343 }
2344 else if( item->Type() == PCB_TABLECELL_T )
2345 {
2346 commit.Push( _( "Resize Table Cells" ) );
2347 }
2348 else
2349 {
2350 commit.Push( _( "Move Point" ) );
2351 }
2352
2353 if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
2354 {
2355 shape->ClearFlags( IS_MOVING );
2356 shape->UpdateHatching();
2357 }
2358
2359 inDrag = false;
2360 frame()->UndoRedoBlock( false );
2361
2362 m_toolMgr->PostAction<EDA_ITEM*>( ACTIONS::reselectItem, item ); // FIXME: Needed for generators
2363 }
2364 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2365 {
2366 if( inDrag ) // Restore the last change
2367 {
2368 if( item->Type() == PCB_GENERATOR_T )
2369 {
2370 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genCancelEdit, &commit,
2371 static_cast<PCB_GENERATOR*>( item ) );
2372 }
2373
2374 commit.Revert();
2375
2376 if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
2377 {
2378 shape->ClearFlags( IS_MOVING );
2379 shape->UpdateHatching();
2380 }
2381
2382 inDrag = false;
2383 frame()->UndoRedoBlock( false );
2384 }
2385
2386 // Only cancel point editor when activating a new tool
2387 // Otherwise, allow the points to persist when moving up the
2388 // tool stack
2389 if( evt->IsActivate() && !evt->IsMoveTool() )
2390 break;
2391 }
2392 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2393 {
2394 // Re-create the points for items which can have different behavior on different layers
2395 if( item->Type() == PCB_PAD_T && m_isFootprintEditor )
2396 {
2397 getView()->Remove( m_editPoints.get() );
2398
2399 if( m_angleItem )
2400 getView()->Remove( m_angleItem.get() );
2401
2402 m_editPoints = makePoints( item );
2403
2404 if( m_angleItem )
2405 {
2406 m_angleItem->SetEditPoints( m_editPoints.get() );
2407 getView()->Add( m_angleItem.get() );
2408 }
2409
2410 getView()->Add( m_editPoints.get() );
2411 }
2412 }
2413 else if( evt->Action() == TA_UNDO_REDO_POST )
2414 {
2415 break;
2416 }
2417 else
2418 {
2419 evt->SetPassEvent();
2420 }
2421 }
2422
2423 if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
2424 {
2425 shape->ClearFlags( IS_MOVING );
2426 shape->UpdateHatching();
2427 }
2428
2429 m_preview.FreeItems();
2430 getView()->Remove( &m_preview );
2431
2432 if( m_editPoints )
2433 {
2434 getView()->Remove( m_editPoints.get() );
2435
2436 if( m_angleItem )
2437 getView()->Remove( m_angleItem.get() );
2438
2439 m_editPoints.reset();
2440 m_angleItem.reset();
2441 }
2442
2443 m_editedPoint = nullptr;
2444
2445 return 0;
2446}
2447
2448
2450{
2451 if( !m_editPoints || !m_editPoints->GetParent() || !HasPoint() )
2452 return 0;
2453
2455
2456 BOARD_COMMIT commit( editFrame );
2457 commit.Stage( m_editPoints->GetParent(), CHT_MODIFY );
2458
2459 VECTOR2I pt = m_editedPoint->GetPosition();
2460 wxString title;
2461 wxString msg;
2462
2463 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2464 {
2465 title = _( "Move Midpoint to Location" );
2466 msg = _( "Move Midpoint" );
2467 }
2468 else
2469 {
2470 title = _( "Move Corner to Location" );
2471 msg = _( "Move Corner" );
2472 }
2473
2474 WX_PT_ENTRY_DIALOG dlg( editFrame, title, _( "X:" ), _( "Y:" ), pt, false );
2475
2476 if( dlg.ShowModal() == wxID_OK )
2477 {
2478 m_editedPoint->SetPosition( dlg.GetValue() );
2479 updateItem( commit );
2480 commit.Push( msg );
2481 }
2482
2483 return 0;
2484}
2485
2486
2488{
2489 wxCHECK( m_editPoints, /* void */ );
2490 EDA_ITEM* item = m_editPoints->GetParent();
2491
2492 if( !item )
2493 return;
2494
2495 // item is always updated
2496 std::vector<EDA_ITEM*> updatedItems = { item };
2497 aCommit.Modify( item );
2498
2499 if( m_editorBehavior )
2500 {
2501 wxCHECK( m_editedPoint, /* void */ );
2502 m_editorBehavior->UpdateItem( *m_editedPoint, *m_editPoints, aCommit, updatedItems );
2503 }
2504
2505 // Perform any post-edit actions that the item may require
2506
2507 switch( item->Type() )
2508 {
2509 case PCB_TEXTBOX_T:
2510 case PCB_SHAPE_T:
2511 {
2512 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2513
2514 if( shape->IsProxyItem() )
2515 {
2516 for( PAD* pad : shape->GetParentFootprint()->Pads() )
2517 {
2518 if( pad->IsEntered() )
2519 view()->Update( pad );
2520 }
2521 }
2522
2523 // Nuke outline font render caches
2524 if( PCB_TEXTBOX* textBox = dynamic_cast<PCB_TEXTBOX*>( item ) )
2525 textBox->ClearRenderCache();
2526
2527 break;
2528 }
2529 case PCB_GENERATOR_T:
2530 {
2531 GENERATOR_TOOL* generatorTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
2532 PCB_GENERATOR* generatorItem = static_cast<PCB_GENERATOR*>( item );
2533
2534 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genUpdateEdit, &aCommit, generatorItem );
2535
2536 // Note: POINT_EDITOR::m_preview holds only the canvas-draw status "popup"; the meanders
2537 // themselves (ROUTER_PREVIEW_ITEMs) are owned by the router.
2538
2539 m_preview.FreeItems();
2540
2541 for( EDA_ITEM* previewItem : generatorItem->GetPreviewItems( generatorTool, frame(), STATUS_ITEMS_ONLY ) )
2542 m_preview.Add( previewItem );
2543
2544 getView()->Update( &m_preview );
2545 break;
2546 }
2547 default:
2548 break;
2549 }
2550
2551 // Update the item and any affected items
2552 for( EDA_ITEM* updatedItem : updatedItems )
2553 {
2554 getView()->Update( updatedItem );
2555 }
2556
2557 frame()->SetMsgPanel( item );
2558}
2559
2560
2562{
2563 if( !m_editPoints )
2564 return;
2565
2566 EDA_ITEM* item = m_editPoints->GetParent();
2567
2568 if( !item )
2569 return;
2570
2571 if( !m_editorBehavior )
2572 return;
2573
2574 int editedIndex = -1;
2575
2576 if( m_editedPoint )
2577 {
2578 for( unsigned ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2579 {
2580 if( &m_editPoints->Point( ii ) == m_editedPoint )
2581 {
2582 editedIndex = ii;
2583 break;
2584 }
2585 }
2586 }
2587
2588 m_editorBehavior->UpdatePoints( *m_editPoints );
2589
2590 if( editedIndex >= 0 )
2591 {
2592 if( editedIndex < (int) m_editPoints->PointsSize() )
2593 m_editedPoint = &m_editPoints->Point( editedIndex );
2594 else
2595 m_editedPoint = nullptr;
2596 }
2597
2598 getView()->Update( m_editPoints.get() );
2599
2600 if( m_angleItem )
2601 getView()->Update( m_angleItem.get() );
2602}
2603
2604
2606{
2608
2609 if( aPoint )
2610 {
2611 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2612 controls->ForceCursorPosition( true, aPoint->GetPosition() );
2613 controls->ShowCursor( true );
2614 }
2615 else
2616 {
2617 if( frame()->ToolStackIsEmpty() )
2618 controls->ShowCursor( false );
2619
2620 controls->ForceCursorPosition( false );
2621 }
2622
2623 m_editedPoint = aPoint;
2624}
2625
2626
2628{
2629 if( aEnabled )
2630 {
2631 EDA_ITEM* parent = m_editPoints->GetParent();
2632 EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
2633 bool isPoly;
2634
2635 switch( parent->Type() )
2636 {
2637 case PCB_ZONE_T:
2638 isPoly = true;
2639 break;
2640
2641 case PCB_SHAPE_T:
2642 isPoly = static_cast<PCB_SHAPE*>( parent )->GetShape() == SHAPE_T::POLY;
2643 break;
2644
2645 default:
2646 isPoly = false;
2647 break;
2648 }
2649
2650 if( line && isPoly )
2651 {
2652 EC_CONVERGING* altConstraint = new EC_CONVERGING( *line, *m_editPoints );
2653 m_altConstraint.reset( (EDIT_CONSTRAINT<EDIT_POINT>*) altConstraint );
2654 }
2655 else
2656 {
2657 // Find a proper constraining point for angle snapping mode
2659
2660 if( Is90Limited() )
2662 else
2664 }
2665 }
2666 else
2667 {
2668 m_altConstraint.reset();
2669 }
2670}
2671
2672
2674{
2675 // If there's a behaviour and it provides a constrainer, use that
2676 if( m_editorBehavior )
2677 {
2678 const OPT_VECTOR2I constrainer = m_editorBehavior->Get45DegreeConstrainer( *m_editedPoint, *m_editPoints );
2679
2680 if( constrainer )
2681 return EDIT_POINT( *constrainer );
2682 }
2683
2684 // In any other case we may align item to its original position
2685 return m_original;
2686}
2687
2688
2689// Finds a corresponding vertex in a polygon set
2690static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
2691findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
2692{
2693 for( auto it = aPolySet.IterateWithHoles(); it; ++it )
2694 {
2695 auto vertexIdx = it.GetIndex();
2696
2697 if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
2698 return std::make_pair( true, vertexIdx );
2699 }
2700
2701 return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
2702}
2703
2704
2706{
2707 if( !m_editPoints || !m_editedPoint )
2708 return false;
2709
2710 EDA_ITEM* item = m_editPoints->GetParent();
2711 SHAPE_POLY_SET* polyset = nullptr;
2712
2713 if( !item )
2714 return false;
2715
2716 switch( item->Type() )
2717 {
2718 case PCB_ZONE_T:
2719 polyset = static_cast<ZONE*>( item )->Outline();
2720 break;
2721
2722 case PCB_SHAPE_T:
2723 if( static_cast<PCB_SHAPE*>( item )->GetShape() == SHAPE_T::POLY )
2724 polyset = &static_cast<PCB_SHAPE*>( item )->GetPolyShape();
2725 else
2726 return false;
2727
2728 break;
2729
2730 default:
2731 return false;
2732 }
2733
2734 std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX> vertex = findVertex( *polyset, *m_editedPoint );
2735
2736 if( !vertex.first )
2737 return false;
2738
2739 const SHAPE_POLY_SET::VERTEX_INDEX& vertexIdx = vertex.second;
2740
2741 // Check if there are enough vertices so one can be removed without
2742 // degenerating the polygon.
2743 // The first condition allows one to remove all corners from holes (when
2744 // there are only 2 vertices left, a hole is removed).
2745 if( vertexIdx.m_contour == 0
2746 && polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
2747 {
2748 return false;
2749 }
2750
2751 // Remove corner does not work with lines
2752 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2753 return false;
2754
2755 return m_editedPoint != nullptr;
2756}
2757
2758
2760{
2761 if( !m_editPoints )
2762 return 0;
2763
2764 EDA_ITEM* item = m_editPoints->GetParent();
2766 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
2767
2768 // called without an active edited polygon
2769 if( !item || !canAddCorner( *item ) )
2770 return 0;
2771
2772 PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
2773 BOARD_COMMIT commit( frame );
2774
2775 if( item->Type() == PCB_ZONE_T || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
2776 {
2777 unsigned int nearestIdx = 0;
2778 unsigned int nextNearestIdx = 0;
2779 unsigned int nearestDist = INT_MAX;
2780 unsigned int firstPointInContour = 0;
2781 SHAPE_POLY_SET* zoneOutline;
2782
2783 if( item->Type() == PCB_ZONE_T )
2784 {
2785 ZONE* zone = static_cast<ZONE*>( item );
2786 zoneOutline = zone->Outline();
2787 zone->SetNeedRefill( true );
2788 }
2789 else
2790 {
2791 zoneOutline = &( graphicItem->GetPolyShape() );
2792 }
2793
2794 commit.Modify( item );
2795
2796 // Search the best outline segment to add a new corner
2797 // and therefore break this segment into two segments
2798
2799 // Object to iterate through the corners of the outlines (main contour and its holes)
2800 SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0, zoneOutline->OutlineCount()-1,
2801 /* IterateHoles */ true );
2802 int curr_idx = 0;
2803
2804 // Iterate through all the corners of the outlines and search the best segment
2805 for( ; iterator; iterator++, curr_idx++ )
2806 {
2807 int jj = curr_idx+1;
2808
2809 if( iterator.IsEndContour() )
2810 { // We reach the last point of the current contour (main or hole)
2811 jj = firstPointInContour;
2812 firstPointInContour = curr_idx+1; // Prepare next contour analysis
2813 }
2814
2815 SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
2816
2817 unsigned int distance = curr_segment.Distance( cursorPos );
2818
2819 if( distance < nearestDist )
2820 {
2821 nearestDist = distance;
2822 nearestIdx = curr_idx;
2823 nextNearestIdx = jj;
2824 }
2825 }
2826
2827 // Find the point on the closest segment
2828 const VECTOR2I& sideOrigin = zoneOutline->CVertex( nearestIdx );
2829 const VECTOR2I& sideEnd = zoneOutline->CVertex( nextNearestIdx );
2830 SEG nearestSide( sideOrigin, sideEnd );
2831 VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
2832
2833 // Do not add points that have the same coordinates as ones that already belong to polygon
2834 // instead, add a point in the middle of the side
2835 if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
2836 nearestPoint = ( sideOrigin + sideEnd ) / 2;
2837
2838 zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
2839
2840 if( item->Type() == PCB_ZONE_T )
2841 static_cast<ZONE*>( item )->HatchBorder();
2842
2843 commit.Push( _( "Add Zone Corner" ) );
2844 }
2845 else if( graphicItem )
2846 {
2847 switch( graphicItem->GetShape() )
2848 {
2849 case SHAPE_T::SEGMENT:
2850 {
2851 commit.Modify( graphicItem );
2852
2853 SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
2854 VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
2855
2856 // Move the end of the line to the break point..
2857 graphicItem->SetEnd( nearestPoint );
2858
2859 // and add another one starting from the break point
2860 PCB_SHAPE* newSegment = static_cast<PCB_SHAPE*>( graphicItem->Duplicate( true, &commit ) );
2861 newSegment->ClearSelected();
2862 newSegment->SetStart( nearestPoint );
2863 newSegment->SetEnd( VECTOR2I( seg.B.x, seg.B.y ) );
2864
2865 commit.Add( newSegment );
2866 commit.Push( _( "Split Segment" ) );
2867 break;
2868 }
2869 case SHAPE_T::ARC:
2870 {
2871 commit.Modify( graphicItem );
2872
2873 const SHAPE_ARC arc( graphicItem->GetStart(), graphicItem->GetArcMid(), graphicItem->GetEnd(), 0 );
2874 const VECTOR2I nearestPoint = arc.NearestPoint( cursorPos );
2875
2876 // Move the end of the arc to the break point..
2877 graphicItem->SetEnd( nearestPoint );
2878
2879 // and add another one starting from the break point
2880 PCB_SHAPE* newArc = static_cast<PCB_SHAPE*>( graphicItem->Duplicate( true, &commit ) );
2881
2882 newArc->ClearSelected();
2883 newArc->SetEnd( arc.GetP1() );
2884 newArc->SetStart( nearestPoint );
2885
2886 commit.Add( newArc );
2887 commit.Push( _( "Split Arc" ) );
2888 break;
2889 }
2890 default:
2891 // No split implemented for other shapes
2892 break;
2893 }
2894 }
2895
2896 updatePoints();
2897 return 0;
2898}
2899
2900
2902{
2903 if( !m_editPoints || !m_editedPoint )
2904 return 0;
2905
2906 EDA_ITEM* item = m_editPoints->GetParent();
2907
2908 if( !item )
2909 return 0;
2910
2911 SHAPE_POLY_SET* polygon = nullptr;
2912
2913 if( item->Type() == PCB_ZONE_T )
2914 {
2915 ZONE* zone = static_cast<ZONE*>( item );
2916 polygon = zone->Outline();
2917 zone->SetNeedRefill( true );
2918 }
2919 else if( item->Type() == PCB_SHAPE_T )
2920 {
2921 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2922
2923 if( shape->GetShape() == SHAPE_T::POLY )
2924 polygon = &shape->GetPolyShape();
2925 }
2926
2927 if( !polygon )
2928 return 0;
2929
2931 BOARD_COMMIT commit( frame );
2932 auto vertex = findVertex( *polygon, *m_editedPoint );
2933
2934 if( vertex.first )
2935 {
2936 const auto& vertexIdx = vertex.second;
2937 auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
2938
2939 if( outline.PointCount() > 3 )
2940 {
2941 // the usual case: remove just the corner when there are >3 vertices
2942 commit.Modify( item );
2943 polygon->RemoveVertex( vertexIdx );
2944 }
2945 else
2946 {
2947 // either remove a hole or the polygon when there are <= 3 corners
2948 if( vertexIdx.m_contour > 0 )
2949 {
2950 // remove hole
2951 commit.Modify( item );
2952 polygon->RemoveContour( vertexIdx.m_contour );
2953 }
2954 else
2955 {
2956 m_toolMgr->RunAction( ACTIONS::selectionClear );
2957 commit.Remove( item );
2958 }
2959 }
2960
2961 setEditedPoint( nullptr );
2962
2963 if( item->Type() == PCB_ZONE_T )
2964 commit.Push( _( "Remove Zone Corner" ) );
2965 else
2966 commit.Push( _( "Remove Polygon Corner" ) );
2967
2968 if( item->Type() == PCB_ZONE_T )
2969 static_cast<ZONE*>( item )->HatchBorder();
2970
2971 updatePoints();
2972 }
2973
2974 return 0;
2975}
2976
2977
2979{
2980 if( !m_editPoints || !m_editedPoint )
2981 return 0;
2982
2983 EDA_ITEM* item = m_editPoints->GetParent();
2984
2985 if( !item )
2986 return 0;
2987
2988 SHAPE_POLY_SET* polygon = nullptr;
2989
2990 if( item->Type() == PCB_ZONE_T )
2991 {
2992 ZONE* zone = static_cast<ZONE*>( item );
2993 polygon = zone->Outline();
2994 zone->SetNeedRefill( true );
2995 }
2996 else if( item->Type() == PCB_SHAPE_T )
2997 {
2998 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2999
3000 if( shape->GetShape() == SHAPE_T::POLY )
3001 polygon = &shape->GetPolyShape();
3002 }
3003
3004 if( !polygon )
3005 return 0;
3006
3007 // Search the best outline corner to break
3008
3010 BOARD_COMMIT commit( frame );
3011 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
3012
3013 unsigned int nearestIdx = 0;
3014 unsigned int nearestDist = INT_MAX;
3015
3016 int curr_idx = 0;
3017 // Object to iterate through the corners of the outlines (main contour and its holes)
3018 SHAPE_POLY_SET::ITERATOR iterator = polygon->Iterate( 0, polygon->OutlineCount() - 1,
3019 /* IterateHoles */ true );
3020
3021 // Iterate through all the corners of the outlines and search the best segment
3022 for( ; iterator; iterator++, curr_idx++ )
3023 {
3024 unsigned int distance = polygon->CVertex( curr_idx ).Distance( cursorPos );
3025
3026 if( distance < nearestDist )
3027 {
3028 nearestDist = distance;
3029 nearestIdx = curr_idx;
3030 }
3031 }
3032
3033 int prevIdx, nextIdx;
3034 if( polygon->GetNeighbourIndexes( nearestIdx, &prevIdx, &nextIdx ) )
3035 {
3036 const SEG segA{ polygon->CVertex( prevIdx ), polygon->CVertex( nearestIdx ) };
3037 const SEG segB{ polygon->CVertex( nextIdx ), polygon->CVertex( nearestIdx ) };
3038
3039 // A plausible setback that won't consume a whole edge
3040 int setback = pcbIUScale.mmToIU( 5 );
3041 setback = std::min( setback, (int) ( segA.Length() * 0.25 ) );
3042 setback = std::min( setback, (int) ( segB.Length() * 0.25 ) );
3043
3044 CHAMFER_PARAMS chamferParams{ setback, setback };
3045
3046 std::optional<CHAMFER_RESULT> chamferResult = ComputeChamferPoints( segA, segB, chamferParams );
3047
3048 if( chamferResult && chamferResult->m_updated_seg_a && chamferResult->m_updated_seg_b )
3049 {
3050 commit.Modify( item );
3051 polygon->RemoveVertex( nearestIdx );
3052
3053 // The two end points of the chamfer are the new corners
3054 polygon->InsertVertex( nearestIdx, chamferResult->m_updated_seg_b->B );
3055 polygon->InsertVertex( nearestIdx, chamferResult->m_updated_seg_a->B );
3056 }
3057 }
3058
3059 setEditedPoint( nullptr );
3060
3061 if( item->Type() == PCB_ZONE_T )
3062 commit.Push( _( "Break Zone Corner" ) );
3063 else
3064 commit.Push( _( "Break Polygon Corner" ) );
3065
3066 if( item->Type() == PCB_ZONE_T )
3067 static_cast<ZONE*>( item )->HatchBorder();
3068
3069 updatePoints();
3070
3071 return 0;
3072}
3073
3074
3076{
3077 updatePoints();
3078 return 0;
3079}
3080
3081
3083{
3085
3086 if( aEvent.Matches( ACTIONS::cycleArcEditMode.MakeEvent() ) )
3087 {
3088 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
3090 else
3092
3094 }
3095 else
3096 {
3098 }
3099
3100 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
3102 else
3104
3105 return 0;
3106}
3107
3108
3110{
3128}
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 pcbIUScale
Definition base_units.h:112
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
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 reselectItem
Definition actions.h:228
static TOOL_ACTION activatePointEditor
Definition actions.h:272
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:223
static TOOL_ACTION pointEditorArcKeepEndpoint
Definition actions.h:275
void updateOrthogonalDimension(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints)
Update orthogonal dimension points.
ALIGNED_DIMENSION_POINT_EDIT_BEHAVIOR(PCB_DIM_ALIGNED &aDimension)
void updateAlignedDimension(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints)
Update non-orthogonal dimension points.
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.
void UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
OPT_VECTOR2I Get45DegreeConstrainer(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints) const override
Get the 45-degree constrainer for the item, when the given point is moved.
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE) override
Add a change of the item aItem of type aChangeType to the change list.
virtual void Revert() override
Revert the commit by restoring the modified items state.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:79
virtual BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const
Create a copy of this BOARD_ITEM.
bool IsLocked() const override
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:252
constexpr void SetMaximum()
Definition box2.h:80
constexpr const Vec GetEnd() const
Definition box2.h:212
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr Vec Centre() const
Definition box2.h:97
constexpr size_type GetHeight() const
Definition box2.h:215
constexpr coord_type GetLeft() const
Definition box2.h:228
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:168
constexpr const Vec & GetOrigin() const
Definition box2.h:210
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
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:90
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
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:78
int ShowModal() override
Class to help update the text position of a dimension when the crossbar changes.
DIM_ALIGNED_TEXT_UPDATER(PCB_DIM_ALIGNED &aDimension)
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
OPT_VECTOR2I Get45DegreeConstrainer(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints) const override
Get the 45-degree constrainer for the item, when the given point is moved.
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 UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
DIM_CENTER_POINT_EDIT_BEHAVIOR(PCB_DIM_CENTER &aDimension)
DIM_LEADER_POINT_EDIT_BEHAVIOR(PCB_DIM_LEADER &aDimension)
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.
void UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
DIM_RADIAL_POINT_EDIT_BEHAVIOR(PCB_DIM_RADIAL &aDimension)
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.
void UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
OPT_VECTOR2I Get45DegreeConstrainer(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints) const override
Get the 45-degree constrainer for the item, when the given point is moved.
EDIT_CONSTRAINT that imposes a constraint that two points have to be located at angle of 45 degree mu...
EDIT_CONSTRAINT that imposes a constraint that a point has to be located at angle of 90 degree multip...
EDIT_CONSTRAINT for 3 segments: dragged and two adjacent ones, enforcing to keep their slopes and all...
EDIT_CONSTRAINT that imposes a constraint that a point has to lie on a line (determined by 2 points).
EDIT_CONSTRAINT for a EDIT_LINE, that constrains the line to move perpendicular to the line itself.
double AsDegrees() const
Definition eda_angle.h:116
bool IsType(FRAME_T aType) const
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearSelected()
Definition eda_item.h:137
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.h:113
virtual EDA_ITEM * Clone() const
Create a duplicate of this item with linked list members set to NULL.
Definition eda_item.cpp:118
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:39
virtual VECTOR2I GetTopLeft() const
Definition eda_shape.h:246
void SetCornerRadius(int aRadius)
SHAPE_POLY_SET & GetPolyShape()
Definition eda_shape.h:337
SHAPE_T GetShape() const
Definition eda_shape.h:168
virtual VECTOR2I GetBotRight() const
Definition eda_shape.h:247
virtual void SetBottom(int val)
Definition eda_shape.h:252
virtual void SetTop(int val)
Definition eda_shape.h:249
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:215
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:177
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:173
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:219
virtual void SetLeft(int val)
Definition eda_shape.h:250
void SetWidth(int aWidth)
virtual void SetRight(int val)
Definition eda_shape.h:251
int GetCornerRadius() const
VECTOR2I GetArcMid() const
Describe constraints between two edit handles.
Represent a line connecting two EDIT_POINTs.
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.
unsigned int PointsSize() const
Return number of stored EDIT_POINTs.
void AddPoint(const EDIT_POINT &aPoint)
Add an EDIT_POINT.
void SetSwapY(bool aSwap)
void Clear()
Clear all stored EDIT_POINTs and EDIT_LINEs.
EDIT_LINE & Line(unsigned int aIndex)
void AddIndicatorLine(EDIT_POINT &aOrigin, EDIT_POINT &aEnd)
Adds an EDIT_LINE that is shown as an indicator, rather than an editable line (no center point drag,...
bool SwapX() const
bool SwapY() const
void SetSwapX(bool aSwap)
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
int GetY() const
Return Y coordinate of an EDIT_POINT.
Definition edit_points.h:96
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
void SetSnapConstraint(SNAP_CONSTRAINT_TYPE aConstraint)
void SetConstraint(EDIT_CONSTRAINT< EDIT_POINT > *aConstraint)
Set a constraint for and EDIT_POINT.
int GetX() const
Return X coordinate of an EDIT_POINT.
Definition edit_points.h:88
void SetActive(bool aActive=true)
void SetDrawCircle(bool aDrawCircle=true)
static const TOOL_EVENT InhibitSelectionEditing
Definition actions.h:359
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 UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition actions.h:360
static const TOOL_EVENT PointSelectedEvent
Definition actions.h:345
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition actions.h:356
static const TOOL_EVENT UnselectedEvent
Definition actions.h:347
std::deque< PAD * > & Pads()
Definition footprint.h:224
void 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.
GENERATOR_POINT_EDIT_BEHAVIOR(PCB_GENERATOR &aGenerator)
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
Handle actions specific to filling copper zones.
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition pcb_view.cpp:91
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
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
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllLayersMask()
Definition lset.cpp:624
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:145
void 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.
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.
PAD_POINT_EDIT_BEHAVIOR(PAD &aPad, PCB_LAYER_ID aLayer)
Definition pad.h:54
ARC_EDIT_MODE m_ArcEditMode
static TOOL_ACTION layerChanged
static TOOL_ACTION pointEditorMoveMidpoint
static TOOL_ACTION genFinishEdit
static TOOL_ACTION genStartEdit
static TOOL_ACTION pointEditorMoveCorner
static TOOL_ACTION genCancelEdit
static TOOL_ACTION genUpdateEdit
static TOOL_ACTION pointEditorChamferCorner
static TOOL_ACTION pointEditorRemoveCorner
static TOOL_ACTION pointEditorAddCorner
Common, abstract interface for edit frames.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
PCBNEW_SETTINGS * GetPcbNewSettings() const
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
FOOTPRINT_EDITOR_SETTINGS * GetFootprintEditorSettings() const
For better understanding of the points that make a dimension:
Mark the center of a circle or arc with a cross shape.
A leader is a dimension-like object pointing to a specific point.
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
void SetOrientation(DIR aOrientation)
Set the orientation of the dimension line (so, perpendicular to the feature lines).
DIR GetOrientation() const
A radial dimension indicates either the radius or diameter of an arc or circle.
virtual wxString GetCommitMessage() const =0
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:53
int changeArcEditMode(const TOOL_EVENT &aEvent)
void updateItem(BOARD_COMMIT &aCommit)
Update edit points with item's points.
VECTOR2I m_stickyDisplacement
int OnSelectionChange(const TOOL_EVENT &aEvent)
Change selection event handler.
void setAltConstraint(bool aEnabled)
Return a point that should be used as a constrainer for 45 degrees mode.
static const unsigned int COORDS_PADDING
bool removeCornerCondition(const SELECTION &aSelection)
EDIT_POINT * m_hoveredPoint
int addCorner(const TOOL_EVENT &aEvent)
bool HasPoint()
Indicate the cursor is over an edit point.
EDIT_POINT m_original
Original pos for the current drag point.
EDIT_POINT get45DegConstrainer() const
Condition to display "Remove corner" context menu entry.
int modifiedSelection(const TOOL_EVENT &aEvent)
Change the edit method for arcs.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
std::unique_ptr< POINT_EDIT_BEHAVIOR > m_editorBehavior
int removeCorner(const TOOL_EVENT &aEvent)
bool Init() override
Init() is called once upon a registration of the tool.
PCB_SELECTION m_preview
int movePoint(const TOOL_EVENT &aEvent)
TOOL_ACTION handlers.
void setEditedPoint(EDIT_POINT *aPoint)
EDIT_POINT m_altConstrainer
ARC_EDIT_MODE m_arcEditMode
std::unique_ptr< KIGFX::PREVIEW::ANGLE_ITEM > m_angleItem
EDIT_POINT * m_editedPoint
void updateEditedPoint(const TOOL_EVENT &aEvent)
Set the current point being edited. NULL means none.
std::shared_ptr< EDIT_CONSTRAINT< EDIT_POINT > > m_altConstraint
int getEditedPointIndex() const
Return true if aPoint is the currently modified point.
void updatePoints()
Update which point is being edited.
int chamferCorner(const TOOL_EVENT &aEvent)
void setTransitions() override
< Set up handlers for various events.
std::shared_ptr< EDIT_POINTS > m_editPoints
PCB_BASE_FRAME * m_frame
std::shared_ptr< EDIT_POINTS > makePoints(EDA_ITEM *aItem)
Update item's points with edit points.
PCB_SELECTION_TOOL * m_selectionTool
Object to handle a bitmap image that can be inserted in a PCB.
The selection tool: currently supports:
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:81
int GetWidth() const override
bool IsProxyItem() const override
Definition pcb_shape.h:116
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void Scale(double aScale)
PCB_TABLECELL_POINT_EDIT_BEHAVIOR(PCB_TABLECELL &aCell)
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.
T * frame() const
KIGFX::PCB_VIEW * view() const
virtual bool Is45Limited() const
Should the tool use its 45° mode option?
KIGFX::VIEW_CONTROLS * controls() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
BOARD * board() const
virtual bool Is90Limited() const
Should the tool limit drawing to horizontal and vertical only?
const PCB_SELECTION & selection() const
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.
POLYGON_POINT_EDIT_BEHAVIOR(SHAPE_POLY_SET &aPolygon)
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.
static void UpdateItem(SCH_SHAPE &aRect, const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints)
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 UpdatePoints(PCB_SHAPE &aRectangle, EDIT_POINTS &aPoints)
static void PinEditedCorner(const EDIT_POINT &aEditedPoint, const EDIT_POINTS &aEditPoints, VECTOR2I &aTopLeft, VECTOR2I &aTopRight, VECTOR2I &aBotLeft, VECTOR2I &aBotRight, const VECTOR2I &aHole={ 0, 0 }, const VECTOR2I &aHoleSize={ 0, 0 })
Update the coordinates of 4 corners of a rectangle, according to pad 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.
static void MakePoints(const PCB_SHAPE &aRectangle, EDIT_POINTS &aPoints)
Standard rectangle points construction utility (other shapes may use this as well)
static void UpdateItem(PCB_SHAPE &aRectangle, const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints)
void UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
RECTANGLE_POINT_EDIT_BEHAVIOR(PCB_SHAPE &aRectangle)
const EDA_IU_SCALE & m_iuScale
wxString GetClass() const override
Return the class name.
std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
void Set(int aRadius, const VECTOR2I &aCorner, const VECTOR2I &aQuadrant, EDA_UNITS aUnits)
RECT_RADIUS_TEXT_ITEM(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits)
void ViewDraw(int aLayer, KIGFX::VIEW *aView) const override
Draw the parts of the object belonging to layer aLayer.
REFERENCE_IMAGE_POINT_EDIT_BEHAVIOR(PCB_REFERENCE_IMAGE &aRefImage)
void 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.
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.
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
void SetTransformOriginOffset(const VECTOR2I &aCenter)
VECTOR2I GetTransformOriginOffset() const
Get the center of scaling, etc, relative to the image center (GetPosition()).
VECTOR2I GetPosition() const
VECTOR2I GetSize() const
double GetImageScale() const
void SetImageScale(double aScale)
Set the image "zoom" value.
Definition seg.h:42
VECTOR2I B
Definition seg.h:50
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:604
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:673
EDA_ANGLE Angle(const SEG &aOther) const
Determine the smallest angle between two segments.
Definition seg.cpp:102
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.
VECTOR2I NearestPoint(const VECTOR2I &aP) const
const VECTOR2I & GetP1() const
Definition shape_arc.h:117
SHAPE_GROUP_POINT_EDIT_BEHAVIOR(PCB_GROUP &aGroup)
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
std::unordered_map< PCB_SHAPE *, double > m_originalWidths
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 UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
Represent a set of closed polygons.
ITERATOR_TEMPLATE< VECTOR2I > ITERATOR
ITERATOR IterateWithHoles(int aOutline)
void InsertVertex(int aGlobalIndex, const VECTOR2I &aNewVertex)
Adds a vertex in the globally indexed position aGlobalIndex.
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
void RemoveVertex(int aGlobalIndex)
Delete the aGlobalIndex-th vertex.
void RemoveContour(int aContourIdx, int aPolygonIdx=-1)
Delete the aContourIdx-th contour of the aPolygonIdx-th polygon in the set.
bool GetNeighbourIndexes(int aGlobalIndex, int *aPrevious, int *aNext) const
Return the global indexes of the previous and the next corner of the aGlobalIndex-th corner of a cont...
ITERATOR Iterate(int aFirst, int aLast, bool aIterateHoles=false)
Return an object to iterate through the points of the polygons between aFirst and aLast.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
int OutlineCount() const
Return the number of outlines in the set.
A textbox is edited as a rectnagle when it is orthogonally aligned.
TEXTBOX_POINT_EDIT_BEHAVIOR(PCB_TEXTBOX &aTextbox)
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.
void UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:186
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:44
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:38
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:78
Generic, UI-independent tool event.
Definition tool_event.h:171
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(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_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.
EDA_UNITS GetUserUnits() const
constexpr extended_type Cross(const VECTOR2< T > &aVector) const
Compute cross product of self with aVector.
Definition vector2d.h:546
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition vector2d.h:561
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:283
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:385
VECTOR2I GetValue()
Return the value in internal units.
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.
Handle a list of polygons defining a copper zone.
Definition zone.h:74
void SetNeedRefill(bool aNeedRefill)
Definition zone.h:296
SHAPE_POLY_SET * Outline()
Definition zone.h:335
@ CHT_MODIFY
Definition commit.h:44
This file is part of the common library.
std::optional< CHAMFER_RESULT > ComputeChamferPoints(const SEG &aSegA, const SEG &aSegB, const CHAMFER_PARAMS &aChamferParams)
Compute the chamfer points for a given line pair and chamfer parameters.
@ ARROW
Definition cursors.h:46
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
static constexpr EDA_ANGLE ANGLE_270
Definition eda_angle.h:416
@ RECURSE
Definition eda_item.h:51
#define IS_MOVING
Item being moved.
SHAPE_T
Definition eda_shape.h:43
@ SEGMENT
Definition eda_shape.h:45
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
@ NO_FILL
Definition eda_shape.h:57
EDA_UNITS
Definition eda_units.h:48
@ ALL_LAYERS
@ OBJECT_LAYERS
@ IGNORE_SNAPS
@ SNAP_BY_GRID
@ SNAP_TO_GRID
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
a few functions useful in geometry calculations.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamps a vector to values that can be negated, respecting numeric limits of coordinates data type wit...
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:776
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:674
@ LAYER_GP_OVERLAY
General purpose overlay.
Definition layer_ids.h:278
@ LAYER_SELECT_OVERLAY
Selected items overlay.
Definition layer_ids.h:279
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ B_Cu
Definition layer_ids.h:65
@ F_Cu
Definition layer_ids.h:64
constexpr int Mils2IU(const EDA_IU_SCALE &aIuScale, int mils)
Definition eda_units.h:166
bool PointProjectsOntoSegment(const VECTOR2I &aPoint, const SEG &aSeg)
Determine if a point projects onto a segment.
double GetLengthRatioFromStart(const VECTOR2I &aPoint, const SEG &aSeg)
Get the ratio of the vector to a point from the segment's start, compared to the segment's length.
const VECTOR2I & GetNearestEndpoint(const SEG &aSeg, const VECTOR2I &aPoint)
Get the nearest end of a segment to a point.
bool PointIsInDirection(const VECTOR2< T > &aPoint, const VECTOR2< T > &aDirection, const VECTOR2< T > &aFrom)
void DrawTextNextToCursor(KIGFX::VIEW *aView, const VECTOR2D &aCursorPos, const VECTOR2D &aTextQuadrant, const wxArrayString &aStrings, bool aDrawingDropShadows)
Draw strings next to the cursor.
wxString DimensionLabel(const wxString &prefix, double aVal, const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, bool aIncludeUnits=true)
Get a formatted string showing a dimension to a sane precision with an optional prefix and unit suffi...
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ TRAPEZOID
Definition padstack.h:56
@ RECTANGLE
Definition padstack.h:54
@ MANUAL
Text placement is manually set by the user.
#define STATUS_ITEMS_ONLY
Class to handle a set of BOARD_ITEMs.
static VECTOR2I snapCorner(const VECTOR2I &aPrev, const VECTOR2I &aNext, const VECTOR2I &aGuess, double aAngleDeg)
@ RECT_RIGHT
@ RECT_LEFT
@ RECT_BOT
@ RECT_TOP
static std::pair< bool, SHAPE_POLY_SET::VERTEX_INDEX > findVertex(SHAPE_POLY_SET &aPolySet, const EDIT_POINT &aPoint)
static bool canAddCorner(const EDA_ITEM &aItem)
Condition to check if a point editor can add a corner to the given item.
TEXTBOX_POINT_COUNT
@ WHEN_POLYGON
@ WHEN_RECTANGLE
@ RECT_MAX_POINTS
@ RECT_BOT_LEFT
@ RECT_BOT_RIGHT
@ RECT_CENTER
@ RECT_TOP_RIGHT
@ RECT_RADIUS
@ RECT_TOP_LEFT
DIMENSION_POINTS
@ DIM_LEADER_MAX
@ DIM_KNEE
@ DIM_TEXT
@ DIM_RADIAL_MAX
@ DIM_CROSSBAREND
@ DIM_END
@ DIM_START
@ DIM_ALIGNED_MAX
@ DIM_CENTER_MAX
@ DIM_CROSSBARSTART
static bool canChamferCorner(const EDA_ITEM &aItem)
Condition to check if a point editor can add a chamfer to a corner of the given item.
ARC_EDIT_MODE IncrementArcEditMode(ARC_EDIT_MODE aMode)
#define CHECK_POINT_COUNT(aPoints, aExpected)
#define CHECK_POINT_COUNT_GE(aPoints, aExpected)
CITER next(CITER it)
Definition ptree.cpp:124
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
SCH_CONDITIONS S_C
@ RECT_CENTER
@ RECT_RADIUS
@ RECT_RIGHT
@ RECT_LEFT
@ RECT_BOT
@ RECT_TOP
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:39
const int scale
Parameters that define a simple chamfer operation.
Structure to hold the necessary information in order to index a vertex on a SHAPE_POLY_SET object: th...
VECTOR2I center
int radius
VECTOR2I end
int delta
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
#define M_PI
@ TA_MOUSE_DOWN
Definition tool_event.h:70
@ TA_UNDO_REDO_POST
This event is sent after undo/redo command is performed.
Definition tool_event.h:109
@ MD_SHIFT
Definition tool_event.h:143
@ BUT_LEFT
Definition tool_event.h:132
VECTOR2I GetRotated(const VECTOR2I &aVector, const EDA_ANGLE &aAngle)
Return a new VECTOR2I that is the result of rotating aVector by aAngle.
Definition trigo.h:77
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:78
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:105
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:102
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:91
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:103
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:110
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:107
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:89
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:79
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:95
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:101
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:104
constexpr int sign(T val)
Definition util.h:145
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694
Supplemental functions for working with vectors and simple objects that interact with vectors.