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-2019 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>
26 #include <board_design_settings.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 
40 void 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 );
46 
47  PCB_SHAPE* item = new PCB_SHAPE();
48  item->SetShape( SHAPE_T::POLY );
49  item->SetFilled( aFilled );
50  item->SetPolyShape( poly_no_hole );
51  item->SetWidth( aThickness );
52  item->SetParent( this );
53  m_editPrimitives.emplace_back( item );
54  SetDirty();
55 }
56 
57 
58 void PAD::AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness, bool aFilled )
59 {
60  PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::POLY );
61  item->SetFilled( aFilled );
62  item->SetPolyPoints( aPoly );
63  item->SetWidth( aThickness );
64  item->SetParent( this );
65  m_editPrimitives.emplace_back( item );
66  SetDirty();
67 }
68 
69 
70 void PAD::AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness )
71 {
72  PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::SEGMENT );
73  item->SetFilled( false );
74  item->SetStart( aStart );
75  item->SetEnd( aEnd );
76  item->SetWidth( aThickness );
77  item->SetParent( this );
78  m_editPrimitives.emplace_back( item );
79  SetDirty();
80 }
81 
82 
83 void PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aArcAngle,
84  int aThickness )
85 {
86  PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::ARC );
87  item->SetFilled( false );
88  item->SetCenter( aCenter );
89  item->SetStart( aStart );
90  item->SetArcAngleAndEnd( aArcAngle );
91  item->SetWidth( aThickness );
92  item->SetParent( this );
93  m_editPrimitives.emplace_back( item );
94  SetDirty();
95 }
96 
97 
98 void PAD::AddPrimitiveCurve( const wxPoint& aStart, const wxPoint& aEnd, const wxPoint& aCtrl1,
99  const wxPoint& aCtrl2, int aThickness )
100 {
101  PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::BEZIER );
102  item->SetFilled( false );
103  item->SetStart( aStart );
104  item->SetEnd( aEnd );
105  item->SetBezierC1( aCtrl1 );
106  item->SetBezierC2( aCtrl2 );
107  item->SetWidth( aThickness );
108  item->SetParent( this );
109  m_editPrimitives.emplace_back( item );
110  SetDirty();
111 }
112 
113 
114 void PAD::AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness, bool aFilled )
115 {
116  PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::CIRCLE );
117  item->SetFilled( aFilled );
118  item->SetStart( aCenter );
119  item->SetEnd( wxPoint( aCenter.x + aRadius, aCenter.y ) );
120  item->SetWidth( aThickness );
121  item->SetParent( this );
122  m_editPrimitives.emplace_back( item );
123  SetDirty();
124 }
125 
126 
127 void PAD::AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness,
128  bool aFilled)
129 {
130  PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T:: RECT );
131  item->SetFilled( aFilled );
132  item->SetStart( aStart );
133  item->SetEnd( aEnd );
134  item->SetWidth( aThickness );
135  item->SetParent( this );
136  m_editPrimitives.emplace_back( item );
137  SetDirty();
138 }
139 
140 
141 void PAD::ReplacePrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
142 {
143  // clear old list
145 
146  // Import to the given shape list
147  if( aPrimitivesList.size() )
148  AppendPrimitives( aPrimitivesList );
149 
150  SetDirty();
151 }
152 
153 
154 void PAD::AppendPrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
155 {
156  // Add duplicates of aPrimitivesList to the pad primitives list:
157  for( const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
158  AddPrimitive( new PCB_SHAPE( *prim ) );
159 
160  SetDirty();
161 }
162 
163 
164 void PAD::AddPrimitive( PCB_SHAPE* aPrimitive )
165 {
166  aPrimitive->SetParent( this );
167  m_editPrimitives.emplace_back( aPrimitive );
168 
169  SetDirty();
170 }
171 
172 
173 // clear the basic shapes list and associated data
175 {
176  m_editPrimitives.clear();
177 
178  SetDirty();
179 }
180 
181 
182 void PAD::addPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError,
183  ERROR_LOC aErrorLoc ) const
184 {
185  SHAPE_POLY_SET polyset;
186 
187  for( const std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
188  {
189  primitive->TransformShapeWithClearanceToPolygon( polyset, UNDEFINED_LAYER, 0, aError,
190  aErrorLoc );
191  }
192 
194 
195  // Merge all polygons with the initial pad anchor shape
196  if( polyset.OutlineCount() )
197  {
198  aMergedPolygon->BooleanAdd( polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
199  aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
200  }
201 }
202 
203 void PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon, ERROR_LOC aErrorLoc ) const
204 {
205  const BOARD* board = GetBoard();
206  int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
207 
208  aMergedPolygon->RemoveAllContours();
209 
210  // Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
211  // The anchor pad is always at 0,0
212  switch( GetAnchorPadShape() )
213  {
214  case PAD_SHAPE::RECT:
215  {
216  SHAPE_RECT rect( -GetSize().x / 2, -GetSize().y / 2, GetSize().x, GetSize().y );
217  aMergedPolygon->AddOutline( rect.Outline() );
218  }
219  break;
220 
221  default:
222  case PAD_SHAPE::CIRCLE:
223  TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError,
224  ERROR_INSIDE );
225  break;
226  }
227 
228  addPadPrimitivesToPolygon( aMergedPolygon, maxError, aErrorLoc );
229 }
230 
231 
233 {
234  SHAPE_POLY_SET poly;
235  addPadPrimitivesToPolygon( &poly, ARC_LOW_DEF, ERROR_INSIDE );
236 
237  if( poly.OutlineCount() > 1 )
238  return false;
239 
240  const int minSteps = 10;
241  const int maxSteps = 50;
242 
243  int stepsX, stepsY;
244 
245  auto bbox = poly.BBox();
246 
247  if( bbox.GetWidth() < bbox.GetHeight() )
248  {
249  stepsX = minSteps;
250  stepsY = minSteps * (double) bbox.GetHeight() / (double )(bbox.GetWidth() + 1);
251  }
252  else
253  {
254  stepsY = minSteps;
255  stepsX = minSteps * (double) bbox.GetWidth() / (double )(bbox.GetHeight() + 1);
256  }
257 
258  stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) );
259  stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) );
260 
261  VECTOR2I center = bbox.Centre();
262 
263  int64_t minDist = std::numeric_limits<int64_t>::max();
264  int64_t minDistEdge;
265 
267  {
268  minDistEdge = GetSize().x;
269  }
270  else
271  {
272  minDistEdge = std::max( GetSize().x, GetSize().y );
273  }
274 
275  OPT<VECTOR2I> bestAnchor( []()->OPT<VECTOR2I> { return NULLOPT; }() );
276 
277  for( int y = 0; y < stepsY ; y++ )
278  {
279  for( int x = 0; x < stepsX; x++ )
280  {
281  VECTOR2I p = bbox.GetPosition();
282  p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) );
283  p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) );
284 
285  if( poly.Contains(p) )
286  {
287 
288  int dist = (center - p).EuclideanNorm();
289  int distEdge = poly.COutline(0).Distance( p, true );
290 
291  if( distEdge >= minDistEdge )
292  {
293  if( dist < minDist )
294  {
295  bestAnchor = p;
296  minDist = dist;
297  }
298  }
299  }
300  }
301  }
302 
303  if( bestAnchor )
304  {
305  aPos = *bestAnchor;
306  return true;
307  }
308 
309  return false;
310 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:146
int OutlineCount() const
Return the number of vertices in a given outline/hole.
const SHAPE_LINE_CHAIN Outline() const
Definition: shape_rect.h:170
void SetFilled(bool aFlag)
Definition: eda_shape.h:83
void SetEnd(const wxPoint &aEnd)
Definition: eda_shape.h:126
void addPadPrimitivesToPolygon(SHAPE_POLY_SET *aMergedPolygon, int aError, ERROR_LOC aErrorLoc) const
void BooleanAdd(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset difference For aFastMode meaning, see function booleanOp.
void DeletePrimitivesList()
Clear the basic shapes list.
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:215
void AddPrimitiveRect(const wxPoint &aStart, const wxPoint &aEnd, int aThickness, bool aFilled)
int Distance(const VECTOR2I &aP, bool aOutlineOnly=false) const
Compute the minimum distance between the line chain and a point aP.
void SetArcAngleAndEnd(double aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:506
void ReplacePrimitives(const std::vector< std::shared_ptr< PCB_SHAPE >> &aPrimitivesList)
Clear the current custom shape primitives list and import a new list.
void AddPrimitiveSegment(const wxPoint &aStart, const wxPoint &aEnd, int aThickness)
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.
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:590
void SetBezierC1(const wxPoint &aPt)
Definition: eda_shape.h:144
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:116
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
void SetStart(const wxPoint &aStart)
Definition: eda_shape.h:101
const auto NULLOPT
Definition: optional.h:9
Represent a set of closed polygons.
const wxSize & GetSize() const
Definition: pad.h:233
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, const wxPoint &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
void SetWidth(int aWidth)
Definition: eda_shape.h:88
void Simplify(POLYGON_MODE aFastMode)
void SetCenter(const wxPoint &aCenter)
Definition: eda_shape.cpp:416
void Fracture(POLYGON_MODE aFastMode)
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
void AddPrimitiveCurve(const wxPoint &aStart, const wxPoint &aEnd, const wxPoint &aCtrl1, const wxPoint &aCtrl2, int aThickness)
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new hole to the given outline (default: last) and returns its index.
void SetPolyPoints(const std::vector< wxPoint > &aPoints)
Definition: eda_shape.cpp:1021
std::vector< std::shared_ptr< PCB_SHAPE > > m_editPrimitives
Definition: pad.h:690
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
void AddPrimitivePoly(const SHAPE_POLY_SET &aPoly, int aThickness, bool aFilled)
Has meaning only for custom shape pads.
void SetDirty()
Definition: pad.h:361
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:91
void AddPrimitive(PCB_SHAPE *aPrimitive)
Add item to the custom shape primitives list.
T rescale(T aNumerator, T aValue, T aDenominator)
Scale a number (value) by rational (numerator/denominator).
Definition: util.h:98
void AddPrimitiveArc(const wxPoint &aCenter, const wxPoint &aStart, int aArcAngle, int aThickness)
coord_type GetHeight() const
Definition: box2.h:181
void SetBezierC2(const wxPoint &aPt)
Definition: eda_shape.h:147
void MergePrimitivesAsPolygon(SHAPE_POLY_SET *aMergedPolygon, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Merge all basic shapes to a SHAPE_POLY_SET.
boost::optional< T > OPT
Definition: optional.h:7
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:36
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.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
bool GetBestAnchorPosition(VECTOR2I &aPos)
PAD_SHAPE GetAnchorPadShape() const
Definition: pad.h:183
void AddPrimitiveCircle(const wxPoint &aCenter, int aRadius, int aThickness, bool aFilled)
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...