KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_via.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2014 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * Author: Tomasz Wlostowski <[email protected]>
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "pns_via.h"
23#include "pns_node.h"
24#include "pns_utils.h"
25#include "pns_router.h"
26#include "pns_debug_decorator.h"
27
28#include <geometry/shape_rect.h>
29#include <math/box2.h>
30
31namespace PNS {
32
33int VIA::EffectiveLayer( int aLayer ) const
34{
35 switch( m_stackMode )
36 {
37 default:
39 return ALL_LAYERS;
40
42 if( aLayer == m_layers.Start() || aLayer == m_layers.End() )
43 return aLayer;
44
45 if( m_layers.Start() + 1 < m_layers.End() )
46 return m_layers.Start() + 1;
47
48 return m_layers.Start();
49
51 return m_layers.Overlaps( aLayer ) ? aLayer : m_layers.Start();
52 }
53}
54
55
56std::vector<int> VIA::UniqueShapeLayers() const
57{
58 switch( m_stackMode )
59 {
60 default:
62 return { ALL_LAYERS };
63
65 return { ALL_LAYERS, INNER_LAYERS, m_layers.End() };
66
68 std::vector<int> ret;
69
70 for( int l = m_layers.Start(); l <= m_layers.End(); l++ )
71 ret.push_back( l );
72
73 return ret;
74 }
75}
76
77
78bool VIA::ConnectsLayer( int aLayer ) const
79{
81 return aLayer == m_layers.Start() || aLayer == m_layers.End();
82
83 return m_layers.Overlaps( aLayer );
84}
85
86
87void VIA::SetStackMode( STACK_MODE aStackMode )
88{
89 m_stackMode = aStackMode;
90
91 wxASSERT_MSG( m_stackMode != STACK_MODE::FRONT_INNER_BACK || m_layers.Start() == 0,
92 wxT( "Cannot use FRONT_INNER_BACK with blind/buried vias!" ) );
93
94 // In theory, it might be good to do some housekeeping on m_diameters and m_shapes here,
95 // but it's not yet clear if the stack mode needs to be changed after initial creation.
96}
97
98
99bool VIA::PadstackMatches( const VIA& aOther ) const
100{
101 std::vector<int> myLayers = UniqueShapeLayers();
102 std::vector<int> otherLayers = aOther.UniqueShapeLayers();
103
104 if( !std::equal( myLayers.begin(), myLayers.end(), otherLayers.begin() ) )
105 return false;
106
107 for( int i : myLayers )
108 {
109 if( Diameter( i ) != aOther.Diameter( i ) )
110 return false;
111 }
112
113 return true;
114}
115
116
117bool VIA::PushoutForce( NODE* aNode, const ITEM* aOther, VECTOR2I& aForce )
118{
119 int clearance = aNode->GetClearance( this, aOther, false );
120 VECTOR2I elementForce;
121
122 for( int layer : RelevantShapeLayers( aOther ) )
123 {
124 aOther->Shape( layer )->Collide( Shape( layer ), clearance, &elementForce );
125
126 if( elementForce.SquaredEuclideanNorm() > aForce.SquaredEuclideanNorm() )
127 aForce = elementForce;
128 }
129
130 return ( aForce != VECTOR2I( 0, 0 ) );
131}
132
133
134bool VIA::PushoutForce( NODE* aNode, const VECTOR2I& aDirection, VECTOR2I& aForce,
135 int aCollisionMask, int aMaxIterations )
136{
137 int iter = 0;
138 VIA mv( *this );
139 VECTOR2I totalForce;
140
142 PNS_DBG( dbg, AddPoint, Pos(), YELLOW, 100000, wxString::Format( "via-force-init-pos, iter %d", aMaxIterations ) );
143
144 while( iter < aMaxIterations )
145 {
147 opt.m_limitCount = 1;
148 opt.m_kindMask = aCollisionMask;
149 opt.m_useClearanceEpsilon = false;
150
151 NODE::OPT_OBSTACLE obs = aNode->CheckColliding( &mv, opt );
152
153 if( !obs )
154 break;
155
156 VECTOR2I force;
157 bool collFound = mv.PushoutForce( aNode, obs->m_item, force );
158
159 if( !collFound )
160 {
161 if( obs )
162 {
163 // might happen (although rarely) that we see a collision, but the MTV
164 // is zero... Assume force propagation has failed in such case.
165 return false;
166 }
167 PNS_DBG( dbg, Message, wxString::Format( "no-coll %d", iter ) );
168 break;
169 }
170
171 // TODO(JE) padstacks -- what is the correct logic here?
172 const int threshold = Diameter( EffectiveLayer( 0 ) ) / 4; // another stupid heuristic.
173 const int forceMag = force.EuclideanNorm();
174
175 // We've been through a lot of iterations already and our pushout force is still too big?
176 // Perhaps the barycentric force goes in the wrong direction, let's try to move along
177 // the 'lead' vector instead (usually backwards to the cursor)
178 if( iter > aMaxIterations / 2 && forceMag > threshold )
179 {
180 VECTOR2I l = aDirection.Resize( threshold );
181 totalForce += l;
182
184 ff.Append( mv.Pos() );
185 ff.Append( mv.Pos() + l );
186
187 mv.SetPos( mv.Pos() + l );
188
189 PNS_DBG( dbg, AddShape, &ff, YELLOW, 100000, "via-force-lead" );
190 }
191 else if( collFound ) // push along the minmum translation vector
192 {
193 // Limit the force magnitude to, say, 25% of the via diameter
194 // This adds a few iterations for large areas (e.g. keepouts)
195 // But makes the algorithm more predictable and less 'jumpy'
196 if( forceMag > threshold )
197 {
198 force.Resize( threshold );
199 }
200
201 totalForce += force;
202
204 ff.Append( mv.Pos() );
205 ff.Append( mv.Pos() + force );
206
207 mv.SetPos( mv.Pos() + force );
208
209 PNS_DBG( dbg, AddShape, &ff, WHITE, 100000, "via-force-coll" );
210 }
211
212 iter++;
213 }
214
215 if( iter == aMaxIterations )
216 return false;
217
218 PNS_DBG( dbg, AddPoint, ( Pos() + totalForce ), WHITE, 1000000, "via-force-new" );
219
220 aForce = totalForce;
221
222 return true;
223}
224
225
226const SHAPE_LINE_CHAIN VIA::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const
227{
228 wxASSERT_MSG( aLayer >= 0 || m_stackMode == STACK_MODE::NORMAL,
229 wxT( "Warning: VIA::Hull called with invalid layer but viastack is complex" ) );
230
231 int cl = ( aClearance + aWalkaroundThickness / 2 );
232 int width = Diameter( aLayer );
233
234 if( m_hole && !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) )
235 width = m_hole->Radius() * 2;
236
237 // Chamfer = width * ( 1 - sqrt(2)/2 ) for equilateral octagon
238 return OctagonalHull( m_pos - VECTOR2I( width / 2, width / 2 ),
239 VECTOR2I( width, width ),
240 cl, ( 2 * cl + width ) * ( 1.0 - M_SQRT1_2 ) );
241}
242
243
245{
246 VIA* v = new VIA();
247
248 v->m_uid = m_uid; // fixme: oop
249 v->m_parent = m_parent;
251
252 v->SetNet( Net() );
253 v->SetLayers( Layers() );
254 v->m_movable = m_movable;
255 v->m_pos = m_pos;
258 v->m_drill = m_drill;
259
260 for( const auto& [layer, shape] : m_shapes )
261 v->m_shapes[layer] = SHAPE_CIRCLE( m_pos, shape.GetRadius() );
262
264 v->m_rank = m_rank;
265 v->m_marker = m_marker;
267 v->m_viaType = m_viaType;
268 v->m_parent = m_parent;
269 v->m_isFree = m_isFree;
271
272 return v;
273}
274
275
276OPT_BOX2I VIA::ChangedArea( const VIA* aOther ) const
277{
278 if( aOther->Pos() != Pos() )
279 {
280 BOX2I tmp;
281
282 for( int layer : UniqueShapeLayers() )
283 tmp.Merge( Shape( layer )->BBox() );
284
285 for( int layer : aOther->UniqueShapeLayers() )
286 tmp.Merge( aOther->Shape( layer )->BBox() );
287
288 return tmp;
289 }
290
291 return OPT_BOX2I();
292}
293
294
296{
297 VIA_HANDLE h;
298 h.pos = Pos();
299 h.layers = Layers();
300 h.net = Net();
301 h.valid = true;
302 return h;
303}
304
305
306const std::string VIA::Format( ) const
307{
308 std::stringstream ss;
309 ss << ITEM::Format() << " drill " << m_drill << " ";
310 // TODO(JE) padstacks
311 ss << Shape( 0 )->Format( false );
312 return ss.str();
313}
314
315}
std::optional< BOX2I > OPT_BOX2I
Definition: box2.h:926
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:658
int Radius() const
Definition: pns_hole.cpp:103
static HOLE * MakeCircularHole(const VECTOR2I &pos, int radius, PNS_LAYER_RANGE aLayers)
Definition: pns_hole.cpp:131
Base class for PNS router board items.
Definition: pns_item.h:98
void SetLayers(const PNS_LAYER_RANGE &aLayers)
Definition: pns_item.h:213
BOARD_ITEM * m_sourceItem
Definition: pns_item.h:319
virtual const std::string Format() const
Definition: pns_item.cpp:338
bool m_isVirtual
Definition: pns_item.h:330
virtual const SHAPE * Shape(int aLayer) const
Return the geometrical shape of the item.
Definition: pns_item.h:242
const PNS_LAYER_RANGE & Layers() const
Definition: pns_item.h:212
bool m_movable
Definition: pns_item.h:325
virtual NET_HANDLE Net() const
Definition: pns_item.h:210
PNS_LAYER_RANGE m_layers
Definition: pns_item.h:323
bool m_routable
Definition: pns_item.h:329
std::set< int > RelevantShapeLayers(const ITEM *aOther) const
Returns the set of layers on which either this or the other item can have a unique shape.
Definition: pns_item.cpp:94
void SetNet(NET_HANDLE aNet)
Definition: pns_item.h:209
int m_marker
Definition: pns_item.h:327
BOARD_ITEM * m_parent
Definition: pns_item.h:317
int m_rank
Definition: pns_item.h:328
Keep the router "world" - i.e.
Definition: pns_node.h:232
int GetClearance(const ITEM *aA, const ITEM *aB, bool aUseClearanceEpsilon=true) const
Return the pre-set worst case clearance between any pair of items.
Definition: pns_node.cpp:129
OPT_OBSTACLE CheckColliding(const ITEM *aItem, int aKindMask=ITEM::ANY_T)
Check if the item collides with anything else in the world, and if found, returns the obstacle.
Definition: pns_node.cpp:410
std::optional< OBSTACLE > OPT_OBSTACLE
Definition: pns_node.h:242
virtual DEBUG_DECORATOR * GetDebugDecorator()=0
ROUTER_IFACE * GetInterface() const
Definition: pns_router.h:232
static ROUTER * GetInstance()
Definition: pns_router.cpp:81
VIA()
Definition: pns_via.h:79
int Diameter(int aLayer) const
Definition: pns_via.h:208
OPT_BOX2I ChangedArea(const VIA *aOther) const
Definition: pns_via.cpp:276
std::vector< int > UniqueShapeLayers() const override
Return a list of layers that have unique (potentially different) shapes.
Definition: pns_via.cpp:56
int m_drill
Definition: pns_via.h:296
const VECTOR2I & Pos() const
Definition: pns_via.h:184
virtual void SetHole(HOLE *aHole) override
Definition: pns_via.h:272
std::map< int, int > m_diameters
May contain 1..n diameters depending on m_stackMode.
Definition: pns_via.h:293
const VIA_HANDLE MakeHandle() const
Definition: pns_via.cpp:295
const SHAPE * Shape(int aLayer) const override
Return the geometrical shape of the item.
Definition: pns_via.h:246
virtual const std::string Format() const override
Definition: pns_via.cpp:306
bool PushoutForce(NODE *aNode, const VECTOR2I &aDirection, VECTOR2I &aForce, int aCollisionMask=ITEM::ANY_T, int aMaxIterations=10)
Definition: pns_via.cpp:134
std::map< int, SHAPE_CIRCLE > m_shapes
Definition: pns_via.h:294
int EffectiveLayer(int aLayer) const
Definition: pns_via.cpp:33
void SetPos(const VECTOR2I &aPos)
Definition: pns_via.h:186
VIATYPE m_viaType
Definition: pns_via.h:298
bool ConnectsLayer(int aLayer) const
Definition: pns_via.cpp:78
HOLE * m_hole
Definition: pns_via.h:301
bool PadstackMatches(const VIA &aOther) const
Definition: pns_via.cpp:99
VECTOR2I m_pos
Definition: pns_via.h:297
const SHAPE_LINE_CHAIN Hull(int aClearance=0, int aWalkaroundThickness=0, int aLayer=-1) const override
Definition: pns_via.cpp:226
STACK_MODE
Definition: pns_via.h:62
static constexpr int ALL_LAYERS
Definition: pns_via.h:76
static constexpr int INNER_LAYERS
Definition: pns_via.h:77
void SetStackMode(STACK_MODE aStackMode)
Definition: pns_via.cpp:87
PADSTACK::UNCONNECTED_LAYER_MODE m_unconnectedLayerMode
Definition: pns_via.h:299
bool m_isFree
Definition: pns_via.h:300
STACK_MODE m_stackMode
Definition: pns_via.h:290
VIA * Clone() const override
Return a deep copy of the item.
Definition: pns_via.cpp:244
int Start() const
Definition: pns_layerset.h:86
bool Overlaps(const PNS_LAYER_RANGE &aOther) const
Definition: pns_layerset.h:67
int End() const
Definition: pns_layerset.h:91
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition: shape.h:181
virtual const std::string Format(bool aCplusPlus=true) const
Definition: shape.cpp:47
virtual const BOX2I BBox(int aClearance=0) const =0
Compute a bounding box of the shape, with a margin of aClearance a collision.
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition: vector2d.h:307
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:283
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:385
@ WHITE
Definition: color4d.h:48
@ YELLOW
Definition: color4d.h:67
Push and Shove diff pair dimensions (gap) settings dialog.
const SHAPE_LINE_CHAIN OctagonalHull(const VECTOR2I &aP0, const VECTOR2I &aSize, int aClearance, int aChamfer)
Definition: pns_utils.cpp:37
#define PNS_DBG(dbg, method,...)
VECTOR2I pos
Definition: pns_via.h:53
NET_HANDLE net
Definition: pns_via.h:55
PNS_LAYER_RANGE layers
Definition: pns_via.h:54
int clearance
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695