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_item.h>
27 #include <pcb_shape.h>
28 #include <pad.h>
30 #include <geometry/shape_rect.h>
31 
32 
33 /*
34  * Has meaning only for free shape pads.
35  * add a free shape to the shape list.
36  * the shape is a polygon (can be with thick outline), segment, circle or arc
37  */
38 
39 void PAD::AddPrimitivePoly( const SHAPE_POLY_SET& aPoly, int aThickness, bool aFilled )
40 {
41  std::vector<wxPoint> points;
42 
43  // If aPoly has holes, convert it to a polygon with no holes.
44  SHAPE_POLY_SET poly_no_hole;
45  poly_no_hole.Append( aPoly );
47 
48  for( auto iter = poly_no_hole.CIterate(); iter; iter++ )
49  points.emplace_back( iter->x, iter->y );
50 
51  AddPrimitivePoly( points, aThickness, aFilled );
52 }
53 
54 
55 void PAD::AddPrimitivePoly( const std::vector<wxPoint>& aPoly, int aThickness, bool aFilled )
56 {
57  PCB_SHAPE* item = new PCB_SHAPE();
58  item->SetShape( S_POLYGON );
59  item->SetFilled( aFilled );
60  item->SetPolyPoints( aPoly );
61  item->SetWidth( aThickness );
62  item->SetParent( this );
63  m_editPrimitives.emplace_back( item );
64  SetDirty();
65 }
66 
67 
68 void PAD::AddPrimitiveSegment( const wxPoint& aStart, const wxPoint& aEnd, int aThickness )
69 {
70  PCB_SHAPE* item = new PCB_SHAPE();
71  item->SetShape( S_SEGMENT );
72  item->SetFilled( false );
73  item->SetStart( aStart );
74  item->SetEnd( aEnd );
75  item->SetWidth( aThickness );
76  item->SetParent( this );
77  m_editPrimitives.emplace_back( item );
78  SetDirty();
79 }
80 
81 
82 void PAD::AddPrimitiveArc( const wxPoint& aCenter, const wxPoint& aStart, int aArcAngle,
83  int aThickness )
84 {
85  PCB_SHAPE* item = new PCB_SHAPE();
86  item->SetShape( S_ARC );
87  item->SetFilled( false );
88  item->SetCenter( aCenter );
89  item->SetArcStart( aStart );
90  item->SetAngle( 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();
102  item->SetShape( S_CURVE );
103  item->SetFilled( false );
104  item->SetStart( aStart );
105  item->SetEnd( aEnd );
106  item->SetBezControl1( aCtrl1 );
107  item->SetBezControl2( aCtrl2 );
108  item->SetWidth( aThickness );
109  item->SetParent( this );
110  m_editPrimitives.emplace_back( item );
111  SetDirty();
112 }
113 
114 
115 void PAD::AddPrimitiveCircle( const wxPoint& aCenter, int aRadius, int aThickness, bool aFilled )
116 {
117  PCB_SHAPE* item = new PCB_SHAPE();
118  item->SetShape( S_CIRCLE );
119  item->SetFilled( aFilled );
120  item->SetStart( aCenter );
121  item->SetEnd( wxPoint( aCenter.x + aRadius, aCenter.y ) );
122  item->SetWidth( aThickness );
123  item->SetParent( this );
124  m_editPrimitives.emplace_back( item );
125  SetDirty();
126 }
127 
128 
129 void PAD::AddPrimitiveRect( const wxPoint& aStart, const wxPoint& aEnd, int aThickness,
130  bool aFilled)
131 {
132  PCB_SHAPE* item = new PCB_SHAPE();
133  item->SetShape( S_RECT );
134  item->SetFilled( aFilled );
135  item->SetStart( aStart );
136  item->SetEnd( aEnd );
137  item->SetWidth( aThickness );
138  item->SetParent( this );
139  m_editPrimitives.emplace_back( item );
140  SetDirty();
141 }
142 
143 
144 void PAD::ReplacePrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
145 {
146  // clear old list
148 
149  // Import to the given shape list
150  if( aPrimitivesList.size() )
151  AppendPrimitives( aPrimitivesList );
152 
153  SetDirty();
154 }
155 
156 
157 void PAD::AppendPrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
158 {
159  // Add duplicates of aPrimitivesList to the pad primitives list:
160  for( const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
161  AddPrimitive( new PCB_SHAPE( *prim ) );
162 
163  SetDirty();
164 }
165 
166 
167 void PAD::AddPrimitive( PCB_SHAPE* aPrimitive )
168 {
169  aPrimitive->SetParent( this );
170  m_editPrimitives.emplace_back( aPrimitive );
171 
172  SetDirty();
173 }
174 
175 
176 // clear the basic shapes list and associated data
178 {
179  m_editPrimitives.clear();
180 
181  SetDirty();
182 }
183 
184 
186  int aError, ERROR_LOC aErrorLoc ) const
187 {
188  SHAPE_POLY_SET polyset;
189 
190  for( const std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
191  primitive->TransformShapeWithClearanceToPolygon( polyset, aLayer, 0, aError, aErrorLoc );
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, PCB_LAYER_ID aLayer ) const
204 {
205  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, aLayer, maxError, ERROR_INSIDE );
229 }
230 
231 
233 {
234  SHAPE_POLY_SET poly;
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:148
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 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 SetBezControl2(const wxPoint &aPoint)
Definition: pcb_shape.h:135
void SetFilled(bool aFlag)
Definition: pcb_shape.h:94
polygon (not yet used for tracks, but could be in microwave apps)
Definition: board_item.h:54
void AddPrimitiveRect(const wxPoint &aStart, const wxPoint &aEnd, int aThickness, bool aFilled)
usual segment : line with rounded ends
Definition: board_item.h:50
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.h:591
int Distance(const VECTOR2I &aP, bool aOutlineOnly=false) const
Function Distance()
Arcs (with rounded ends)
Definition: board_item.h:52
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.
segment with non rounded ends
Definition: board_item.h:51
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:165
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
void SetShape(PCB_SHAPE_TYPE_T aShape)
Definition: pcb_shape.h:129
Represent a set of closed polygons.
const wxSize & GetSize() const
Definition: pad.h:232
virtual BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:46
void Simplify(POLYGON_MODE aFastMode)
void SetCenter(const wxPoint &aCenterPoint)
For arcs and circles:
Definition: pcb_shape.h:229
PAD_SHAPE_T GetAnchorPadShape() const
Definition: pad.h:182
void SetBezControl1(const wxPoint &aPoint)
Definition: pcb_shape.h:132
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:682
void MergePrimitivesAsPolygon(SHAPE_POLY_SET *aMergedPolygon, PCB_LAYER_ID aLayer) const
Merge all basic shapes to a SHAPE_POLY_SET.
void SetStart(const wxPoint &aStart)
Definition: pcb_shape.h:148
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:353
void AddPrimitive(PCB_SHAPE *aPrimitive)
Add item to the custom shape primitives list.
void SetWidth(int aWidth)
Definition: pcb_shape.h:117
T rescale(T aNumerator, T aValue, T aDenominator)
Function rescale()
Definition: util.h:95
void AddPrimitiveArc(const wxPoint &aCenter, const wxPoint &aStart, int aArcAngle, int aThickness)
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, wxPoint aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc)
Function TransformCircleToPolygon convert a circle to a polygon, using multiple straight lines.
coord_type GetHeight() const
Definition: box2.h:198
boost::optional< T > OPT
Definition: optional.h:7
ring
Definition: board_item.h:53
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.
virtual void SetAngle(double aAngle, bool aUpdateEnd=true)
Sets the angle for arcs, and normalizes it within the range 0 - 360 degrees.
Definition: pcb_shape.cpp:465
Bezier Curve.
Definition: board_item.h:55
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:1075
void SetEnd(const wxPoint &aEnd)
Definition: pcb_shape.h:159
void SetArcStart(const wxPoint &aArcStartPoint)
Initialize the start arc point.
Definition: pcb_shape.h:213
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