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 );
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->SetWidth( aThickness );
59  item->SetParent( this );
60  m_editPrimitives.emplace_back( item );
61  }
62 
63  SetDirty();
64 }
65 
66 
67 void PAD::AddPrimitivePoly( const std::vector<wxPoint>& 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->SetWidth( aThickness );
73  item->SetParent( this );
74  m_editPrimitives.emplace_back( item );
75  SetDirty();
76 }
77 
78 
79 void PAD::AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& 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->SetWidth( aThickness );
86  item->SetParent( this );
87  m_editPrimitives.emplace_back( item );
88  SetDirty();
89 }
90 
91 
92 void PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aArcAngle,
93  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->SetWidth( aThickness );
101  item->SetParent( this );
102  m_editPrimitives.emplace_back( item );
103  SetDirty();
104 }
105 
106 
107 void PAD::AddPrimitiveCurve( const wxPoint& aStart, const wxPoint& aEnd, const wxPoint& aCtrl1,
108  const wxPoint& 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->SetWidth( aThickness );
117  item->SetParent( this );
118  m_editPrimitives.emplace_back( item );
119  SetDirty();
120 }
121 
122 
123 void PAD::AddPrimitiveCircle( const wxPoint& 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( wxPoint( aCenter.x + aRadius, aCenter.y ) );
129  item->SetWidth( aThickness );
130  item->SetParent( this );
131  m_editPrimitives.emplace_back( item );
132  SetDirty();
133 }
134 
135 
136 void PAD::AddPrimitiveRect( const wxPoint& aStart, const wxPoint& 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->SetWidth( aThickness );
144  item->SetParent( this );
145  m_editPrimitives.emplace_back( item );
146  SetDirty();
147 }
148 
149 
150 void PAD::ReplacePrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
151 {
152  // clear old list
154 
155  // Import to the given shape list
156  if( aPrimitivesList.size() )
157  AppendPrimitives( aPrimitivesList );
158 
159  SetDirty();
160 }
161 
162 
163 void PAD::AppendPrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
164 {
165  // Add duplicates of aPrimitivesList to the pad primitives list:
166  for( const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
167  AddPrimitive( new PCB_SHAPE( *prim ) );
168 
169  SetDirty();
170 }
171 
172 
173 void PAD::AddPrimitive( PCB_SHAPE* aPrimitive )
174 {
175  aPrimitive->SetParent( this );
176  m_editPrimitives.emplace_back( aPrimitive );
177 
178  SetDirty();
179 }
180 
181 
182 // clear the basic shapes list and associated data
184 {
185  m_editPrimitives.clear();
186 
187  SetDirty();
188 }
189 
190 
191 void PAD::addPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aError,
192  ERROR_LOC aErrorLoc ) const
193 {
194  SHAPE_POLY_SET polyset;
195 
196  for( const std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
197  {
198  primitive->TransformShapeWithClearanceToPolygon( polyset, UNDEFINED_LAYER, 0, aError,
199  aErrorLoc );
200  }
201 
203 
204  // Merge all polygons with the initial pad anchor shape
205  if( polyset.OutlineCount() )
206  {
207  aMergedPolygon->BooleanAdd( polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
208  aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
209  }
210 }
211 
212 void PAD::MergePrimitivesAsPolygon( SHAPE_POLY_SET* aMergedPolygon, ERROR_LOC aErrorLoc ) const
213 {
214  const BOARD* board = GetBoard();
215  int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
216 
217  aMergedPolygon->RemoveAllContours();
218 
219  // Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
220  // The anchor pad is always at 0,0
221  switch( GetAnchorPadShape() )
222  {
223  case PAD_SHAPE::RECT:
224  {
225  SHAPE_RECT rect( -GetSize().x / 2, -GetSize().y / 2, GetSize().x, GetSize().y );
226  aMergedPolygon->AddOutline( rect.Outline() );
227  }
228  break;
229 
230  default:
231  case PAD_SHAPE::CIRCLE:
232  TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError,
233  aErrorLoc );
234  break;
235  }
236 
237  addPadPrimitivesToPolygon( aMergedPolygon, maxError, aErrorLoc );
238 }
239 
240 
242 {
243  SHAPE_POLY_SET poly;
244  addPadPrimitivesToPolygon( &poly, ARC_LOW_DEF, ERROR_INSIDE );
245 
246  if( poly.OutlineCount() > 1 )
247  return false;
248 
249  const int minSteps = 10;
250  const int maxSteps = 50;
251 
252  int stepsX, stepsY;
253 
254  auto bbox = poly.BBox();
255 
256  if( bbox.GetWidth() < bbox.GetHeight() )
257  {
258  stepsX = minSteps;
259  stepsY = minSteps * (double) bbox.GetHeight() / (double )(bbox.GetWidth() + 1);
260  }
261  else
262  {
263  stepsY = minSteps;
264  stepsX = minSteps * (double) bbox.GetWidth() / (double )(bbox.GetHeight() + 1);
265  }
266 
267  stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) );
268  stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) );
269 
270  VECTOR2I center = bbox.Centre();
271 
272  int64_t minDist = std::numeric_limits<int64_t>::max();
273  int64_t minDistEdge;
274 
276  {
277  minDistEdge = GetSize().x;
278  }
279  else
280  {
281  minDistEdge = std::max( GetSize().x, GetSize().y );
282  }
283 
284  OPT<VECTOR2I> bestAnchor( []()->OPT<VECTOR2I> { return NULLOPT; }() );
285 
286  for( int y = 0; y < stepsY ; y++ )
287  {
288  for( int x = 0; x < stepsX; x++ )
289  {
290  VECTOR2I p = bbox.GetPosition();
291  p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) );
292  p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) );
293 
294  if( poly.Contains(p) )
295  {
296 
297  int dist = (center - p).EuclideanNorm();
298  int distEdge = poly.COutline(0).Distance( p, true );
299 
300  if( distEdge >= minDistEdge )
301  {
302  if( dist < minDist )
303  {
304  bestAnchor = p;
305  minDist = dist;
306  }
307  }
308  }
309  }
310  }
311 
312  if( bestAnchor )
313  {
314  aPos = *bestAnchor;
315  return true;
316  }
317 
318  return false;
319 }
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:92
void SetEnd(const wxPoint &aEnd)
Definition: eda_shape.h:135
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.
bool HasHoles() const
Return true if the polygon set has any holes that share a vertex.
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:547
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:153
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:115
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:110
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:97
void Simplify(POLYGON_MODE aFastMode)
void SetCenter(const wxPoint &aCenter)
Definition: eda_shape.cpp:419
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:1085
std::vector< std::shared_ptr< PCB_SHAPE > > m_editPrimitives
Definition: pad.h:691
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:191
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 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:156
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...