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();
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();
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();
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();
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();
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();
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 
204  ERROR_LOC aErrorLoc ) const
205 {
206  BOARD* board = GetBoard();
207  int maxError = board ? board->GetDesignSettings().m_MaxError: ARC_HIGH_DEF;
208 
209  aMergedPolygon->RemoveAllContours();
210 
211  // Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
212  // The anchor pad is always at 0,0
213  switch( GetAnchorPadShape() )
214  {
215  case PAD_SHAPE::RECT:
216  {
217  SHAPE_RECT rect( -GetSize().x / 2, -GetSize().y / 2, GetSize().x, GetSize().y );
218  aMergedPolygon->AddOutline( rect.Outline() );
219  }
220  break;
221 
222  default:
223  case PAD_SHAPE::CIRCLE:
224  TransformCircleToPolygon( *aMergedPolygon, wxPoint( 0, 0 ), GetSize().x / 2, maxError,
225  ERROR_INSIDE );
226  break;
227  }
228 
229  addPadPrimitivesToPolygon( aMergedPolygon, aLayer, maxError, aErrorLoc );
230 }
231 
232 
234 {
235  SHAPE_POLY_SET poly;
237 
238  if( poly.OutlineCount() > 1 )
239  return false;
240 
241  const int minSteps = 10;
242  const int maxSteps = 50;
243 
244  int stepsX, stepsY;
245 
246  auto bbox = poly.BBox();
247 
248  if( bbox.GetWidth() < bbox.GetHeight() )
249  {
250  stepsX = minSteps;
251  stepsY = minSteps * (double) bbox.GetHeight() / (double )(bbox.GetWidth() + 1);
252  }
253  else
254  {
255  stepsY = minSteps;
256  stepsX = minSteps * (double) bbox.GetWidth() / (double )(bbox.GetHeight() + 1);
257  }
258 
259  stepsX = std::max(minSteps, std::min( maxSteps, stepsX ) );
260  stepsY = std::max(minSteps, std::min( maxSteps, stepsY ) );
261 
262  VECTOR2I center = bbox.Centre();
263 
264  int64_t minDist = std::numeric_limits<int64_t>::max();
265  int64_t minDistEdge;
266 
268  {
269  minDistEdge = GetSize().x;
270  }
271  else
272  {
273  minDistEdge = std::max( GetSize().x, GetSize().y );
274  }
275 
276  OPT<VECTOR2I> bestAnchor( []()->OPT<VECTOR2I> { return NULLOPT; }() );
277 
278  for( int y = 0; y < stepsY ; y++ )
279  {
280  for( int x = 0; x < stepsX; x++ )
281  {
282  VECTOR2I p = bbox.GetPosition();
283  p.x += rescale( x, bbox.GetWidth(), (stepsX - 1) );
284  p.y += rescale( y, bbox.GetHeight(), (stepsY - 1) );
285 
286  if( poly.Contains(p) )
287  {
288 
289  int dist = (center - p).EuclideanNorm();
290  int distEdge = poly.COutline(0).Distance( p, true );
291 
292  if( distEdge >= minDistEdge )
293  {
294  if( dist < minDist )
295  {
296  bestAnchor = p;
297  minDist = dist;
298  }
299  }
300  }
301  }
302  }
303 
304  if( bestAnchor )
305  {
306  aPos = *bestAnchor;
307  return true;
308  }
309 
310  return false;
311 }
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
void AddPrimitiveRect(const wxPoint &aStart, const wxPoint &aEnd, int aThickness, bool aFilled)
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.h:593
int Distance(const VECTOR2I &aP, bool aOutlineOnly=false) const
Function Distance()
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.
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:166
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
polygon (not yet used for tracks, but could be in microwave apps)
PCB_LAYER_ID
A quick note on layer IDs:
const auto NULLOPT
Definition: optional.h:9
Represent a set of closed polygons.
segment with non rounded ends
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
void SetBezControl1(const wxPoint &aPoint)
Definition: pcb_shape.h:132
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:690
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:361
usual segment : line with rounded ends
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
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 SetShape(PCB_SHAPE_TYPE aShape)
Definition: pcb_shape.h:129
Arcs (with rounded ends)
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:466
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:1076
PAD_SHAPE GetAnchorPadShape() const
Definition: pad.h:182
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