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  std::vector<wxPoint> points;
43 
44  // If aPoly has holes, convert it to a polygon with no holes.
45  SHAPE_POLY_SET poly_no_hole;
46  poly_no_hole.Append( aPoly );
48 
49  for( auto iter = poly_no_hole.CIterate(); iter; iter++ )
50  points.emplace_back( iter->x, iter->y );
51 
52  AddPrimitivePoly( points, aThickness, aFilled );
53 }
54 
55 
56 void PAD::AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness, bool aFilled )
57 {
58  PCB_SHAPE* item = new PCB_SHAPE();
59  item->SetShape( SHAPE_T::POLY );
60  item->SetFilled( aFilled );
61  item->SetPolyPoints( aPoly );
62  item->SetWidth( aThickness );
63  item->SetParent( this );
64  m_editPrimitives.emplace_back( item );
65  SetDirty();
66 }
67 
68 
69 void PAD::AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness )
70 {
71  PCB_SHAPE* item = new PCB_SHAPE();
72  item->SetShape( 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();
87  item->SetShape( SHAPE_T::ARC );
88  item->SetFilled( false );
89  item->SetCenter( aCenter );
90  item->SetArcStart( aStart );
91  item->SetAngle( aArcAngle );
92  item->SetWidth( aThickness );
93  item->SetParent( this );
94  m_editPrimitives.emplace_back( item );
95  SetDirty();
96 }
97 
98 
99 void PAD::AddPrimitiveCurve( const wxPoint& aStart, const wxPoint& aEnd, const wxPoint& aCtrl1,
100  const wxPoint& aCtrl2, int aThickness )
101 {
102  PCB_SHAPE* item = new PCB_SHAPE();
103  item->SetShape( SHAPE_T::BEZIER );
104  item->SetFilled( false );
105  item->SetStart( aStart );
106  item->SetEnd( aEnd );
107  item->SetBezierC1( aCtrl1 );
108  item->SetBezierC2( aCtrl2 );
109  item->SetWidth( aThickness );
110  item->SetParent( this );
111  m_editPrimitives.emplace_back( item );
112  SetDirty();
113 }
114 
115 
116 void PAD::AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness, bool aFilled )
117 {
118  PCB_SHAPE* item = new PCB_SHAPE();
119  item->SetShape( SHAPE_T::CIRCLE );
120  item->SetFilled( aFilled );
121  item->SetStart( aCenter );
122  item->SetEnd( wxPoint( aCenter.x + aRadius, aCenter.y ) );
123  item->SetWidth( aThickness );
124  item->SetParent( this );
125  m_editPrimitives.emplace_back( item );
126  SetDirty();
127 }
128 
129 
130 void PAD::AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness,
131  bool aFilled)
132 {
133  PCB_SHAPE* item = new PCB_SHAPE();
134  item->SetShape( SHAPE_T::RECT );
135  item->SetFilled( aFilled );
136  item->SetStart( aStart );
137  item->SetEnd( aEnd );
138  item->SetWidth( aThickness );
139  item->SetParent( this );
140  m_editPrimitives.emplace_back( item );
141  SetDirty();
142 }
143 
144 
145 void PAD::ReplacePrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
146 {
147  // clear old list
149 
150  // Import to the given shape list
151  if( aPrimitivesList.size() )
152  AppendPrimitives( aPrimitivesList );
153 
154  SetDirty();
155 }
156 
157 
158 void PAD::AppendPrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
159 {
160  // Add duplicates of aPrimitivesList to the pad primitives list:
161  for( const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
162  AddPrimitive( new PCB_SHAPE( *prim ) );
163 
164  SetDirty();
165 }
166 
167 
168 void PAD::AddPrimitive( PCB_SHAPE* aPrimitive )
169 {
170  aPrimitive->SetParent( this );
171  m_editPrimitives.emplace_back( aPrimitive );
172 
173  SetDirty();
174 }
175 
176 
177 // clear the basic shapes list and associated data
179 {
180  m_editPrimitives.clear();
181 
182  SetDirty();
183 }
184 
185 
187  int aError, ERROR_LOC aErrorLoc ) const
188 {
189  SHAPE_POLY_SET polyset;
190 
191  for( const std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
192  primitive->TransformShapeWithClearanceToPolygon( polyset, aLayer, 0, aError, aErrorLoc );
193 
195 
196  // Merge all polygons with the initial pad anchor shape
197  if( polyset.OutlineCount() )
198  {
199  aMergedPolygon->BooleanAdd( polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
200  aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
201  }
202 }
203 
205  ERROR_LOC aErrorLoc ) const
206 {
207  BOARD* board = GetBoard();
208  int maxError = board ? board->GetDesignSettings().m_MaxError: ARC_HIGH_DEF;
209 
210  aMergedPolygon->RemoveAllContours();
211 
212  // Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
213  // The anchor pad is always at 0,0
214  switch( GetAnchorPadShape() )
215  {
216  case PAD_SHAPE::RECT:
217  {
218  SHAPE_RECT rect( -GetSize().x / 2, -GetSize().y / 2, GetSize().x, GetSize().y );
219  aMergedPolygon->AddOutline( rect.Outline() );
220  }
221  break;
222 
223  default:
224  case PAD_SHAPE::CIRCLE:
225  TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError,
226  ERROR_INSIDE );
227  break;
228  }
229 
230  addPadPrimitivesToPolygon( aMergedPolygon, aLayer, maxError, aErrorLoc );
231 }
232 
233 
235 {
236  SHAPE_POLY_SET poly;
238 
239  if( poly.OutlineCount() > 1 )
240  return false;
241 
242  const int minSteps = 10;
243  const int maxSteps = 50;
244 
245  int stepsX, stepsY;
246 
247  auto bbox = poly.BBox();
248 
249  if( bbox.GetWidth() < bbox.GetHeight() )
250  {
251  stepsX = minSteps;
252  stepsY = minSteps * (double) bbox.GetHeight() / (double )(bbox.GetWidth() + 1);
253  }
254  else
255  {
256  stepsY = minSteps;
257  stepsX = minSteps * (double) bbox.GetWidth() / (double )(bbox.GetHeight() + 1);
258  }
259 
260  stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) );
261  stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) );
262 
263  VECTOR2I center = bbox.Centre();
264 
265  int64_t minDist = std::numeric_limits<int64_t>::max();
266  int64_t minDistEdge;
267 
269  {
270  minDistEdge = GetSize().x;
271  }
272  else
273  {
274  minDistEdge = std::max( GetSize().x, GetSize().y );
275  }
276 
277  OPT<VECTOR2I> bestAnchor( []()->OPT<VECTOR2I> { return NULLOPT; }() );
278 
279  for( int y = 0; y < stepsY ; y++ )
280  {
281  for( int x = 0; x < stepsX; x++ )
282  {
283  VECTOR2I p = bbox.GetPosition();
284  p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) );
285  p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) );
286 
287  if( poly.Contains(p) )
288  {
289 
290  int dist = (center - p).EuclideanNorm();
291  int distEdge = poly.COutline(0).Distance( p, true );
292 
293  if( distEdge >= minDistEdge )
294  {
295  if( dist < minDist )
296  {
297  bestAnchor = p;
298  minDist = dist;
299  }
300  }
301  }
302  }
303  }
304 
305  if( bestAnchor )
306  {
307  aPos = *bestAnchor;
308  return true;
309  }
310 
311  return false;
312 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:148
Arcs (with rounded ends)
Bezier Curve.
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 SetShape(SHAPE_T aShape)
Definition: pcb_shape.h:109
polygon (not yet used for tracks, but could be in microwave apps)
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 SetFilled(bool aFlag)
Definition: pcb_shape.h:73
usual segment : line with rounded ends
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 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:588
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:116
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, wxPoint 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...
PCB_LAYER_ID
A quick note on layer IDs:
const auto NULLOPT
Definition: optional.h:9
Represent a set of closed polygons.
const wxSize & GetSize() const
Definition: pad.h:229
virtual BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:51
void Simplify(POLYGON_MODE aFastMode)
void SetCenter(const wxPoint &aCenterPoint)
Definition: pcb_shape.h:198
void MergePrimitivesAsPolygon(SHAPE_POLY_SET *aMergedPolygon, PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Merge all basic shapes to a SHAPE_POLY_SET.
CONST_ITERATOR CIterate(int aFirst, int aLast, bool aIterateHoles=false) const
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.
std::vector< std::shared_ptr< PCB_SHAPE > > m_editPrimitives
Definition: pad.h:687
void SetStart(const wxPoint &aStart)
Definition: pcb_shape.h:127
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:358
void AddPrimitive(PCB_SHAPE *aPrimitive)
Add item to the custom shape primitives list.
void SetWidth(int aWidth)
Definition: pcb_shape.h:96
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
boost::optional< T > OPT
Definition: optional.h:7
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 SetBezierC2(const wxPoint &aPoint)
Definition: pcb_shape.h:115
segment with non rounded ends
virtual void SetAngle(double aAngle, bool aUpdateEnd=true)
Set the angle for arcs, and normalizes it within the range 0 - 360 degrees.
Definition: pcb_shape.cpp:519
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)
void SetPolyPoints(const std::vector< wxPoint > &aPoints)
Definition: pcb_shape.cpp:1153
PAD_SHAPE GetAnchorPadShape() const
Definition: pad.h:179
void SetEnd(const wxPoint &aEnd)
Definition: pcb_shape.h:137
void SetBezierC1(const wxPoint &aPoint)
Definition: pcb_shape.h:112
void SetArcStart(const wxPoint &aArcStartPoint)
Initialize the start arc point.
Definition: pcb_shape.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...
void addPadPrimitivesToPolygon(SHAPE_POLY_SET *aMergedPolygon, PCB_LAYER_ID aLayer, int aError, ERROR_LOC aErrorLoc) const