KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_shape.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) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright (C) 2011 Wayne Stambaugh <[email protected]>
7 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#include "pcb_shape.h"
24
25#include <google/protobuf/any.pb.h>
26#include <magic_enum.hpp>
27
28#include <bitmaps.h>
29#include <macros.h>
30#include <pcb_edit_frame.h>
32#include <board.h>
33#include <footprint.h>
34#include <lset.h>
35#include <pad.h>
36#include <base_units.h>
37#include <trigo.h>
38#include <drc/drc_engine.h>
44#include <pcb_painter.h>
46#include <api/board/board_types.pb.h>
47#include <api/api_enums.h>
48#include <api/api_utils.h>
49#include <properties/property.h>
51
52
53namespace
54{
55struct BOARD_ELLIPSE
56{
57 double major;
58 double minor;
59 EDA_ANGLE rotation;
60 EDA_ANGLE startShift;
61};
62
63
64// SVD of a 2x2 matrix into ellipse major / minor / rotation and the arc angle shift.
65static BOARD_ELLIPSE decompose2x2( double m00, double m01, double m10, double m11 )
66{
67 const double A = m00 * m00 + m10 * m10;
68 const double B = m00 * m01 + m10 * m11;
69 const double C = m01 * m01 + m11 * m11;
70
71 const double diff = A - C;
72 const double rad = std::hypot( diff, 2.0 * B );
73 const double lambda1 = ( A + C + rad ) * 0.5;
74 const double lambda2 = ( A + C - rad ) * 0.5;
75
76 const double sigma1 = std::sqrt( std::max( 0.0, lambda1 ) );
77 const double sigma2 = std::sqrt( std::max( 0.0, lambda2 ) );
78
79 double v0x;
80 double v0y;
81
82 if( std::abs( B ) > 1e-12 )
83 {
84 v0x = lambda1 - C;
85 v0y = B;
86 }
87 else if( A >= C )
88 {
89 v0x = 1.0;
90 v0y = 0.0;
91 }
92 else
93 {
94 v0x = 0.0;
95 v0y = 1.0;
96 }
97
98 const double vn = std::hypot( v0x, v0y );
99 v0x /= vn;
100 v0y /= vn;
101
102 double u0x = 1.0;
103 double u0y = 0.0;
104
105 if( sigma1 > 1e-12 )
106 {
107 u0x = ( m00 * v0x + m01 * v0y ) / sigma1;
108 u0y = ( m10 * v0x + m11 * v0y ) / sigma1;
109 }
110
111 BOARD_ELLIPSE out;
112 out.major = sigma1;
113 out.minor = sigma2;
114 out.rotation = EDA_ANGLE( std::atan2( u0y, u0x ), RADIANS_T );
115 out.startShift = EDA_ANGLE( -std::atan2( v0y, v0x ), RADIANS_T );
116 return out;
117}
118
119
120// Board ellipse from a lib ellipse: M = R(theta) * diag(sx, sy) * R(phi) * diag(a, b).
121static BOARD_ELLIPSE decomposeBoardEllipse( const TRANSFORM_TRS& aXform, int aLibMajor, int aLibMinor,
122 const EDA_ANGLE& aLibRotation )
123{
124 const double sx = aXform.GetScaleX();
125 const double sy = aXform.GetScaleY();
126 // Lib rotation and xform rotation use opposite signs, negate to match.
127 const double theta = -aXform.GetRotate().AsRadians();
128 const double phi = aLibRotation.AsRadians();
129 const double cTheta = std::cos( theta );
130 const double sTheta = std::sin( theta );
131 const double cPhi = std::cos( phi );
132 const double sPhi = std::sin( phi );
133 const double a = aLibMajor;
134 const double b = aLibMinor;
135
136 const double m00 = a * ( sx * cTheta * cPhi - sy * sTheta * sPhi );
137 const double m01 = -b * ( sx * cTheta * sPhi + sy * sTheta * cPhi );
138 const double m10 = a * ( sx * sTheta * cPhi + sy * cTheta * sPhi );
139 const double m11 = b * ( -sx * sTheta * sPhi + sy * cTheta * cPhi );
140
141 return decompose2x2( m00, m01, m10, m11 );
142}
143
144
145// Inverse of decomposeBoardEllipse: lib ellipse from a board ellipse.
146static BOARD_ELLIPSE composeLibEllipse( const TRANSFORM_TRS& aXform, double aBoardMajor, double aBoardMinor,
147 const EDA_ANGLE& aBoardRotation )
148{
149 const double sx = aXform.GetScaleX();
150 const double sy = aXform.GetScaleY();
151 const double theta = -aXform.GetRotate().AsRadians();
152 const double beta = aBoardRotation.AsRadians();
153
154 const double li00 = std::cos( theta ) / sx;
155 const double li01 = std::sin( theta ) / sx;
156 const double li10 = -std::sin( theta ) / sy;
157 const double li11 = std::cos( theta ) / sy;
158
159 const double e00 = aBoardMajor * std::cos( beta );
160 const double e01 = -aBoardMinor * std::sin( beta );
161 const double e10 = aBoardMajor * std::sin( beta );
162 const double e11 = aBoardMinor * std::cos( beta );
163
164 return decompose2x2( li00 * e00 + li01 * e10, li00 * e01 + li01 * e11, li10 * e00 + li11 * e10,
165 li10 * e01 + li11 * e11 );
166}
167} // namespace
168
169
170PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T aItemType, SHAPE_T aShapeType ) :
171 BOARD_CONNECTED_ITEM( aParent, aItemType ),
172 EDA_SHAPE( aShapeType, pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ), FILL_T::NO_FILL ),
173 m_libStart( 0, 0 ),
174 m_libEnd( 0, 0 ),
176 m_libArcMid( 0, 0 ),
177 m_libBezierC1( 0, 0 ),
178 m_libBezierC2( 0, 0 ),
179 m_libEllipseCenter( 0, 0 ),
185 m_libShape( aShapeType )
186{
187 m_hasSolderMask = false;
188}
189
190
193 EDA_SHAPE( shapetype, pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ), FILL_T::NO_FILL ),
194 m_libStart( 0, 0 ),
195 m_libEnd( 0, 0 ),
197 m_libArcMid( 0, 0 ),
198 m_libBezierC1( 0, 0 ),
199 m_libBezierC2( 0, 0 ),
200 m_libEllipseCenter( 0, 0 ),
206 m_libShape( shapetype )
207{
208 m_hasSolderMask = false;
209}
210
211
215
216
217void PCB_SHAPE::CopyFrom( const BOARD_ITEM* aOther )
218{
219 wxCHECK( aOther && aOther->Type() == PCB_SHAPE_T, /* void */ );
220 *this = *static_cast<const PCB_SHAPE*>( aOther );
221}
222
223
224void PCB_SHAPE::Serialize( google::protobuf::Any &aContainer ) const
225{
226 using namespace kiapi::common;
227 using namespace kiapi::board::types;
228 BoardGraphicShape msg;
229
231 PackNet( msg.mutable_net() );
232 msg.mutable_id()->set_value( m_Uuid.AsStdString() );
233 msg.set_locked( IsLocked() ? types::LockedState::LS_LOCKED : types::LockedState::LS_UNLOCKED );
234
235 EDA_SHAPE::Serialize( *msg.mutable_shape(), pcbIUScale );
236
237 if( IsProxyItem() )
238 {
239 PadCustomShapeOptions* options = msg.mutable_pad_custom_shape_options();
240
241 switch( GetShape() )
242 {
243 case SHAPE_T::RECTANGLE: options->set_is_number_box( true ); break;
244 case SHAPE_T::SEGMENT: options->set_is_thermal_spoke_template( true ); break;
245 default: wxFAIL_MSG( wxT( "Unexpected proxy shape type" ) ); break;
246 }
247 }
248
249 if( FOOTPRINT* parent = GetParentFootprint() )
250 msg.mutable_parent()->set_value( parent->m_Uuid.AsStdString() );
251 else if( const BOARD* board = GetBoard() )
252 msg.mutable_parent()->set_value( board->m_Uuid.AsStdString() );
253
254 if( HasSolderMask() )
255 {
256 SolderMaskOverrides* sm = msg.mutable_solder_mask();
257 sm->set_expose_copper( true );
258
259 if( GetLocalSolderMaskMargin().has_value() )
260 sm->mutable_solder_mask_margin()->set_value_nm( GetLocalSolderMaskMargin().value() );
261 }
262
263 kiapi::common::PackCustomProperties( msg.mutable_custom_properties(), *this );
264 aContainer.PackFrom( msg );
265}
266
267
268bool PCB_SHAPE::Deserialize( const google::protobuf::Any &aContainer )
269{
270 using namespace kiapi::common;
271 using namespace kiapi::board::types;
272
273 BoardGraphicShape msg;
274
275 if( !aContainer.UnpackTo( &msg ) )
276 return false;
277
278 // Initialize everything to a known state that doesn't get touched by every
279 // codepath below, to make sure the equality operator is consistent
280 m_start = {};
281 m_end = {};
282 m_arcCenter = {};
283 m_arcMidData = {};
284 m_bezierC1 = {};
285 m_bezierC2 = {};
286 m_editState = 0;
287 m_proxyItem = false;
288 m_endsSwapped = false;
289
290 SetUuidDirect( KIID( msg.id().value() ) );
291 SetLocked( msg.locked() == types::LS_LOCKED );
293 UnpackNet( msg.net() );
294 kiapi::common::UnpackCustomProperties( msg.custom_properties(), *this );
295
296 EDA_SHAPE::Deserialize( msg.shape(), pcbIUScale );
297
298 if( msg.has_pad_custom_shape_options() )
299 {
300 switch( msg.pad_custom_shape_options().role_case() )
301 {
302 case PadCustomShapeOptions::kIsNumberBox:
304 m_proxyItem = true;
305
306 break;
307
308 case PadCustomShapeOptions::kIsThermalSpokeTemplate:
309 if( GetShape() == SHAPE_T::SEGMENT )
310 m_proxyItem = true;
311
312 break;
313
314 case PadCustomShapeOptions::ROLE_NOT_SET:
315 break;
316 }
317 }
318
319 if( msg.has_solder_mask() )
320 {
321 SetHasSolderMask( msg.solder_mask().expose_copper() );
322
323 if( msg.solder_mask().has_solder_mask_margin() )
324 SetLocalSolderMaskMargin( msg.solder_mask().solder_mask_margin().value_nm() );
325 else
327 }
328 else
329 {
330 SetHasSolderMask( false );
332 }
333
334 return true;
335}
336
337
338bool PCB_SHAPE::IsType( const std::vector<KICAD_T>& aScanTypes ) const
339{
340 if( BOARD_ITEM::IsType( aScanTypes ) )
341 return true;
342
343 bool sametype = false;
344
345 for( KICAD_T scanType : aScanTypes )
346 {
347 if( scanType == PCB_LOCATE_BOARD_EDGE_T )
348 sametype = m_layer == Edge_Cuts;
349 else if( scanType == PCB_SHAPE_LOCATE_ARC_T )
350 sametype = m_shape == SHAPE_T::ARC;
351 else if( scanType == PCB_SHAPE_LOCATE_CIRCLE_T )
352 sametype = m_shape == SHAPE_T::CIRCLE;
353 else if( scanType == PCB_SHAPE_LOCATE_RECT_T )
354 sametype = m_shape == SHAPE_T::RECTANGLE;
355 else if( scanType == PCB_SHAPE_LOCATE_SEGMENT_T )
356 sametype = m_shape == SHAPE_T::SEGMENT;
357 else if( scanType == PCB_SHAPE_LOCATE_POLY_T )
358 sametype = m_shape == SHAPE_T::POLY;
359 else if( scanType == PCB_SHAPE_LOCATE_BEZIER_T )
360 sametype = m_shape == SHAPE_T::BEZIER;
361 else if( scanType == PCB_SHAPE_LOCATE_ELLIPSE_T )
362 sametype = m_shape == SHAPE_T::ELLIPSE;
363 else if( scanType == PCB_SHAPE_LOCATE_ELLIPSE_ARC_T )
364 sametype = m_shape == SHAPE_T::ELLIPSE_ARC;
365
366 if( sametype )
367 return true;
368 }
369
370 return false;
371}
372
373
375{
376 // Only board-level copper shapes are connectable
377 return IsOnCopperLayer() && !GetParentFootprint();
378}
379
380
382{
383 BOARD_ITEM::SetLayer( aLayer );
384
385 if( !IsOnCopperLayer() )
386 SetNetCode( -1 );
387}
388
389
391{
392 int margin = 0;
393
394 if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine
395 && GetBoard()->GetDesignSettings().m_DRCEngine->HasRulesForConstraintType(
397 {
398 DRC_CONSTRAINT constraint;
399 std::shared_ptr<DRC_ENGINE> drcEngine = GetBoard()->GetDesignSettings().m_DRCEngine;
400
401 constraint = drcEngine->EvalRules( SOLDER_MASK_EXPANSION_CONSTRAINT, this, nullptr, m_layer );
402
403 if( constraint.m_Value.HasOpt() )
404 margin = constraint.m_Value.Opt();
405 }
406 else if( m_solderMaskMargin.has_value() )
407 {
408 margin = m_solderMaskMargin.value();
409 }
410 else if( const BOARD* board = GetBoard() )
411 {
412 margin = board->GetDesignSettings().m_SolderMaskExpansion;
413 }
414
415 // Ensure the resulting mask opening has a non-negative size
416 if( margin < 0 && !IsSolidFill() )
417 margin = std::max( margin, -GetWidth() / 2 );
418
419 return margin;
420}
421
422
424{
425 if( aLayer == m_layer )
426 {
427 return true;
428 }
429
431 && ( ( aLayer == F_Mask && m_layer == F_Cu )
432 || ( aLayer == B_Mask && m_layer == B_Cu ) ) )
433 {
434 return true;
435 }
436
437 return false;
438}
439
440
442{
443 LSET layermask( { m_layer } );
444
445 if( m_hasSolderMask )
446 {
447 if( layermask.test( F_Cu ) )
448 layermask.set( F_Mask );
449
450 if( layermask.test( B_Cu ) )
451 layermask.set( B_Mask );
452 }
453
454 return layermask;
455}
456
457
458void PCB_SHAPE::SetLayerSet( const LSET& aLayerSet )
459{
460 aLayerSet.RunOnLayers(
461 [&]( PCB_LAYER_ID layer )
462 {
463 if( IsCopperLayer( layer ) )
464 SetLayer( layer );
465 else if( IsSolderMaskLayer( layer ) )
466 SetHasSolderMask( true );
467 } );
468}
469
470
471std::vector<VECTOR2I> PCB_SHAPE::GetConnectionPoints() const
472{
473 std::vector<VECTOR2I> ret;
474
475 // For filled shapes, we may as well use a centroid
476 if( IsSolidFill() )
477 {
478 ret.emplace_back( GetCenter() );
479 return ret;
480 }
481
482 switch( m_shape )
483 {
484 case SHAPE_T::CIRCLE:
485 {
486 const CIRCLE circle( GetCenter(), GetRadius() );
487
488 for( const TYPED_POINT2I& pt : KIGEOM::GetCircleKeyPoints( circle, false ) )
489 ret.emplace_back( pt.m_point );
490
491 break;
492 }
493
494 case SHAPE_T::ARC:
495 ret.emplace_back( GetArcMid() );
497 case SHAPE_T::SEGMENT:
498 case SHAPE_T::BEZIER:
499 ret.emplace_back( GetStart() );
500 ret.emplace_back( GetEnd() );
501 break;
502
503 case SHAPE_T::POLY:
504 for( auto iter = GetPolyShape().CIterate(); iter; ++iter )
505 ret.emplace_back( *iter );
506
507 break;
508
510 for( const VECTOR2I& pt : GetRectCorners() )
511 ret.emplace_back( pt );
512
513 break;
514
515 case SHAPE_T::ELLIPSE:
516 {
517 const double phi = GetEllipseRotation().AsRadians();
518 const double cosPhi = std::cos( phi );
519 const double sinPhi = std::sin( phi );
520 const int a = GetEllipseMajorRadius();
521 const int b = GetEllipseMinorRadius();
522 const VECTOR2I c = GetEllipseCenter();
523
524 ret.emplace_back( c + VECTOR2I( KiROUND( a * cosPhi ), KiROUND( a * sinPhi ) ) );
525 ret.emplace_back( c + VECTOR2I( KiROUND( -a * cosPhi ), KiROUND( -a * sinPhi ) ) );
526 ret.emplace_back( c + VECTOR2I( KiROUND( -b * sinPhi ), KiROUND( b * cosPhi ) ) );
527 ret.emplace_back( c + VECTOR2I( KiROUND( b * sinPhi ), KiROUND( -b * cosPhi ) ) );
528 break;
529 }
530
532 {
533 const double a = GetEllipseMajorRadius();
534 const double b = GetEllipseMinorRadius();
535 const double phi = GetEllipseRotation().AsRadians();
536 const double cosPhi = std::cos( phi );
537 const double sinPhi = std::sin( phi );
538 const VECTOR2I c = GetEllipseCenter();
539
540 auto eval = [&]( double theta ) -> VECTOR2I
541 {
542 const double lx = a * std::cos( theta );
543 const double ly = b * std::sin( theta );
544 return c + VECTOR2I( KiROUND( lx * cosPhi - ly * sinPhi ), KiROUND( lx * sinPhi + ly * cosPhi ) );
545 };
546
547 double thetaStart = GetEllipseStartAngle().AsRadians();
548 double thetaEnd = GetEllipseEndAngle().AsRadians();
549
550 if( thetaEnd < thetaStart )
551 thetaEnd += 2.0 * M_PI;
552
553 ret.emplace_back( eval( thetaStart ) );
554 ret.emplace_back( eval( thetaEnd ) );
555 ret.emplace_back( eval( 0.5 * ( thetaStart + thetaEnd ) ) );
556 break;
557 }
558
561 break;
562 }
563
564 return ret;
565}
566
567
569{
570 // Force update; we don't bother to propagate damage from all the things that might
571 // knock-out parts of our hatching.
572 m_hatchingDirty = true;
573
575}
576
577
579{
580 SHAPE_POLY_SET knockouts;
581 PCB_LAYER_ID layer = GetLayer();
582 BOX2I bbox = GetBoundingBox();
583 int maxError = ARC_LOW_DEF;
584
585 auto knockoutItem =
586 [&]( BOARD_ITEM* item )
587 {
588 int margin = GetHatchLineSpacing() / 2;
589
590 if( item->Type() == PCB_TEXTBOX_T )
591 margin = 0;
592
593 item->TransformShapeToPolygon( knockouts, layer, margin, maxError, ERROR_OUTSIDE );
594 };
595
596 for( BOARD_ITEM* item : GetBoard()->Drawings() )
597 {
598 if( item == this )
599 continue;
600
601 if( item->Type() == PCB_FIELD_T
602 || item->Type() == PCB_TEXT_T
603 || item->Type() == PCB_TEXTBOX_T
604 || item->Type() == PCB_SHAPE_T )
605 {
606 if( item->GetLayer() == layer && item->GetBoundingBox().Intersects( bbox ) )
607 knockoutItem( item );
608 }
609 }
610
611 for( FOOTPRINT* footprint : GetBoard()->Footprints() )
612 {
613 if( footprint == GetParentFootprint() )
614 continue;
615
616 // GetCourtyard() returns the front courtyard for any non-back layer, so only knock it
617 // out when the hatched shape actually lives on a courtyard layer.
618 if( layer == F_CrtYd || layer == B_CrtYd )
619 knockouts.Append( footprint->GetCourtyard( layer ) );
620
621 // Knockout footprint fields
622 footprint->RunOnChildren(
623 [&]( BOARD_ITEM* item )
624 {
625 if( ( item->Type() == PCB_FIELD_T || item->Type() == PCB_SHAPE_T )
626 && item->GetLayer() == layer
627 && !( item->Type() == PCB_FIELD_T && !static_cast<PCB_FIELD*>(item)->IsVisible() )
628 && item->GetBoundingBox().Intersects( bbox ) )
629 {
630 knockoutItem( item );
631 }
632 },
634 }
635
636 return knockouts;
637}
638
639
640static double fpScaleLinear( const FOOTPRINT* aFp )
641{
642 if( !aFp )
643 return 1.0;
644
645 const TRANSFORM_TRS& xform = aFp->GetTransform();
646 return ( xform.GetScaleX() + xform.GetScaleY() ) * 0.5;
647}
648
649
651{
652 if( GetParent() && GetParent()->Type() == PCB_PAD_T )
653 return nullptr;
654
655 return GetParentFootprint();
656}
657
658
660{
661 // Clamp negative widths to zero. They mean something in eeschema but break
662 // plotters and exporters here.
663 const int lib = std::max( EDA_SHAPE::GetWidth(), 0 );
664 const double s = fpScaleLinear( transformFp() );
665 return s == 1.0 ? lib : KiROUND( lib * s );
666}
667
668
669void PCB_SHAPE::StyleFromSettings( const BOARD_DESIGN_SETTINGS& settings, bool aCheckSide )
670{
671 m_stroke.SetWidth( settings.GetLineThickness( GetLayer() ) );
672}
673
674
676{
677 // For some shapes return the visual center, but for not filled polygonal shapes,
678 // the center is usually far from the shape: a point on the outline is better
679
680 switch( m_shape )
681 {
682 case SHAPE_T::CIRCLE:
683 if( !IsAnyFill() )
684 return VECTOR2I( GetCenter().x + GetRadius(), GetCenter().y );
685 else
686 return GetCenter();
687
689 if( !IsAnyFill() )
690 return GetStart();
691 else
692 return GetCenter();
693
694 case SHAPE_T::POLY:
695 if( !IsAnyFill() )
696 {
697 VECTOR2I pos = GetPolyShape().Outline(0).CPoint(0);
698 return VECTOR2I( pos.x, pos.y );
699 }
700 else
701 {
702 return GetCenter();
703 }
704
705 case SHAPE_T::ARC:
706 return GetArcMid();
707
708 case SHAPE_T::BEZIER:
709 return GetStart();
710
711 default:
712 return GetCenter();
713 }
714}
715
716
717std::vector<VECTOR2I> PCB_SHAPE::GetCorners() const
718{
719 std::vector<VECTOR2I> pts;
720
722 {
723 pts = GetRectCorners();
724 }
725 else if( GetShape() == SHAPE_T::POLY )
726 {
727 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
728 {
729 for( const VECTOR2I& pt : GetPolyShape().Outline( ii ).CPoints() )
730 pts.emplace_back( pt );
731 }
732 }
733 else
734 {
736 }
737
738 while( pts.size() < 4 )
739 pts.emplace_back( pts.back() + VECTOR2I( 10, 10 ) );
740
741 return pts;
742}
743
744
745void PCB_SHAPE::Move( const VECTOR2I& aMoveVector )
746{
747 move( aMoveVector );
749}
750
751
752void PCB_SHAPE::Scale( double aScale )
753{
754 scale( aScale );
755}
756
757
759{
761 {
762 VECTOR2I start = GetStart();
763 VECTOR2I end = GetEnd();
764
765 BOX2I rect( start, end - start );
766 rect.Normalize();
767
768 SetStart( rect.GetPosition() );
769 SetEnd( rect.GetEnd() );
770 }
771 else if( m_shape == SHAPE_T::POLY )
772 {
773 auto horizontal =
774 []( const SEG& seg )
775 {
776 return seg.A.y == seg.B.y;
777 };
778
779 auto vertical =
780 []( const SEG& seg )
781 {
782 return seg.A.x == seg.B.x;
783 };
784
785 // Convert a poly back to a rectangle if appropriate
786 if( GetPolyShape().OutlineCount() == 1 && GetPolyShape().Outline( 0 ).SegmentCount() == 4 )
787 {
788 SHAPE_LINE_CHAIN& outline = GetPolyShape().Outline( 0 );
789
790 if( horizontal( outline.Segment( 0 ) )
791 && vertical( outline.Segment( 1 ) )
792 && horizontal( outline.Segment( 2 ) )
793 && vertical( outline.Segment( 3 ) ) )
794 {
796 SetStart( VECTOR2I( std::min( outline.Segment( 0 ).A.x, outline.Segment( 0 ).B.x ),
797 std::min( outline.Segment( 1 ).A.y, outline.Segment( 1 ).B.y ) ) );
798 SetEnd( VECTOR2I( std::max( outline.Segment( 0 ).A.x, outline.Segment( 0 ).B.x ),
799 std::max( outline.Segment( 1 ).A.y, outline.Segment( 1 ).B.y ) ) );
800 }
801 else if( vertical( outline.Segment( 0 ) )
802 && horizontal( outline.Segment( 1 ) )
803 && vertical( outline.Segment( 2 ) )
804 && horizontal( outline.Segment( 3 ) ) )
805 {
807 SetStart( VECTOR2I( std::min( outline.Segment( 1 ).A.x, outline.Segment( 1 ).B.x ),
808 std::min( outline.Segment( 0 ).A.y, outline.Segment( 0 ).B.y ) ) );
809 SetEnd( VECTOR2I( std::max( outline.Segment( 1 ).A.x, outline.Segment( 1 ).B.x ),
810 std::max( outline.Segment( 0 ).A.y, outline.Segment( 0 ).B.y ) ) );
811 }
812 }
813 }
814}
815
816
818{
820 {
821 VECTOR2I libStart = GetLibraryStart();
822 VECTOR2I libEnd = GetLibraryEnd();
823
824 if( ( libStart.x > libEnd.x ) || ( libStart.x == libEnd.x && libStart.y < libEnd.y ) )
825 {
826 VECTOR2I s = GetStart();
827 VECTOR2I e = GetEnd();
828 SetStart( e );
829 SetEnd( s );
830 }
831 }
832 else
833 Normalize();
834}
835
836
837void PCB_SHAPE::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
838{
839 rotate( aRotCentre, aAngle );
841}
842
843
844void PCB_SHAPE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
845{
846 const FOOTPRINT* fp = transformFp();
847
848 TRANSFORM_TRS xform;
849 bool mirrorLib = false;
850
851 if( fp )
852 {
853 xform = fp->GetTransform();
854 mirrorLib = true;
855 }
856 else if( GetParent() && GetParent()->Type() == PCB_PAD_T && m_shape != m_libShape )
857 {
858 double sx = 1.0, sy = 1.0;
859 static_cast<const PAD*>( static_cast<const BOARD_ITEM*>( GetParent() ) )->GetPrimitiveLibScale( sx, sy );
860 xform.SetScale( sx, sy );
861 mirrorLib = true;
862 }
863
864 if( mirrorLib
867 {
868 const VECTOR2I libCenter = xform.InverseApply( aCentre );
869
870 auto mirrorPt =
871 [&]( VECTOR2I& p )
872 {
873 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
874 p.x = 2 * libCenter.x - p.x;
875 else
876 p.y = 2 * libCenter.y - p.y;
877 };
878
879 if( m_libShape == SHAPE_T::ARC )
880 {
881 mirrorPt( m_libStart );
882 mirrorPt( m_libEnd );
883 mirrorPt( m_libArcMid );
884 std::swap( m_libStart, m_libEnd );
885 }
886 else
887 {
888 mirrorPt( m_libStart );
889 mirrorPt( m_libEnd );
890 }
891
893 {
894 mirrorPt( m_libBezierC1 );
895 mirrorPt( m_libBezierC2 );
896 }
897
899 {
900 for( auto it = m_libPoly.IterateWithHoles(); it; it++ )
901 {
902 VECTOR2I p = *it;
903 mirrorPt( p );
904 m_libPoly.SetVertex( it.GetIndex(), p );
905 }
906 }
907
909 rebakeFromTransform( xform );
910 return;
911 }
912
913 if( mirrorLib && ( m_libShape == SHAPE_T::ELLIPSE || m_libShape == SHAPE_T::ELLIPSE_ARC ) )
914 {
915 const VECTOR2I libCenter = xform.InverseApply( aCentre );
916
917 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
918 m_libEllipseCenter.x = 2 * libCenter.x - m_libEllipseCenter.x;
919 else
920 m_libEllipseCenter.y = 2 * libCenter.y - m_libEllipseCenter.y;
921
923
924 const EDA_ANGLE oldStart = m_libEllipseStartAngle;
925 const EDA_ANGLE oldEnd = m_libEllipseEndAngle;
926
927 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
928 {
930 m_libEllipseEndAngle = ANGLE_180 - oldStart;
931 }
932 else
933 {
934 m_libEllipseStartAngle = -oldEnd;
935 m_libEllipseEndAngle = -oldStart;
936 }
937
939 rebakeFromTransform( xform );
940 return;
941 }
942
943 flip( aCentre, aFlipDirection );
944
947}
948
949
950void PCB_SHAPE::SetStart( const VECTOR2I& aStart )
951{
952 EDA_SHAPE::SetStart( aStart );
954}
955
956
957void PCB_SHAPE::SetEnd( const VECTOR2I& aEnd )
958{
959 EDA_SHAPE::SetEnd( aEnd );
961}
962
963
964void PCB_SHAPE::SetCornerRadius( int aCornerRadius )
965{
966 EDA_SHAPE::SetCornerRadius( aCornerRadius );
968}
969
970
971void PCB_SHAPE::OnFootprintRescaled( double aRatioX, double aRatioY, double aLinearFactor, const VECTOR2I& aAnchor,
972 const EDA_ANGLE& aParentRotate )
973{
975}
976
977
978void PCB_SHAPE::SetWidth( int aWidth )
979{
980 const double s = fpScaleLinear( transformFp() );
981
982 if( s == 1.0 )
983 m_stroke.SetWidth( aWidth );
984 else
985 m_stroke.SetWidth( KiROUND( aWidth / s ) );
986
987 m_hatchingDirty = true;
988}
989
990
992{
993 m_stroke.SetWidth( aWidth );
994 m_hatchingDirty = true;
995}
996
997
999{
1001 s.SetWidth( GetWidth() );
1002 return s;
1003}
1004
1005
1007{
1008 m_stroke = aStroke;
1009 SetWidth( aStroke.GetWidth() );
1010}
1011
1012
1013void PCB_SHAPE::RebakeWithScale( double aScaleX, double aScaleY )
1014{
1015 TRANSFORM_TRS xform;
1016 xform.SetScale( aScaleX, aScaleY );
1017 rebakeFromTransform( xform );
1018}
1019
1020
1022{
1023 const FOOTPRINT* fp = transformFp();
1024
1025 if( !fp )
1026 return;
1027
1029}
1030
1031
1033{
1034 const bool nonUniform = xform.GetScaleX() != xform.GetScaleY();
1035
1037 {
1038 VECTOR2I libCenter = m_libStart;
1039 int libRadius = ( m_libEnd - m_libStart ).EuclideanNorm();
1040 VECTOR2I newCenter = xform.Apply( libCenter );
1041
1042 if( nonUniform )
1043 {
1044 int majorRadius = std::abs( KiROUND( libRadius * xform.GetScaleX() ) );
1045 int minorRadius = std::abs( KiROUND( libRadius * xform.GetScaleY() ) );
1046
1047 EDA_ANGLE rotation = -xform.GetRotate();
1048
1049 if( minorRadius > majorRadius )
1050 {
1051 std::swap( majorRadius, minorRadius );
1052 rotation += EDA_ANGLE( 90.0, DEGREES_T );
1053 }
1054
1056 SetEllipseCenter( newCenter );
1057 SetEllipseMajorRadius( majorRadius );
1058 SetEllipseMinorRadius( minorRadius );
1059 SetEllipseRotation( rotation );
1060 }
1061 else
1062 {
1064 EDA_SHAPE::SetStart( newCenter );
1065 EDA_SHAPE::SetEnd( xform.Apply( m_libEnd ) );
1066 }
1067
1068 return;
1069 }
1070
1071 if( m_libShape == SHAPE_T::ARC )
1072 {
1074 int libRadius = ( m_libStart - libCenter ).EuclideanNorm();
1075 VECTOR2I newCenter = xform.Apply( libCenter );
1076
1077 if( nonUniform )
1078 {
1079 int majorRadius = std::abs( KiROUND( libRadius * xform.GetScaleX() ) );
1080 int minorRadius = std::abs( KiROUND( libRadius * xform.GetScaleY() ) );
1081
1082 EDA_ANGLE rotation = -xform.GetRotate();
1083 EDA_ANGLE startAngle( VECTOR2D( m_libStart - libCenter ) );
1084 EDA_ANGLE midAngle( VECTOR2D( m_libArcMid - libCenter ) );
1085 EDA_ANGLE endAngle( VECTOR2D( m_libEnd - libCenter ) );
1086
1087 auto wrap = []( EDA_ANGLE a, EDA_ANGLE base )
1088 {
1089 while( a < base )
1090 a += ANGLE_360;
1091 return a;
1092 };
1093
1094 if( wrap( midAngle, startAngle ) > wrap( endAngle, startAngle ) )
1095 std::swap( startAngle, endAngle );
1096
1097 while( endAngle < startAngle )
1098 endAngle += ANGLE_360;
1099
1100 if( minorRadius > majorRadius )
1101 {
1102 std::swap( majorRadius, minorRadius );
1103 rotation += EDA_ANGLE( 90.0, DEGREES_T );
1104 startAngle -= EDA_ANGLE( 90.0, DEGREES_T );
1105 endAngle -= EDA_ANGLE( 90.0, DEGREES_T );
1106 }
1107
1109 SetEllipseCenter( newCenter );
1110 SetEllipseMajorRadius( majorRadius );
1111 SetEllipseMinorRadius( minorRadius );
1112 SetEllipseRotation( rotation );
1113 SetEllipseStartAngle( startAngle );
1114 SetEllipseEndAngle( endAngle );
1115 }
1116 else
1117 {
1120 }
1121
1122 return;
1123 }
1124
1126 {
1127 // The linear transform preserves ellipse-ness only when scale is uniform
1128 // or the lib ellipse's axes are aligned with the scale axes (cardinal lib
1129 // rotation). Otherwise the result is a sheared shape that no standard
1130 // ellipse can represent; tessellate to POLY in that case.
1131 const bool keepNative = xform.IsUniformScale() || m_libEllipseRotation.IsCardinal();
1132
1133 if( keepNative )
1134 {
1137
1138 if( xform.IsUniformScale() )
1139 {
1140 double absScale = std::abs( xform.GetScaleX() );
1144
1146 {
1149 }
1150 }
1151 else
1152 {
1153 BOARD_ELLIPSE be = decomposeBoardEllipse( xform, m_libEllipseMajorRadius,
1156
1159 EDA_SHAPE::SetEllipseRotation( be.rotation );
1160
1162 {
1165 }
1166 }
1167 }
1168 else
1169 {
1170 const bool isArc = ( m_libShape == SHAPE_T::ELLIPSE_ARC );
1171
1172 std::unique_ptr<SHAPE_ELLIPSE> libEllipse;
1173
1174 if( isArc )
1175 {
1176 libEllipse = std::make_unique<SHAPE_ELLIPSE>( m_libEllipseCenter, m_libEllipseMajorRadius,
1179 }
1180 else
1181 {
1182 libEllipse = std::make_unique<SHAPE_ELLIPSE>( m_libEllipseCenter, m_libEllipseMajorRadius,
1184 }
1185
1186 SHAPE_LINE_CHAIN chain = libEllipse->ConvertToPolyline( getMaxError() );
1187
1189 SHAPE_POLY_SET& poly = GetPolyShape();
1190 poly.RemoveAllContours();
1191 poly.NewOutline();
1192
1193 for( int ii = 0; ii < chain.PointCount(); ++ii )
1194 poly.Append( xform.Apply( chain.CPoint( ii ) ) );
1195
1196 poly.Outline( 0 ).SetClosed( !isArc );
1197 }
1198
1199 return;
1200 }
1201
1203 {
1204 const VECTOR2I c1 = xform.Apply( m_libStart );
1205 const VECTOR2I c2 = xform.Apply( VECTOR2I( m_libEnd.x, m_libStart.y ) );
1206 const VECTOR2I c3 = xform.Apply( m_libEnd );
1207 const VECTOR2I c4 = xform.Apply( VECTOR2I( m_libStart.x, m_libEnd.y ) );
1208
1209 if( xform.GetRotate().IsCardinal()
1210 && ( m_libCornerRadius == 0 || xform.IsUniformScale() ) )
1211 {
1213 BOX2I bbox( c1, VECTOR2I( 0, 0 ) );
1214 bbox.Merge( c2 );
1215 bbox.Merge( c3 );
1216 bbox.Merge( c4 );
1218 EDA_SHAPE::SetEnd( bbox.GetEnd() );
1219
1221 }
1222 else
1223 {
1225 SHAPE_POLY_SET& poly = GetPolyShape();
1226 poly.RemoveAllContours();
1227
1228 if( m_libCornerRadius > 0 )
1229 {
1230 SHAPE_POLY_SET rounded;
1231 VECTOR2I libCenter = ( m_libStart + m_libEnd ) / 2;
1232 VECTOR2I libSize( std::abs( m_libEnd.x - m_libStart.x ), std::abs( m_libEnd.y - m_libStart.y ) );
1233
1234 TransformRoundChamferedRectToPolygon( rounded, libCenter, libSize, ANGLE_0, m_cornerRadius, 0.0, 0, 0,
1236
1237 poly.NewOutline();
1238
1239 for( int ii = 0; ii < rounded.Outline( 0 ).PointCount(); ++ii )
1240 poly.Append( xform.Apply( rounded.Outline( 0 ).CPoint( ii ) ) );
1241 }
1242 else
1243 {
1244 poly.NewOutline();
1245 poly.Append( c1 );
1246 poly.Append( c2 );
1247 poly.Append( c3 );
1248 poly.Append( c4 );
1249 }
1250
1251 EDA_SHAPE::SetStart( c1 );
1252 EDA_SHAPE::SetEnd( c3 );
1253 }
1254
1255 return;
1256 }
1257
1258 if( m_libShape == SHAPE_T::POLY )
1259 {
1260 SHAPE_POLY_SET& poly = GetPolyShape();
1261 poly = m_libPoly;
1262
1263 for( auto it = poly.IterateWithHoles(); it; it++ )
1264 poly.SetVertex( it.GetIndex(), xform.Apply( *it ) );
1265
1266 // m_libStart/m_libEnd are not seeded for POLY, skip the start/end fall-through below.
1267 return;
1268 }
1269
1271 EDA_SHAPE::SetEnd( xform.Apply( m_libEnd ) );
1272
1274 {
1278 }
1279}
1280
1281
1283{
1284 if( m_libShape == SHAPE_T::ARC )
1285 return m_libArcMid;
1286
1287 if( const FOOTPRINT* fp = transformFp() )
1288 return fp->GetTransform().InverseApply( GetArcMid() );
1289
1290 return GetArcMid();
1291}
1292
1293
1295{
1296 if( GetParent() && GetParent()->Type() == PCB_PAD_T )
1297 return m_libBezierC1;
1298
1299 if( const FOOTPRINT* fp = transformFp() )
1300 return fp->GetTransform().InverseApply( GetBezierC1() );
1301
1302 return GetBezierC1();
1303}
1304
1305
1307{
1308 if( GetParent() && GetParent()->Type() == PCB_PAD_T )
1309 return m_libBezierC2;
1310
1311 if( const FOOTPRINT* fp = transformFp() )
1312 return fp->GetTransform().InverseApply( GetBezierC2() );
1313
1314 return GetBezierC2();
1315}
1316
1317
1319{
1320 if( GetParent() && GetParent()->Type() == PCB_PAD_T )
1321 return m_libPoly;
1322
1324
1325 if( const FOOTPRINT* fp = transformFp() )
1326 {
1327 const TRANSFORM_TRS& xform = fp->GetTransform();
1328
1329 for( auto it = poly.IterateWithHoles(); it; it++ )
1330 poly.SetVertex( it.GetIndex(), xform.InverseApply( *it ) );
1331 }
1332
1333 return poly;
1334}
1335
1336
1337void PCB_SHAPE::SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
1338{
1339 EDA_SHAPE::SetArcGeometry( aStart, aMid, aEnd );
1340 syncLibCoords();
1341}
1342
1343
1345{
1347 syncLibCoords();
1348}
1349
1350
1352{
1354 syncLibCoords();
1355}
1356
1357
1359{
1360 EDA_SHAPE::SetPolyShape( aShape );
1361 syncLibCoords();
1362}
1363
1364
1366{
1368 syncLibCoords();
1369}
1370
1371
1377
1378
1384
1385
1391
1392
1398
1399
1405
1406
1408{
1409 TRANSFORM_TRS xform;
1410 bool hasXform = false;
1411
1412 if( GetParent() && GetParent()->Type() == PCB_PAD_T )
1413 {
1414 double sx = 1.0, sy = 1.0;
1415 static_cast<const PAD*>( static_cast<const BOARD_ITEM*>( GetParent() ) )->GetPrimitiveLibScale( sx, sy );
1416 xform.SetScale( sx, sy );
1417 hasXform = ( sx != 1.0 || sy != 1.0 );
1418 }
1419 else if( const FOOTPRINT* fp = transformFp() )
1420 {
1421 xform = fp->GetTransform();
1422 hasXform = true;
1423 }
1424
1426 {
1427 if( hasXform )
1428 {
1430
1431 if( xform.IsUniformScale() )
1432 {
1433 double invScale = 1.0 / std::abs( xform.GetScaleX() );
1439 }
1440 else
1441 {
1442 // Non-uniform scale shears the ellipse, so invert the whole board ellipse to lib.
1443 BOARD_ELLIPSE le = composeLibEllipse( xform, GetEllipseMajorRadius(), GetEllipseMinorRadius(),
1445 m_libEllipseMajorRadius = KiROUND( le.major );
1446 m_libEllipseMinorRadius = KiROUND( le.minor );
1447 m_libEllipseRotation = le.rotation;
1448 m_libEllipseStartAngle = GetEllipseStartAngle() + le.startShift;
1449 m_libEllipseEndAngle = GetEllipseEndAngle() + le.startShift;
1450 }
1451 }
1452 else
1453 {
1460 }
1461
1462 return;
1463 }
1464
1465 if( m_shape == SHAPE_T::POLY )
1466 {
1468
1469 if( hasXform )
1470 {
1471 for( auto it = m_libPoly.IterateWithHoles(); it; it++ )
1472 m_libPoly.SetVertex( it.GetIndex(), xform.InverseApply( *it ) );
1473 }
1474
1475 return;
1476 }
1477
1478 if( hasXform )
1479 {
1480 m_libStart = xform.InverseApply( GetStart() );
1481 m_libEnd = xform.InverseApply( GetEnd() );
1482
1484 {
1485 // We assume xform is uniform scale. If not, we should have been turned into a
1486 // polygon.
1488 }
1489
1490 if( m_shape == SHAPE_T::ARC )
1491 m_libArcMid = xform.InverseApply( GetArcMid() );
1492
1493 if( m_shape == SHAPE_T::BEZIER )
1494 {
1497 }
1498 }
1499 else
1500 {
1501 m_libStart = GetStart();
1502 m_libEnd = GetEnd();
1503
1506
1507 if( m_shape == SHAPE_T::ARC )
1509
1510 if( m_shape == SHAPE_T::BEZIER )
1511 {
1514 }
1515 }
1516}
1517
1518
1519void PCB_SHAPE::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
1520{
1521 flip( aCentre, aFlipDirection );
1522}
1523
1524
1525void PCB_SHAPE::SetIsProxyItem( bool aIsProxy )
1526{
1527 PAD* parentPad = nullptr;
1528
1529 if( GetBoard() && GetBoard()->IsFootprintHolder() )
1530 {
1531 for( FOOTPRINT* fp : GetBoard()->Footprints() )
1532 {
1533 for( PAD* pad : fp->Pads() )
1534 {
1535 if( pad->IsEntered() )
1536 {
1537 parentPad = pad;
1538 break;
1539 }
1540 }
1541 }
1542 }
1543
1544 if( aIsProxy && !m_proxyItem )
1545 {
1546 if( GetShape() == SHAPE_T::SEGMENT )
1547 {
1548 if( parentPad && parentPad->GetLocalThermalSpokeWidthOverride().has_value() )
1549 SetWidth( parentPad->GetLocalThermalSpokeWidthOverride().value() );
1550 else
1552 }
1553 else
1554 {
1555 SetWidth( 1 );
1556 }
1557 }
1558 else if( m_proxyItem && !aIsProxy )
1559 {
1561 }
1562
1563 m_proxyItem = aIsProxy;
1564}
1565
1566
1567double PCB_SHAPE::ViewGetLOD( int aLayer, const KIGFX::VIEW* aView ) const
1568{
1569 KIGFX::PCB_PAINTER& painter = static_cast<KIGFX::PCB_PAINTER&>( *aView->GetPainter() );
1570 KIGFX::PCB_RENDER_SETTINGS& renderSettings = *painter.GetSettings();
1571
1572 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
1573 {
1574 // Hide shadow if the main layer is not shown
1575 if( !aView->IsLayerVisibleCached( m_layer ) )
1576 return LOD_HIDE;
1577
1578 // Hide shadow on dimmed tracks
1579 if( renderSettings.GetHighContrast() )
1580 {
1581 if( m_layer != renderSettings.GetPrimaryHighContrastLayer() )
1582 return LOD_HIDE;
1583 }
1584 }
1585
1586 if( aLayer == LAYER_CONSTRAINT_SHADOW )
1587 {
1588 // Shadow always appended gate draw here on live constrained-item set not in
1589 // ViewGetLayers which caches
1590 if( !renderSettings.GetConstrainedItems().count( m_Uuid ) )
1591 return LOD_HIDE;
1592
1593 if( !aView->IsLayerVisibleCached( m_layer ) )
1594 return LOD_HIDE;
1595
1596 if( renderSettings.GetHighContrast() && m_layer != renderSettings.GetPrimaryHighContrastLayer() )
1597 return LOD_HIDE;
1598 }
1599
1600 if( FOOTPRINT* parent = GetParentFootprint() )
1601 {
1602 PCB_LAYER_ID checkLayer = m_layer;
1603
1604 if( !IsFrontLayer( checkLayer ) && !IsBackLayer( checkLayer ) )
1605 checkLayer = parent->GetLayer();
1606
1607 if( IsFrontLayer( checkLayer ) && !aView->IsLayerVisibleCached( LAYER_FOOTPRINTS_FR ) )
1608 return LOD_HIDE;
1609
1610 if( IsBackLayer( checkLayer ) && !aView->IsLayerVisibleCached( LAYER_FOOTPRINTS_BK ) )
1611 return LOD_HIDE;
1612 }
1613
1614 return LOD_SHOW;
1615}
1616
1617
1618std::vector<int> PCB_SHAPE::ViewGetLayers() const
1619{
1620 std::vector<int> layers;
1621 layers.reserve( 5 );
1622
1623 layers.push_back( GetLayer() );
1624
1625 if( IsOnCopperLayer() )
1626 {
1627 layers.push_back( GetNetnameLayer( GetLayer() ) );
1628
1629 if( m_hasSolderMask )
1630 {
1631 if( m_layer == F_Cu )
1632 layers.push_back( F_Mask );
1633 else if( m_layer == B_Cu )
1634 layers.push_back( B_Mask );
1635 }
1636 }
1637
1639 layers.push_back( LAYER_LOCKED_ITEM_SHADOW );
1640
1641 // Always advertise constraint-shadow layer ViewGetLOD gates draw by constrained state
1642 layers.push_back( LAYER_CONSTRAINT_SHADOW );
1643
1644 return layers;
1645}
1646
1647
1648void PCB_SHAPE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
1649{
1650 if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
1651 {
1652 if( FOOTPRINT* parent = GetParentFootprint() )
1653 aList.emplace_back( _( "Footprint" ), parent->GetReference() );
1654 }
1655
1656 aList.emplace_back( _( "Type" ), _( "Drawing" ) );
1657
1658 if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
1659 aList.emplace_back( _( "Status" ), _( "Locked" ) );
1660
1661 ShapeGetMsgPanelInfo( aFrame, aList );
1662
1663 aList.emplace_back( _( "Layer" ), GetLayerName() );
1664
1665 if( IsOnCopperLayer() )
1666 {
1667 if( GetNetCode() > 0 ) // Only graphics connected to a net have a netcode > 0
1668 aList.emplace_back( _( "Net" ), GetNetname() );
1669 }
1670}
1671
1672
1673wxString PCB_SHAPE::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
1674{
1675 FOOTPRINT* parentFP = GetParentFootprint();
1676
1677 // Don't report parent footprint info from footprint editor, viewer, etc.
1678 if( GetBoard() && GetBoard()->GetBoardUse() == BOARD_USE::FPHOLDER )
1679 parentFP = nullptr;
1680
1681 if( IsOnCopperLayer() )
1682 {
1683 if( parentFP )
1684 {
1685 return wxString::Format( _( "%s %s of %s on %s" ),
1687 GetNetnameMsg(),
1688 parentFP->GetReference(),
1689 GetLayerName() );
1690 }
1691 else
1692 {
1693 return wxString::Format( _( "%s %s on %s" ),
1695 GetNetnameMsg(),
1696 GetLayerName() );
1697 }
1698 }
1699 else
1700 {
1701 if( parentFP )
1702 {
1703 return wxString::Format( _( "%s of %s on %s" ),
1705 parentFP->GetReference(),
1706 GetLayerName() );
1707 }
1708 else
1709 {
1710 return wxString::Format( _( "%s on %s" ),
1712 GetLayerName() );
1713 }
1714 }
1715}
1716
1717
1719{
1720 if( GetParentFootprint() )
1722 else
1724}
1725
1726
1728{
1729 return new PCB_SHAPE( *this );
1730}
1731
1732
1734{
1735 BOX2I return_box = EDA_ITEM::ViewBBox();
1736
1737 // Inflate the bounding box by just a bit more for safety.
1738 return_box.Inflate( GetWidth() );
1739
1740 return return_box;
1741}
1742
1743
1745{
1746 BOX2I bbox = getBoundingBox();
1747 BOX2I endingsBBox;
1748
1749 if( GetLineEndingsBoundingBox( endingsBBox, GetEffectiveWidth() ) )
1750 bbox.Merge( endingsBBox );
1751
1752 bbox.Normalize();
1753
1754 return bbox;
1755}
1756
1757
1759{
1760 return std::make_shared<SHAPE_COMPOUND>( MakeEffectiveShapesWithLineEndings( GetEffectiveWidth() ) );
1761}
1762
1763
1765{
1766 return GetMaxError();
1767}
1768
1769
1771{
1772 PCB_SHAPE* image = dynamic_cast<PCB_SHAPE*>( aImage );
1773 wxCHECK( image, /* void */ );
1774
1775 SwapShape( image );
1776
1777 // Swap params not handled by SwapShape( image )
1778 std::swap( m_layer, image->m_layer );
1779 std::swap( m_isKnockout, image->m_isKnockout );
1780 std::swap( m_isLocked, image->m_isLocked );
1781 std::swap( m_flags, image->m_flags );
1782 std::swap( m_parent, image->m_parent );
1783 std::swap( m_forceVisible, image->m_forceVisible );
1784 std::swap( m_netinfo, image->m_netinfo );
1785 std::swap( m_hasSolderMask, image->m_hasSolderMask );
1786 std::swap( m_solderMaskMargin, image->m_solderMaskMargin );
1787 std::swap( m_customProperties, image->m_customProperties );
1788
1789 std::swap( m_libStart, image->m_libStart );
1790 std::swap( m_libEnd, image->m_libEnd );
1791 std::swap( m_libCornerRadius, image->m_libCornerRadius );
1792 std::swap( m_libArcMid, image->m_libArcMid );
1793 std::swap( m_libBezierC1, image->m_libBezierC1 );
1794 std::swap( m_libBezierC2, image->m_libBezierC2 );
1795 std::swap( m_libPoly, image->m_libPoly );
1796 std::swap( m_libEllipseCenter, image->m_libEllipseCenter );
1797 std::swap( m_libEllipseMajorRadius, image->m_libEllipseMajorRadius );
1798 std::swap( m_libEllipseMinorRadius, image->m_libEllipseMinorRadius );
1799 std::swap( m_libEllipseRotation, image->m_libEllipseRotation );
1800 std::swap( m_libEllipseStartAngle, image->m_libEllipseStartAngle );
1801 std::swap( m_libEllipseEndAngle, image->m_libEllipseEndAngle );
1802 std::swap( m_libShape, image->m_libShape );
1803}
1804
1805
1806bool PCB_SHAPE::cmp_drawings::operator()( const BOARD_ITEM* aFirst, const BOARD_ITEM* aSecond ) const
1807{
1808 if( aFirst->Type() != aSecond->Type() )
1809 return aFirst->Type() < aSecond->Type();
1810
1811 if( aFirst->GetLayer() != aSecond->GetLayer() )
1812 return aFirst->GetLayer() < aSecond->GetLayer();
1813
1814 if( aFirst->Type() == PCB_SHAPE_T )
1815 {
1816 const PCB_SHAPE* dwgA = static_cast<const PCB_SHAPE*>( aFirst );
1817 const PCB_SHAPE* dwgB = static_cast<const PCB_SHAPE*>( aSecond );
1818
1819 if( dwgA->GetShape() != dwgB->GetShape() )
1820 return dwgA->GetShape() < dwgB->GetShape();
1821 }
1822
1823 return aFirst->m_Uuid < aSecond->m_Uuid;
1824}
1825
1826
1827double PCB_SHAPE::GetCoverageArea( int aTextMargin ) const
1828{
1829 // Approximate "linear" shapes with just their width squared, as we don't want to consider
1830 // a linear shape as being much bigger than another for purposes of selection filtering
1831 // just because it happens to be really long.
1832 const double width = GetWidth();
1833
1834 switch( GetShape() )
1835 {
1836 case SHAPE_T::SEGMENT:
1837 case SHAPE_T::ARC:
1838 case SHAPE_T::BEZIER:
1839 return width * width;
1840
1841 case SHAPE_T::RECTANGLE:
1842 case SHAPE_T::CIRCLE:
1843 case SHAPE_T::POLY:
1844 if( !IsAnyFill() )
1845 return width * width;
1846
1848
1849 default:
1850 return BOARD_ITEM::GetCoverageArea( aTextMargin );
1851 }
1852}
1853
1854
1856 int aClearance, int aError, ERROR_LOC aErrorLoc,
1857 bool ignoreLineWidth ) const
1858{
1859 EDA_SHAPE::TransformShapeToPolygon( aBuffer, aClearance, aError, aErrorLoc, ignoreLineWidth,
1860 false );
1861}
1862
1863
1865 int aClearance, int aError, ERROR_LOC aErrorLoc,
1866 KIGFX::RENDER_SETTINGS* aRenderSettings ) const
1867{
1868 EDA_SHAPE::TransformShapeToPolygon( aBuffer, aClearance, aError, aErrorLoc, false, true );
1869}
1870
1871
1872bool PCB_SHAPE::operator==( const BOARD_ITEM& aOther ) const
1873{
1874 if( aOther.Type() != Type() )
1875 return false;
1876
1877 const PCB_SHAPE& other = static_cast<const PCB_SHAPE&>( aOther );
1878
1879 return *this == other;
1880}
1881
1882
1883bool PCB_SHAPE::operator==( const PCB_SHAPE& aOther ) const
1884{
1885 if( aOther.Type() != Type() )
1886 return false;
1887
1888 const PCB_SHAPE& other = static_cast<const PCB_SHAPE&>( aOther );
1889
1890 if( m_layer != other.m_layer )
1891 return false;
1892
1893 if( m_isKnockout != other.m_isKnockout )
1894 return false;
1895
1896 if( m_isLocked != other.m_isLocked )
1897 return false;
1898
1899 if( m_flags != other.m_flags )
1900 return false;
1901
1902 if( m_forceVisible != other.m_forceVisible )
1903 return false;
1904
1905 if( m_netinfo->GetNetCode() != other.m_netinfo->GetNetCode() )
1906 return false;
1907
1908 if( m_hasSolderMask != other.m_hasSolderMask )
1909 return false;
1910
1912 return false;
1913
1914 return EDA_SHAPE::operator==( other );
1915}
1916
1917
1918double PCB_SHAPE::Similarity( const BOARD_ITEM& aOther ) const
1919{
1920 if( aOther.Type() != Type() )
1921 return 0.0;
1922
1923 const PCB_SHAPE& other = static_cast<const PCB_SHAPE&>( aOther );
1924
1925 double similarity = 1.0;
1926
1927 if( GetLayer() != other.GetLayer() )
1928 similarity *= 0.9;
1929
1930 if( m_isKnockout != other.m_isKnockout )
1931 similarity *= 0.9;
1932
1933 if( m_isLocked != other.m_isLocked )
1934 similarity *= 0.9;
1935
1936 if( m_flags != other.m_flags )
1937 similarity *= 0.9;
1938
1939 if( m_forceVisible != other.m_forceVisible )
1940 similarity *= 0.9;
1941
1942 if( m_netinfo->GetNetCode() != other.m_netinfo->GetNetCode() )
1943 similarity *= 0.9;
1944
1945 if( m_hasSolderMask != other.m_hasSolderMask )
1946 similarity *= 0.9;
1947
1949 similarity *= 0.9;
1950
1951 similarity *= EDA_SHAPE::Similarity( other );
1952
1953 return similarity;
1954}
1955
1956
1957static struct PCB_SHAPE_DESC
1958{
1960 {
1967
1968 // Need to initialise enum_map before we can use a Property enum for it
1970
1971 if( layerEnum.Choices().GetCount() == 0 )
1972 {
1973 layerEnum.Undefined( UNDEFINED_LAYER );
1974
1975 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
1976 layerEnum.Map( layer, LSET::Name( layer ) );
1977 }
1978
1979 void ( PCB_SHAPE::*shapeLayerSetter )( PCB_LAYER_ID ) = &PCB_SHAPE::SetLayer;
1980 PCB_LAYER_ID ( PCB_SHAPE::*shapeLayerGetter )() const = &PCB_SHAPE::GetLayer;
1981
1982 propMgr.ReplaceProperty( TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ),
1984 shapeLayerSetter, shapeLayerGetter ) ).SetIsCopyable();
1985
1986 auto isPolygonOrEllipse =
1987 []( INSPECTABLE* aItem ) -> bool
1988 {
1989 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
1990 {
1991 const SHAPE_T t = shape->GetShape();
1992 return t == SHAPE_T::POLY || t == SHAPE_T::ELLIPSE || t == SHAPE_T::ELLIPSE_ARC;
1993 }
1994
1995 return false;
1996 };
1997
1998 propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( BOARD_ITEM ), _HKI( "Position X" ),
1999 isPolygonOrEllipse );
2000 propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( BOARD_ITEM ), _HKI( "Position Y" ),
2001 isPolygonOrEllipse );
2002
2003 propMgr.Mask( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ), _HKI( "Line Color" ) );
2004 propMgr.Mask( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ), _HKI( "Fill Color" ) );
2005
2006 auto isNotBezierOrEllipseArc =
2007 []( INSPECTABLE* aItem ) -> bool
2008 {
2009 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
2010 {
2011 const SHAPE_T t = shape->GetShape();
2012 return t != SHAPE_T::BEZIER && t != SHAPE_T::ELLIPSE_ARC;
2013 }
2014
2015 return true;
2016 };
2017
2019 isNotBezierOrEllipseArc );
2020
2021 auto isCircle =
2022 []( INSPECTABLE* aItem ) -> bool
2023 {
2024 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
2025 return shape->GetShape() == SHAPE_T::CIRCLE;
2026
2027 return false;
2028 };
2029
2030 auto isNotCircleOrEllipse =
2031 []( INSPECTABLE* aItem ) -> bool
2032 {
2033 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
2034 {
2035 const SHAPE_T t = shape->GetShape();
2036 return t != SHAPE_T::CIRCLE && t != SHAPE_T::ELLIPSE && t != SHAPE_T::ELLIPSE_ARC;
2037 }
2038
2039 return true;
2040 };
2041
2042 propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ), _HKI( "Start X" ),
2043 isNotCircleOrEllipse );
2044 propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ), _HKI( "Start Y" ),
2045 isNotCircleOrEllipse );
2046 propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ), _HKI( "End X" ),
2047 isNotCircleOrEllipse );
2048 propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ), _HKI( "End Y" ),
2049 isNotCircleOrEllipse );
2050 propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ), _HKI( "Center X" ),
2051 isCircle );
2052 propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ), _HKI( "Center Y" ),
2053 isCircle );
2054 propMgr.OverrideAvailability( TYPE_HASH( PCB_SHAPE ), TYPE_HASH( EDA_SHAPE ), _HKI( "Radius" ),
2055 isCircle );
2056
2057 auto isCopper =
2058 []( INSPECTABLE* aItem ) -> bool
2059 {
2060 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
2061 return shape->IsOnCopperLayer();
2062
2063 return false;
2064 };
2065
2067 isCopper );
2068
2069 auto isPadEditMode =
2070 []( BOARD* aBoard ) -> bool
2071 {
2072 if( aBoard && aBoard->IsFootprintHolder() )
2073 {
2074 for( FOOTPRINT* fp : aBoard->Footprints() )
2075 {
2076 for( PAD* pad : fp->Pads() )
2077 {
2078 if( pad->IsEntered() )
2079 return true;
2080 }
2081 }
2082 }
2083
2084 return false;
2085 };
2086
2087 auto showNumberBoxProperty =
2088 [&]( INSPECTABLE* aItem ) -> bool
2089 {
2090 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
2091 {
2092 if( shape->GetShape() == SHAPE_T::RECTANGLE )
2093 return isPadEditMode( shape->GetBoard() );
2094 }
2095
2096 return false;
2097 };
2098
2099 auto showSpokeTemplateProperty =
2100 [&]( INSPECTABLE* aItem ) -> bool
2101 {
2102 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
2103 {
2104 if( shape->GetShape() == SHAPE_T::SEGMENT )
2105 return isPadEditMode( shape->GetBoard() );
2106 }
2107
2108 return false;
2109 };
2110
2111 const wxString groupPadPrimitives = _HKI( "Pad Primitives" );
2112
2113 propMgr.AddProperty( new PROPERTY<PCB_SHAPE, bool>( _HKI( "Number Box" ),
2115 groupPadPrimitives )
2116 .SetAvailableFunc( showNumberBoxProperty )
2118
2119 propMgr.AddProperty( new PROPERTY<PCB_SHAPE, bool>( _HKI( "Thermal Spoke Template" ),
2121 groupPadPrimitives )
2122 .SetAvailableFunc( showSpokeTemplateProperty )
2124
2125 const wxString groupTechLayers = _HKI( "Technical Layers" );
2126
2127 auto isExternalCuLayer =
2128 []( INSPECTABLE* aItem )
2129 {
2130 if( auto shape = dynamic_cast<PCB_SHAPE*>( aItem ) )
2131 return IsExternalCopperLayer( shape->GetLayer() );
2132
2133 return false;
2134 };
2135
2136 propMgr.AddProperty( new PROPERTY<PCB_SHAPE, bool>( _HKI( "Soldermask" ),
2138 groupTechLayers )
2139 .SetAvailableFunc( isExternalCuLayer ).SetIsCopyable();
2140
2141 propMgr.AddProperty( new PROPERTY<PCB_SHAPE, std::optional<int>>( _HKI( "Soldermask Margin Override" ),
2144 groupTechLayers )
2145 .SetAvailableFunc( isExternalCuLayer ).SetIsCopyable();
2146 }
KICOMMON_API types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICOMMON_API KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:55
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
constexpr int ARC_LOW_DEF
Definition base_units.h:143
BITMAPS
A list of all bitmap identifiers.
@ add_dashed_line
@ FPHOLDER
Definition board.h:402
#define DEFAULT_LINE_WIDTH
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
BOARD_CONNECTED_ITEM(BOARD_ITEM *aParent, KICAD_T idtype)
void PackNet(kiapi::board::types::Net *aProto) const
NETINFO_ITEM * m_netinfo
Store all information about the net that item belongs to.
void UnpackNet(const kiapi::board::types::Net &aProto)
Assigns a net to this item from an API message.
Container for design settings for a BOARD object.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
BOARD_ITEM(BOARD_ITEM *aParent, KICAD_T idtype, PCB_LAYER_ID aLayer=F_Cu)
Definition board_item.h:86
friend class BOARD
Definition board_item.h:593
void SetUuidDirect(const KIID &aUuid)
Raw UUID assignment.
void SetLocked(bool aLocked) override
Definition board_item.h:418
bool m_isKnockout
Definition board_item.h:587
PCB_LAYER_ID m_layer
Definition board_item.h:586
bool m_isLocked
Definition board_item.h:588
bool IsLocked() const override
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
virtual double GetCoverageArea(int aTextMargin) const
Return the board area this item covers, used to rank candidates under the cursor so a click on a smal...
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:374
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:266
virtual bool IsOnCopperLayer() const
Definition board_item.h:189
wxString GetLayerName() const
Return the name of the PCB layer on which the item resides.
int GetMaxError() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1301
constexpr const Vec & GetPosition() const
Definition box2.h:208
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:143
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
constexpr const Vec & GetOrigin() const
Definition box2.h:207
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:308
Represent basic circle geometry with utility geometry functions.
Definition circle.h:33
MINOPTMAX< int > m_Value
Definition drc_rule.h:279
bool IsCardinal() const
Definition eda_angle.cpp:40
double AsRadians() const
Definition eda_angle.h:119
The base class for create windows for drawing purpose.
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:270
const KIID m_Uuid
Definition eda_item.h:599
bool m_forceVisible
Definition eda_item.h:614
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
EDA_ITEM_FLAGS m_flags
Definition eda_item.h:608
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:214
std::map< wxString, wxString > m_customProperties
Definition eda_item.h:617
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
Definition eda_item.cpp:509
EDA_ITEM * m_parent
Owner.
Definition eda_item.h:609
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:84
virtual int GetHatchLineSpacing() const
Definition eda_shape.h:166
SHAPE_T m_shape
Definition eda_shape.h:738
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:329
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false, bool includeFill=false) const
Convert the shape to a closed polygon.
int GetEllipseMinorRadius() const
Definition eda_shape.h:395
bool m_proxyItem
Definition eda_shape.h:766
int m_cornerRadius
Definition eda_shape.h:750
bool m_hatchingDirty
Definition eda_shape.h:746
bool m_endsSwapped
Definition eda_shape.h:737
const VECTOR2I & GetBezierC2() const
Definition eda_shape.h:368
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:377
bool GetLineEndingsBoundingBox(BOX2I &aBBox, int aLineWidth) const
int m_editState
Definition eda_shape.h:765
void rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
std::vector< SHAPE * > MakeEffectiveShapesWithLineEndings(int aLineWidth) const
Make effective geometry for the shape body shortened for line endings plus the line-ending geometry i...
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:423
virtual void SetCornerRadius(int aRadius)
int GetEllipseMajorRadius() const
Definition eda_shape.h:386
virtual int GetEffectiveWidth() const
Definition eda_shape.h:164
SHAPE_POLY_SET & GetPolyShape()
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:404
void ShapeGetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList)
virtual void SetEllipseEndAngle(const EDA_ANGLE &aA)
Definition eda_shape.h:416
bool operator==(const EDA_SHAPE &aOther) const
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:175
virtual void SetBezierC2(const VECTOR2I &aPt)
Definition eda_shape.h:367
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
VECTOR2I m_arcCenter
Definition eda_shape.h:755
virtual void SetBezierC1(const VECTOR2I &aPt)
Definition eda_shape.h:364
virtual void SetEllipseRotation(const EDA_ANGLE &aA)
Definition eda_shape.h:397
ARC_MID m_arcMidData
Definition eda_shape.h:756
bool IsSolidFill() const
Definition eda_shape.h:123
void flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection)
EDA_SHAPE(SHAPE_T aType, int aLineWidth, FILL_T aFill)
Definition eda_shape.cpp:56
VECTOR2I m_start
Definition eda_shape.h:752
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:325
virtual void SetEllipseCenter(const VECTOR2I &aPt)
Definition eda_shape.h:370
virtual void SetEllipseMinorRadius(int aR)
Definition eda_shape.h:388
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:275
virtual void SetEllipseMajorRadius(int aR)
Definition eda_shape.h:379
void SwapShape(EDA_SHAPE *aImage)
std::vector< VECTOR2I > GetRectCorners() const
bool IsAnyFill() const
Definition eda_shape.h:118
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:414
virtual void UpdateHatching() const
virtual void SetEllipseStartAngle(const EDA_ANGLE &aA)
Definition eda_shape.h:407
virtual void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
wxString SHAPE_T_asString() const
double Similarity(const EDA_SHAPE &aOther) const
const VECTOR2I & GetBezierC1() const
Definition eda_shape.h:365
VECTOR2I m_end
Definition eda_shape.h:753
const BOX2I getBoundingBox() const
virtual int GetWidth() const
Definition eda_shape.h:163
STROKE_PARAMS m_stroke
Definition eda_shape.h:739
void RebuildBezierToSegmentsPointsList()
Definition eda_shape.h:541
VECTOR2I m_bezierC1
Definition eda_shape.h:758
int GetCornerRadius() const
virtual void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition eda_shape.h:514
virtual void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:279
VECTOR2I m_bezierC2
Definition eda_shape.h:759
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
VECTOR2I GetArcMid() const
virtual bool IsVisible() const
Definition eda_text.h:226
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition property.h:789
static ENUM_MAP< T > & Instance()
Definition property.h:783
ENUM_MAP & Undefined(T aValue)
Definition property.h:796
wxPGChoices & Choices()
Definition property.h:834
const TRANSFORM_TRS & GetTransform() const
Definition footprint.h:452
const wxString & GetReference() const
Definition footprint.h:909
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:39
Contains methods for drawing PCB-specific items.
virtual PCB_RENDER_SETTINGS * GetSettings() override
Return a pointer to current settings that are going to be used when drawing items.
PCB specific render settings.
Definition pcb_painter.h:84
const std::unordered_set< KIID > & GetConstrainedItems() const
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
PCB_LAYER_ID GetPrimaryHighContrastLayer() const
Return the board layer which is in high-contrast mode.
static constexpr double LOD_HIDE
Return this constant from ViewGetLOD() to hide the item unconditionally.
Definition view_item.h:176
static constexpr double LOD_SHOW
Return this constant from ViewGetLOD() to show the item unconditionally.
Definition view_item.h:181
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
bool IsLayerVisibleCached(int aLayer) const
Definition view.h:441
PAINTER * GetPainter() const
Return the painter object used by the view for drawing VIEW_ITEM objects.
Definition view.h:227
Definition kiid.h:46
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
void RunOnLayers(const std::function< void(PCB_LAYER_ID)> &aFunction) const
Execute a function on each layer of the LSET.
Definition lset.h:275
static const LSET & AllLayersMask()
Definition lset.cpp:637
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:184
T Opt() const
Definition minoptmax.h:31
bool HasOpt() const
Definition minoptmax.h:39
int GetNetCode() const
Definition netinfo.h:104
Definition pad.h:61
std::optional< int > GetLocalThermalSpokeWidthOverride() const
Definition pad.h:741
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
virtual void Mirror(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Mirror this object relative to a given horizontal axis the layer is not changed.
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Populate aList of MSG_PANEL_ITEM objects with it's internal state for display purposes.
void StyleFromSettings(const BOARD_DESIGN_SETTINGS &settings, bool aCheckSide) override
VECTOR2I m_libEllipseCenter
Definition pcb_shape.h:394
void swapData(BOARD_ITEM *aImage) override
bool IsConnected() const override
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
void SetEllipseCenter(const VECTOR2I &aPt) override
EDA_ANGLE m_libEllipseStartAngle
Definition pcb_shape.h:398
double ViewGetLOD(int aLayer, const KIGFX::VIEW *aView) const override
Return the level of detail (LOD) of the item.
VECTOR2I m_libEnd
Definition pcb_shape.h:383
VECTOR2I m_libBezierC1
Definition pcb_shape.h:389
virtual void syncLibCoords()
SHAPE_POLY_SET getHatchingKnockouts() const override
void SetBezierC1(const VECTOR2I &aPt) override
void SetCornerRadius(int aCornerRadius) override
std::optional< int > GetLocalSolderMaskMargin() const
Definition pcb_shape.h:347
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:78
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
VECTOR2I GetLibraryBezierC1() const
BITMAPS GetMenuImage() const override
Return a pointer to an image to be used in menus.
PCB_SHAPE(BOARD_ITEM *aParent, KICAD_T aItemType, SHAPE_T aShapeType)
void SetWidth(int aWidth) override
EDA_ANGLE m_libEllipseEndAngle
Definition pcb_shape.h:399
void SetLibStrokeWidth(int aWidth)
SHAPE_T m_libShape
Definition pcb_shape.h:401
void rebakeFromTransform(const TRANSFORM_TRS &aXform)
int GetWidth() const override
const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
bool HasSolderMask() const
Definition pcb_shape.h:344
void SetEllipseStartAngle(const EDA_ANGLE &aA) override
void SetHasSolderMask(bool aVal)
Definition pcb_shape.h:343
std::optional< int > m_solderMaskMargin
Definition pcb_shape.h:380
int GetSolderMaskExpansion() const
void NormalizeForCompare() override
Normalize coordinates to compare 2 similar PCB_SHAPES similat to Normalize(), but also normalize SEGM...
void SetShape(SHAPE_T aShape) override
Definition pcb_shape.h:209
void TransformShapeToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, KIGFX::RENDER_SETTINGS *aRenderSettings=nullptr) const override
Convert the item shape to a polyset.
VECTOR2I m_libArcMid
Definition pcb_shape.h:387
void SetEllipseEndAngle(const EDA_ANGLE &aA) override
int m_libEllipseMinorRadius
Definition pcb_shape.h:396
const VECTOR2I GetFocusPosition() const override
Allows items to return their visual center rather than their anchor.
virtual void SetLayerSet(const LSET &aLayers) override
void SetEnd(const VECTOR2I &aEnd) override
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
VECTOR2I GetLibraryEnd() const
Definition pcb_shape.h:230
void SetPolyShape(const SHAPE_POLY_SET &aShape) override
virtual std::vector< VECTOR2I > GetCorners() const
Return 4 corners for a rectangle or rotated rectangle (stored as a poly).
bool IsProxyItem() const override
Definition pcb_shape.h:153
void SetEllipseRotation(const EDA_ANGLE &aA) override
bool m_hasSolderMask
Definition pcb_shape.h:379
const FOOTPRINT * transformFp() const
VECTOR2I GetLibraryStart() const
Definition pcb_shape.h:229
void OnFootprintRescaled(double aRatioX, double aRatioY, double aLinearFactor, const VECTOR2I &aAnchor, const EDA_ANGLE &aParentRotate) override
Apply a parent footprint scale to this item.
~PCB_SHAPE() override
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
bool operator==(const PCB_SHAPE &aShape) const
std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const override
Make a set of SHAPE objects representing the PCB_SHAPE.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
int m_libCornerRadius
Definition pcb_shape.h:385
void SetEllipseMinorRadius(int aR) override
wxString GetFriendlyName() const override
Definition pcb_shape.h:63
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the shape to a closed polygon.
STROKE_PARAMS GetStroke() const override
void SetIsProxyItem(bool aIsProxy=true) override
void RebakeWithScale(double aScaleX, double aScaleY)
SHAPE_POLY_SET m_libPoly
Definition pcb_shape.h:392
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition pcb_shape.h:346
void Move(const VECTOR2I &aMoveVector) override
Move this object.
std::vector< VECTOR2I > GetConnectionPoints() const
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
double GetCoverageArea(int aTextMargin) const override
Return the board area this item covers, used to rank candidates under the cursor so a click on a smal...
int getMaxError() const override
void SetStart(const VECTOR2I &aStart) override
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
void SetBezierC2(const VECTOR2I &aPt) override
void UpdateHatching() const override
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
void Scale(double aScale)
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
SHAPE_POLY_SET GetLibraryPolyShape() const
void SetStroke(const STROKE_PARAMS &aStroke) override
void Normalize() override
Perform any normalization required after a user rotate and/or flip.
bool IsType(const std::vector< KICAD_T > &aScanTypes) const override
Check whether the item is one of the listed types.
void CopyFrom(const BOARD_ITEM *aOther) override
VECTOR2I GetLibraryBezierC2() const
VECTOR2I m_libBezierC2
Definition pcb_shape.h:390
std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
void RebakeFromLib()
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd) override
Set the three controlling points for an arc.
double Similarity(const BOARD_ITEM &aBoardItem) const override
Return a measure of how likely the other object is to represent the same object.
VECTOR2I m_libStart
Definition pcb_shape.h:382
EDA_ANGLE m_libEllipseRotation
Definition pcb_shape.h:397
int m_libEllipseMajorRadius
Definition pcb_shape.h:395
VECTOR2I GetLibraryArcMid() const
void SetEllipseMajorRadius(int aR) override
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:68
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition property.h:263
PROPERTY_BASE & SetIsCopyable(bool aIsCopyable=true)
Definition property.h:359
PROPERTY_BASE & SetIsHiddenFromRulesEditor(bool aHide=true)
Definition property.h:332
Provide class metadata.
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
void Mask(TYPE_ID aDerived, TYPE_ID aBase, const wxString &aName)
Sets a base class property as masked in a derived class.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
void OverrideAvailability(TYPE_ID aDerived, TYPE_ID aBase, const wxString &aName, std::function< bool(INSPECTABLE *)> aFunc)
Sets an override availability functor for a base class property of a given derived class.
PROPERTY_BASE & ReplaceProperty(size_t aBase, const wxString &aName, PROPERTY_BASE *aNew, const wxString &aGroup=wxEmptyString)
Replace an existing property for a specific type.
void AddTypeCast(TYPE_CAST_BASE *aCast)
Register a type converter.
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
ITERATOR IterateWithHoles(int aOutline)
void SetVertex(const VERTEX_INDEX &aIndex, const VECTOR2I &aPos)
Accessor function to set the position of a specific point.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
Simple container to manage line stroke parameters.
int GetWidth() const
void SetWidth(int aWidth)
VECTOR2I InverseApply(const VECTOR2I &aPoint) const
bool IsUniformScale() const
const EDA_ANGLE & GetRotate() const
double GetScaleX() const
VECTOR2I Apply(const VECTOR2I &aPoint) const
double GetScaleY() const
double ApplyLinearScale(double aLength) const
void SetScale(double aSx, double aSy)
double InverseApplyLinearScale(double aLength) const
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle with rounded corners and/or chamfered corners to a polygon.
DRC_CONSTRAINT_T
Definition drc_rule.h:49
@ SOLDER_MASK_EXPANSION_CONSTRAINT
Definition drc_rule.h:68
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
@ RADIANS_T
Definition eda_angle.h:31
@ DEGREES_T
Definition eda_angle.h:30
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:454
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:452
#define PCB_EDIT_FRAME_NAME
FILL_T
Definition eda_fill.h:29
@ NO_FILL
Definition eda_fill.h:30
@ RECURSE
Definition eda_item.h:51
SHAPE_T
Definition eda_shape.h:54
@ UNDEFINED
Definition eda_shape.h:55
@ ELLIPSE
Definition eda_shape.h:62
@ SEGMENT
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ ELLIPSE_ARC
Definition eda_shape.h:63
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Definition layer_id.cpp:179
bool IsSolderMaskLayer(int aLayer)
Definition layer_ids.h:774
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:806
FLASHING
Enum used during connectivity building to ensure we do not query connectivity while building the data...
Definition layer_ids.h:180
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:829
int GetNetnameLayer(int aLayer)
Return a netname layer corresponding to the given layer.
Definition layer_ids.h:880
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
@ LAYER_LOCKED_ITEM_SHADOW
Shadow layer for locked items.
Definition layer_ids.h:303
@ LAYER_FOOTPRINTS_FR
Show footprints on front.
Definition layer_ids.h:255
@ LAYER_CONSTRAINT_SHADOW
Shadow layer for items bound to a constraint.
Definition layer_ids.h:320
@ LAYER_FOOTPRINTS_BK
Show footprints on back.
Definition layer_ids.h:256
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:714
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ Edge_Cuts
Definition layer_ids.h:108
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ B_CrtYd
Definition layer_ids.h:111
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ F_Cu
Definition layer_ids.h:60
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:79
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:92
FLIP_DIRECTION
Definition mirror.h:23
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:24
std::vector< TYPED_POINT2I > GetCircleKeyPoints(const CIRCLE &aCircle, bool aIncludeCenter)
Get key points of an CIRCLE.
KICOMMON_API void PackCustomProperties(google::protobuf::RepeatedPtrField< types::CustomProperty > *aOutput, const EDA_ITEM &aItem)
KICOMMON_API void UnpackCustomProperties(const google::protobuf::RepeatedPtrField< types::CustomProperty > &aInput, EDA_ITEM &aItem)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
#define _HKI(x)
Definition page_info.cpp:40
static double fpScaleLinear(const FOOTPRINT *aFp)
static struct PCB_SHAPE_DESC _PCB_SHAPE_DESC
static bool isCopper(const PNS::ITEM *aItem)
#define TYPE_HASH(x)
Macro to generate unique identifier for a type.
Definition property.h:74
@ PT_SIZE
Size expressed in distance units (mm/inch)
Definition property.h:63
#define REGISTER_TYPE(x)
Helper macro to map type hashes to names.
Utility functions for working with shapes.
const int scale
bool operator()(const BOARD_ITEM *aFirst, const BOARD_ITEM *aSecond) const
const SHAPE_LINE_CHAIN chain
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
#define M_PI
const VECTOR2I CalcArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition trigo.cpp:506
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ PCB_SHAPE_LOCATE_ELLIPSE_ARC_T
Definition typeinfo.h:236
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_LOCATE_BOARD_EDGE_T
Definition typeinfo.h:126
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:85
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_SHAPE_LOCATE_CIRCLE_T
Definition typeinfo.h:131
@ PCB_SHAPE_LOCATE_SEGMENT_T
Definition typeinfo.h:129
@ PCB_SHAPE_LOCATE_RECT_T
Definition typeinfo.h:130
@ PCB_SHAPE_LOCATE_ELLIPSE_T
Definition typeinfo.h:235
@ PCB_SHAPE_LOCATE_BEZIER_T
Definition typeinfo.h:134
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_SHAPE_LOCATE_POLY_T
Definition typeinfo.h:133
@ PCB_SHAPE_LOCATE_ARC_T
Definition typeinfo.h:132
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707
#define ZONE_THERMAL_RELIEF_COPPER_WIDTH_MM
Definition zones.h:29