KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_walkaround.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 (C) 2016-2023 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 <optional>
23
25
26#include "pns_walkaround.h"
27#include "pns_optimizer.h"
28#include "pns_router.h"
29#include "pns_debug_decorator.h"
30#include "pns_solid.h"
31
32
33namespace PNS {
34
35void WALKAROUND::start( const LINE& aInitialPath )
36{
37 m_iteration = 0;
38 for( int pol = 0 ; pol < MaxWalkPolicies; pol++)
39 {
41 m_currentResult.lines[ pol ] = aInitialPath;
43 }
44}
45
46
48{
50
52
53 if( ! m_restrictedSet.empty() )
54 {
55 opts.m_filter = [ this ] ( const ITEM* item ) -> bool
56 {
57 if( m_restrictedSet.find( item ) != m_restrictedSet.end() )
58 return true;
59 return false;
60 };
61 }
62
63 opts.m_useClearanceEpsilon = false;
64 return m_world->NearestObstacle( &aPath, opts );
65}
66
67
68void WALKAROUND::RestrictToCluster( bool aEnabled, const TOPOLOGY::CLUSTER& aCluster )
69{
71 m_restrictedSet.clear();
72
73 if( aEnabled )
74 {
75 for( ITEM* item : aCluster.m_items )
76 {
77 m_restrictedSet.insert( item );
78
79 if ( item->HasHole() )
80 m_restrictedSet.insert( item->Hole() );
81 }
82 }
83
84 for( ITEM* item : aCluster.m_items )
85 {
86 if( SOLID* solid = dyn_cast<SOLID*>( item ) )
87 m_restrictedVertices.push_back( solid->Anchor( 0 ) );
88 }
89}
90
91static wxString policy2string ( WALKAROUND::WALK_POLICY policy )
92{
93 switch(policy)
94 {
95 case WALKAROUND::WP_CCW: return wxT("ccw");
96 case WALKAROUND::WP_CW: return wxT("cw");
97 case WALKAROUND::WP_SHORTEST: return wxT("shortest");
98 }
99 return wxT("?");
100}
101
103{
104 TOPOLOGY topo( m_world );
105 TOPOLOGY::CLUSTER pendingClusters[MaxWalkPolicies];
106
107 for( int i = 0; i < MaxWalkPolicies; i++ )
108 {
109 if( !m_enabledPolicies[i] )
110 continue;
111
112 auto& line = m_currentResult.lines[ i ];
113 auto& status = m_currentResult.status[ i ];
114
115 PNS_DBG( Dbg(), AddItem, &line, WHITE, 10000, wxString::Format( "current (policy %d, stat %d)", i, status ) );
116
117 if( status != ST_IN_PROGRESS )
118 continue;
119
120 auto obstacle = nearestObstacle( line );
121
122 if( !obstacle )
123 {
124
126 PNS_DBG( Dbg(), Message, wxString::Format( "no-more-colls pol %d st %d", i, status ) );
127
128 continue;
129 }
130
131
132 pendingClusters[ i ] = topo.AssembleCluster( obstacle->m_item, line.Layer(), 0.0, line.Net() );
133 PNS_DBG( Dbg(), AddItem, obstacle->m_item, BLUE, 10000, wxString::Format( "col-item owner-depth %d cl-items=%d", static_cast<const NODE*>( obstacle->m_item->Owner() )->Depth(), (int) pendingClusters[i].m_items.size() ) );
134
135 }
136
138
139 auto processCluster = [ & ] ( TOPOLOGY::CLUSTER& aCluster, LINE& aLine, bool aCw ) -> bool
140 {
141 PNS_DBG( Dbg(), BeginGroup, wxString::Format( "cluster-details [cw %d]", aCw?1:0 ), 1 );
142
143 for( auto& clItem : aCluster.m_items )
144 {
145 int clearance = m_world->GetClearance( clItem, &aLine, false );
146 SHAPE_LINE_CHAIN hull = clItem->Hull( clearance + 1000, aLine.Width(), aLine.Layer() );
147
148 if( cornerMode == DIRECTION_45::MITERED_90 || cornerMode == DIRECTION_45::ROUNDED_90 )
149 {
150 BOX2I bbox = hull.BBox();
151 hull.Clear();
152 hull.Append( bbox.GetLeft(), bbox.GetTop() );
153 hull.Append( bbox.GetRight(), bbox.GetTop() );
154 hull.Append( bbox.GetRight(), bbox.GetBottom() );
155 hull.Append( bbox.GetLeft(), bbox.GetBottom() );
156 }
157
158 LINE tmp( aLine );
159
160 bool stat = aLine.Walkaround( hull, tmp.Line(), aCw );
161
162 PNS_DBG( Dbg(), AddShape, &hull, YELLOW, 10000, wxString::Format( "hull stat %d", stat?1:0 ) );
163 PNS_DBG( Dbg(), AddItem, &tmp, RED, 10000, wxString::Format( "walk stat %d", stat?1:0 ) );
164 PNS_DBG( Dbg(), AddItem, clItem, WHITE, 10000, wxString::Format( "item stat %d", stat?1:0 ) );
165
166 if( !stat )
167 {
168 PNS_DBGN( Dbg(), EndGroup );
169 return false;
170 }
171
172 aLine.SetShape( tmp.CLine() );
173 }
174
175 PNS_DBGN( Dbg(), EndGroup );
176
177 return true;
178 };
179
181 {
182 bool stat = processCluster( pendingClusters[ WP_CW ], m_currentResult.lines[ WP_CW ], true );
183 if( !stat )
185 }
186
188 {
189 bool stat = processCluster( pendingClusters[ WP_CCW ], m_currentResult.lines[ WP_CCW ], false );
190 if( !stat )
192 }
193
195 {
197 LINE path_cw( line ), path_ccw( line );
198
199 auto st_cw = processCluster( pendingClusters[WP_SHORTEST], path_cw, true );
200 auto st_ccw = processCluster( pendingClusters[WP_SHORTEST], path_ccw, false );
201
202 bool cw_coll = st_cw ? m_world->CheckColliding( &path_cw ).has_value() : false;
203 bool ccw_coll = st_ccw ? m_world->CheckColliding( &path_ccw ).has_value() : false;
204
205 double lengthFactorCw = (double) path_cw.CLine().Length() / (double) m_initialLength;
206 double lengthFactorCcw = (double) path_ccw.CLine().Length() / (double) m_initialLength;
207
208 PNS_DBG( Dbg(), AddItem, &path_cw, RED, 10000, wxString::Format( "shortest-cw stat %d lf %.1f", st_cw?1:0, lengthFactorCw ) );
209 PNS_DBG( Dbg(), AddItem, &path_ccw, BLUE, 10000, wxString::Format( "shortest-ccw stat %d lf %.1f", st_ccw?1:0, lengthFactorCcw ) );
210
211
212 std::optional<LINE> shortest;
213 std::optional<LINE> shortest_alt;
214
215
216 if( st_cw && st_ccw )
217 {
218 if( !cw_coll && !ccw_coll || ( cw_coll && ccw_coll) )
219 {
220 if( path_cw.CLine().Length() > path_ccw.CLine().Length() )
221 {
222 shortest = path_ccw;
223 shortest_alt = path_cw;
224 }
225 else
226 {
227 shortest = path_cw;
228 shortest_alt = path_ccw;
229 }
230 }
231 else if( !cw_coll )
232 shortest = path_cw;
233 else if( !ccw_coll )
234 shortest = path_ccw;
235
236 }
237 else if( st_ccw )
238 shortest = path_ccw;
239 else if( st_cw )
240 shortest = path_cw;
241
242 bool anyColliding = false;
243
244 if( m_lastShortestCluster && shortest.has_value() )
245 {
246 for( auto& item : m_lastShortestCluster->m_items )
247 {
248 if( shortest->Collide( item, m_world, shortest->Layer() ) )
249 {
250 anyColliding = true;
251 break;
252 }
253 }
254
255 PNS_DBG( Dbg(), Message, wxString::Format("check-back cc %d items %d coll %d", (int) pendingClusters[ WP_SHORTEST ].m_items.size(), (int) m_lastShortestCluster->m_items.size(), anyColliding ? 1: 0 ) );
256 }
257
258 if ( anyColliding )
259 {
260 shortest = shortest_alt;
261 }
262
263 if( !shortest )
264 {
266 }
267 else
268 {
269 m_currentResult.lines[WP_SHORTEST] = *shortest;
270 }
271
272 m_lastShortestCluster = pendingClusters[ WP_SHORTEST ];
273 }
274
275 return ST_IN_PROGRESS;
276}
277
278
279const WALKAROUND::RESULT WALKAROUND::Route( const LINE& aInitialPath )
280{
281 RESULT result;
282
283 m_initialLength = aInitialPath.CLine().Length();
284
285 // special case for via-in-the-middle-of-track placement
286
287#if 0
288 if( aInitialPath.PointCount() <= 1 )
289 {
290 if( aInitialPath.EndsWithVia() && m_world->CheckColliding( &aInitialPath.Via(),
291 m_itemMask ) )
292 {
293 // fixme restult
294 }
295 //return RESULT( STUCK, STUCK );
296
297 return RESULT(); //( DONE, DONE, aInitialPath, aInitialPath );
298 }
299#endif
300
301 start( aInitialPath );
302
303 PNS_DBG( Dbg(), AddItem, &aInitialPath, WHITE, 10000, wxT( "initial-path" ) );
304
306 {
307 singleStep();
308
309 bool stillInProgress = false;
310
311 for( int pol = 0; pol < MaxWalkPolicies; pol++ )
312 {
313 if (!m_enabledPolicies[pol])
314 continue;
315
316 auto& st = m_currentResult.status[pol];
317 auto& ln = m_currentResult.lines[pol];
318 double lengthFactor = (double) ln.CLine().Length() / (double) aInitialPath.CLine().Length();
319 // In some situations, there isn't a trivial path (or even a path at all). Hitting the
320 // iteration limit causes lag, so we can exit out early if the walkaround path gets very long
321 // compared with the initial path. If the length exceeds the initial length times this factor,
322 // fail out.
323 if( m_lengthLimitOn )
324 {
325 if( st != ST_DONE && lengthFactor > m_lengthExpansionFactor )
326 st = ST_ALMOST_DONE;
327 }
328
329 PNS_DBG( Dbg(), Message, wxString::Format( "check-wp iter %d st %d i %d lf %.1f", m_iteration, st, pol, lengthFactor ) );
330
331 if ( st == ST_IN_PROGRESS )
332 stillInProgress = true;
333 }
334
335
336 if( !stillInProgress )
337 break;
338
339 m_iteration++;
340 }
341
342
343 for( int pol = 0; pol < MaxWalkPolicies; pol++ )
344 {
345 auto& st = m_currentResult.status[pol];
346 const auto& ln = m_currentResult.lines[pol].CLine();
347
349 if( st == ST_IN_PROGRESS )
350 st = ST_ALMOST_DONE;
351
352 if( ln.SegmentCount() < 1 || ln.CPoint( 0 ) != aInitialPath.CPoint( 0 ) )
353 {
354 st = ST_STUCK;
355 }
356
357 if( ln.PointCount() > 0 && ln.CPoint( -1 ) != aInitialPath.CPoint( -1 ) )
358 {
359 st = ST_ALMOST_DONE;
360
361 }
362 PNS_DBG( Dbg(), Message, wxString::Format( "stat=%d", st ) );
363
364 }
365
366
367 return m_currentResult;
368}
369
370void WALKAROUND::SetAllowedPolicies( std::vector<WALK_POLICY> aPolicies)
371{
372 for( int i = 0; i < MaxWalkPolicies; i++ )
373 m_enabledPolicies[i] = false;
374
375 for ( auto p : aPolicies )
376 m_enabledPolicies[p] = true;
377}
378
379}
380
constexpr coord_type GetLeft() const
Definition: box2.h:228
constexpr coord_type GetRight() const
Definition: box2.h:217
constexpr coord_type GetTop() const
Definition: box2.h:229
constexpr coord_type GetBottom() const
Definition: box2.h:222
CORNER_MODE
Corner modes.
Definition: direction45.h:67
@ ROUNDED_90
H/V with filleted corners.
Definition: direction45.h:71
@ MITERED_90
H/V only (90-degree corners)
Definition: direction45.h:70
ROUTING_SETTINGS & Settings() const
Return the logger object, allowing to dump geometry to a file.
DEBUG_DECORATOR * Dbg() const
Definition: pns_algo_base.h:78
Base class for PNS router board items.
Definition: pns_item.h:97
virtual HOLE * Hole() const
Definition: pns_item.h:291
virtual bool HasHole() const
Definition: pns_item.h:290
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition: pns_line.h:62
const VECTOR2I & CPoint(int aIdx) const
Definition: pns_line.h:146
const SHAPE_LINE_CHAIN & CLine() const
Definition: pns_line.h:138
SHAPE_LINE_CHAIN & Line()
Definition: pns_line.h:137
VIA & Via()
Definition: pns_line.h:198
int PointCount() const
Definition: pns_line.h:141
bool EndsWithVia() const
Definition: pns_line.h:190
Keep the router "world" - i.e.
Definition: pns_node.h:231
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:241
int Depth() const
Definition: pns_node.h:281
OPT_OBSTACLE NearestObstacle(const LINE *aLine, const COLLISION_SEARCH_OPTIONS &aOpts=COLLISION_SEARCH_OPTIONS())
Follow the line in search of an obstacle that is nearest to the starting to the line's starting point...
Definition: pns_node.cpp:286
DIRECTION_45::CORNER_MODE GetCornerMode() const
const CLUSTER AssembleCluster(ITEM *aStart, int aLayer, double aAreaExpansionLimit=0.0, NET_HANDLE aExcludedNet=nullptr)
void RestrictToCluster(bool aEnabled, const TOPOLOGY::CLUSTER &aCluster)
STATUS Route(const LINE &aInitialPath, LINE &aWalkPath, bool aOptimize=true)
bool m_enabledPolicies[MaxWalkPolicies]
NODE::OPT_OBSTACLE nearestObstacle(const LINE &aPath)
std::vector< VECTOR2I > m_restrictedVertices
void SetAllowedPolicies(std::vector< WALK_POLICY > aPolicies)
void start(const LINE &aInitialPath)
static constexpr int MaxWalkPolicies
std::optional< TOPOLOGY::CLUSTER > m_lastShortestCluster
double m_lengthExpansionFactor
std::set< const ITEM * > m_restrictedSet
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Clear()
Remove all points from the line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
long long int Length() const
Return length of the line chain in Euclidean metric.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
@ WHITE
Definition: color4d.h:48
@ BLUE
Definition: color4d.h:56
@ YELLOW
Definition: color4d.h:67
@ RED
Definition: color4d.h:59
Push and Shove diff pair dimensions (gap) settings dialog.
static wxString policy2string(WALKAROUND::WALK_POLICY policy)
#define PNS_DBG(dbg, method,...)
#define PNS_DBGN(dbg, method)
std::function< bool(const ITEM *)> m_filter
Definition: pns_node.h:119
std::set< ITEM * > m_items
Definition: pns_topology.h:46
LINE lines[MaxWalkPolicies]
STATUS status[MaxWalkPolicies]