KiCad PCB EDA Suite
pad_custom_shape_functions.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) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <board.h>
27#include <board_item.h>
28#include <pcb_shape.h>
29#include <pad.h>
31#include <geometry/shape_rect.h>
32
33
34/*
35 * Has meaning only for free shape pads.
36 * add a free shape to the shape list.
37 * the shape is a polygon (can be with thick outline), segment, circle or arc
38 */
39
40void PAD::AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness, bool aFilled )
41{
42 // If aPoly has holes, convert it to a polygon with no holes.
43 SHAPE_POLY_SET poly_no_hole;
44 poly_no_hole.Append( aPoly );
45
46 if( poly_no_hole.HasHoles() )
48
49 // There should never be multiple shapes, but if there are, we split them into
50 // primitives so that we can edit them both.
51 for( int ii = 0; ii < poly_no_hole.OutlineCount(); ++ii )
52 {
53 SHAPE_POLY_SET poly_outline( poly_no_hole.COutline( ii ) );
54 PCB_SHAPE* item = new PCB_SHAPE();
55 item->SetShape( SHAPE_T::POLY );
56 item->SetFilled( aFilled );
57 item->SetPolyShape( poly_outline );
58 item->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
59 item->SetParent( this );
60 m_editPrimitives.emplace_back( item );
61 }
62
63 SetDirty();
64}
65
66
67void PAD::AddPrimitivePoly( const std::vector<VECTOR2I>& aPoly, int aThickness, bool aFilled )
68{
69 PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::POLY );
70 item->SetFilled( aFilled );
71 item->SetPolyPoints( aPoly );
72 item->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
73 item->SetParent( this );
74 m_editPrimitives.emplace_back( item );
75 SetDirty();
76}
77
78
79void PAD::AddPrimitiveSegment( const VECTOR2I& aStart, const VECTOR2I& aEnd, int aThickness )
80{
81 PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::SEGMENT );
82 item->SetFilled( false );
83 item->SetStart( aStart );
84 item->SetEnd( aEnd );
85 item->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
86 item->SetParent( this );
87 m_editPrimitives.emplace_back( item );
88 SetDirty();
89}
90
91
92void PAD::AddPrimitiveArc( const VECTOR2I& aCenter, const VECTOR2I& aStart,
93 const EDA_ANGLE& aArcAngle, int aThickness )
94{
95 PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::ARC );
96 item->SetFilled( false );
97 item->SetCenter( aCenter );
98 item->SetStart( aStart );
99 item->SetArcAngleAndEnd( aArcAngle );
100 item->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
101 item->SetParent( this );
102 m_editPrimitives.emplace_back( item );
103 SetDirty();
104}
105
106
107void PAD::AddPrimitiveCurve( const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCtrl1,
108 const VECTOR2I& aCtrl2, int aThickness )
109{
110 PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::BEZIER );
111 item->SetFilled( false );
112 item->SetStart( aStart );
113 item->SetEnd( aEnd );
114 item->SetBezierC1( aCtrl1 );
115 item->SetBezierC2( aCtrl2 );
116 item->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
117 item->SetParent( this );
118 m_editPrimitives.emplace_back( item );
119 SetDirty();
120}
121
122
123void PAD::AddPrimitiveCircle( const VECTOR2I& aCenter, int aRadius, int aThickness, bool aFilled )
124{
125 PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::CIRCLE );
126 item->SetFilled( aFilled );
127 item->SetStart( aCenter );
128 item->SetEnd( VECTOR2I( aCenter.x + aRadius, aCenter.y ) );
129 item->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
130 item->SetParent( this );
131 m_editPrimitives.emplace_back( item );
132 SetDirty();
133}
134
135
136void PAD::AddPrimitiveRect( const VECTOR2I& aStart, const VECTOR2I& aEnd, int aThickness,
137 bool aFilled)
138{
139 PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::RECT );
140 item->SetFilled( aFilled );
141 item->SetStart( aStart );
142 item->SetEnd( aEnd );
143 item->SetStroke( STROKE_PARAMS( aThickness, PLOT_DASH_TYPE::SOLID ) );
144 item->SetParent( this );
145 m_editPrimitives.emplace_back( item );
146 SetDirty();
147}
148
149
150void PAD::AddPrimitiveAnnotationBox( const VECTOR2I& aStart, const VECTOR2I& aEnd )
151{
152 PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::RECT );
153 item->SetIsAnnotationProxy();
154 item->SetFilled( false );
155 item->SetStart( aStart );
156 item->SetEnd( aEnd );
158 item->SetParent( this );
159 m_editPrimitives.emplace_back( item );
160 SetDirty();
161}
162
163
164void PAD::ReplacePrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
165{
166 // clear old list
168
169 // Import to the given shape list
170 if( aPrimitivesList.size() )
171 AppendPrimitives( aPrimitivesList );
172
173 SetDirty();
174}
175
176
177void PAD::AppendPrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
178{
179 // Add duplicates of aPrimitivesList to the pad primitives list:
180 for( const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
181 AddPrimitive( new PCB_SHAPE( *prim ) );
182
183 SetDirty();
184}
185
186
187void PAD::AddPrimitive( PCB_SHAPE* aPrimitive )
188{
189 aPrimitive->SetParent( this );
190 m_editPrimitives.emplace_back( aPrimitive );
191
192 SetDirty();
193}
194
195
196// clear the basic shapes list and associated data
198{
199 m_editPrimitives.clear();
200
201 SetDirty();
202}
203
204
205void PAD::addPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError,
206 ERROR_LOC aErrorLoc ) const
207{
208 SHAPE_POLY_SET polyset;
209
210 for( const std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
211 {
212 if( !primitive->IsAnnotationProxy() )
213 primitive->TransformShapeToPolygon( polyset, UNDEFINED_LAYER, 0, aError, aErrorLoc );
214 }
215
217
218 // Merge all polygons with the initial pad anchor shape
219 if( polyset.OutlineCount() )
220 {
221 aMergedPolygon->BooleanAdd( polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
223 }
224}
225
226void PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon, ERROR_LOC aErrorLoc ) const
227{
228 const BOARD* board = GetBoard();
229 int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
230
231 aMergedPolygon->RemoveAllContours();
232
233 // Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
234 // The anchor pad is always at 0,0
235 switch( GetAnchorPadShape() )
236 {
237 case PAD_SHAPE::RECT:
238 {
239 SHAPE_RECT rect( -GetSize().x / 2, -GetSize().y / 2, GetSize().x, GetSize().y );
240 aMergedPolygon->AddOutline( rect.Outline() );
241 }
242 break;
243
244 default:
246 TransformCircleToPolygon( *aMergedPolygon, VECTOR2I( 0, 0 ), GetSize().x / 2, maxError,
247 aErrorLoc );
248 break;
249 }
250
251 addPadPrimitivesToPolygon( aMergedPolygon, maxError, aErrorLoc );
252}
253
254
256{
257 SHAPE_POLY_SET poly;
259
260 if( poly.OutlineCount() > 1 )
261 return false;
262
263 const int minSteps = 10;
264 const int maxSteps = 50;
265
266 int stepsX, stepsY;
267
268 auto bbox = poly.BBox();
269
270 if( bbox.GetWidth() < bbox.GetHeight() )
271 {
272 stepsX = minSteps;
273 stepsY = minSteps * (double) bbox.GetHeight() / (double )(bbox.GetWidth() + 1);
274 }
275 else
276 {
277 stepsY = minSteps;
278 stepsX = minSteps * (double) bbox.GetWidth() / (double )(bbox.GetHeight() + 1);
279 }
280
281 stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) );
282 stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) );
283
284 VECTOR2I center = bbox.Centre();
285
286 int64_t minDist = std::numeric_limits<int64_t>::max();
287 int64_t minDistEdge;
288
290 {
291 minDistEdge = GetSize().x;
292 }
293 else
294 {
295 minDistEdge = std::max( GetSize().x, GetSize().y );
296 }
297
298 std::optional<VECTOR2I> bestAnchor( []()->std::optional<VECTOR2I> { return std::nullopt; }() );
299
300 for( int y = 0; y < stepsY ; y++ )
301 {
302 for( int x = 0; x < stepsX; x++ )
303 {
304 VECTOR2I p = bbox.GetPosition();
305 p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) );
306 p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) );
307
308 if( poly.Contains(p) )
309 {
310
311 int dist = (center - p).EuclideanNorm();
312 int distEdge = poly.COutline(0).Distance( p, true );
313
314 if( distEdge >= minDistEdge )
315 {
316 if( dist < minDist )
317 {
318 bestAnchor = p;
319 minDist = dist;
320 }
321 }
322 }
323 }
324 }
325
326 if( bestAnchor )
327 {
328 aPos = *bestAnchor;
329 return true;
330 }
331
332 return false;
333}
constexpr int ARC_HIGH_DEF
Definition: base_units.h:121
constexpr int ARC_LOW_DEF
Definition: base_units.h:120
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:43
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:269
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:704
coord_type GetHeight() const
Definition: box2.h:188
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:100
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:178
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:470
void SetFilled(bool aFlag)
Definition: eda_shape.h:95
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:255
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:124
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:112
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:149
void SetBezierC1(const VECTOR2I &aPt)
Definition: eda_shape.h:175
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:596
void SetIsAnnotationProxy(bool aIsProxy=true)
Definition: eda_shape.h:88
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1121
void AddPrimitiveArc(const VECTOR2I &aCenter, const VECTOR2I &aStart, const EDA_ANGLE &aArcAngle, int aThickness)
void AppendPrimitives(const std::vector< std::shared_ptr< PCB_SHAPE > > &aPrimitivesList)
Import a custom shape primitive list (composed of basic shapes) and add items to the current list.
void AddPrimitiveAnnotationBox(const VECTOR2I &aStart, const VECTOR2I &aEnd)
Has meaning only for custom shape pads.
void MergePrimitivesAsPolygon(SHAPE_POLY_SET *aMergedPolygon, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Merge all basic shapes to a SHAPE_POLY_SET.
std::vector< std::shared_ptr< PCB_SHAPE > > m_editPrimitives
Definition: pad.h:784
void DeletePrimitivesList()
Clear the basic shapes list.
void SetDirty()
Definition: pad.h:391
void AddPrimitiveSegment(const VECTOR2I &aStart, const VECTOR2I &aEnd, int aThickness)
void AddPrimitiveCurve(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCtrl1, const VECTOR2I &aCtrl2, int aThickness)
bool GetBestAnchorPosition(VECTOR2I &aPos)
void AddPrimitiveCircle(const VECTOR2I &aCenter, int aRadius, int aThickness, bool aFilled)
void AddPrimitive(PCB_SHAPE *aPrimitive)
Add item to the custom shape primitives list.
void AddPrimitivePoly(const SHAPE_POLY_SET &aPoly, int aThickness, bool aFilled)
Has meaning only for custom shape pads.
void AddPrimitiveRect(const VECTOR2I &aStart, const VECTOR2I &aEnd, int aThickness, bool aFilled)
void ReplacePrimitives(const std::vector< std::shared_ptr< PCB_SHAPE > > &aPrimitivesList)
Clear the current custom shape primitives list and import a new list.
void addPadPrimitivesToPolygon(SHAPE_POLY_SET *aMergedPolygon, int aError, ERROR_LOC aErrorLoc) const
const VECTOR2I & GetSize() const
Definition: pad.h:258
PAD_SHAPE GetAnchorPadShape() const
Definition: pad.h:208
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:72
int Distance(const VECTOR2I &aP, bool aOutlineOnly=false) const
Compute the minimum distance between the line chain and a point aP.
Represent a set of closed polygons.
bool HasHoles() const
Return true if the polygon set has any holes that share a vertex.
void Fracture(POLYGON_MODE aFastMode)
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new hole to the given outline (default: last) and returns its index.
void BooleanAdd(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset difference For aFastMode meaning, see function booleanOp.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...
void Simplify(POLYGON_MODE aFastMode)
int OutlineCount() const
Return the number of vertices in a given outline/hole.
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
const SHAPE_LINE_CHAIN Outline() const
Definition: shape_rect.h:179
Simple container to manage line stroke parameters.
Definition: stroke_params.h:88
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
@ ERROR_INSIDE
@ UNDEFINED_LAYER
Definition: layer_ids.h:60
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:129
T rescale(T aNumerator, T aValue, T aDenominator)
Scale a number (value) by rational (numerator/denominator).
Definition: util.h:118
VECTOR2< int > VECTOR2I
Definition: vector2d.h:590