KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_tool_base.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013 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
23#include <functional>
24using namespace std::placeholders;
25
28#include <pcb_painter.h>
29#include <pcbnew_settings.h>
30#include <view/view_controls.h>
31
33#include <wx/log.h>
34
35#include "pns_kicad_iface.h"
36#include "pns_tool_base.h"
37#include "pns_arc.h"
38#include "pns_solid.h"
39#include "pns_dragger.h"
40
41const unsigned int PNS::TOOL_BASE::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
42
43using namespace KIGFX;
44
45namespace PNS {
46
47
48TOOL_BASE::TOOL_BASE( const std::string& aToolName ) :
49 PCB_TOOL_BASE( aToolName )
50{
51 m_gridHelper = nullptr;
52 m_iface = nullptr;
53 m_router = nullptr;
54 m_cancelled = false;
55
56 m_startItem = nullptr;
57
58 m_endItem = nullptr;
59 m_gridHelper = nullptr;
60
61 m_cancelled = false;
62}
63
64
66{
67 delete m_gridHelper;
68 delete m_router;
69 delete m_iface; // Delete after m_router because PNS::NODE dtor needs m_ruleResolver
70}
71
72
74{
75 delete m_gridHelper;
76 delete m_router;
77 delete m_iface; // Delete after m_router because PNS::NODE dtor needs m_ruleResolver
78
79 if( aReason == RESET_REASON::SHUTDOWN )
80 {
81 m_gridHelper = nullptr;
82 m_router = nullptr;
83 m_iface = nullptr;
84 return;
85 }
86
90 m_iface->SetHostTool( this );
91
92 m_router = new ROUTER;
96
98
99 PCBNEW_SETTINGS* settings = frame()->GetPcbNewSettings();
100
101 if( !settings->m_PnsSettings )
102 settings->m_PnsSettings = std::make_unique<ROUTING_SETTINGS>( settings, "tools.pns" );
103
104 m_router->LoadSettings( settings->m_PnsSettings.get() );
105
106 m_gridHelper = new PCB_GRID_HELPER( m_toolMgr, frame()->GetMagneticItemsSettings() );
107}
108
109
110ITEM* TOOL_BASE::pickSingleItem( const VECTOR2I& aWhere, NET_HANDLE aNet, int aLayer,
111 bool aIgnorePads, const std::vector<ITEM*> aAvoidItems )
112{
113 int tl = aLayer > 0 ? aLayer
115 static_cast<PCB_LAYER_ID>( getView()->GetTopLayer() ) );
116 int maxSlopRadius = std::max( m_gridHelper->GetGrid().x, m_gridHelper->GetGrid().y );
117
118 static const int candidateCount = 5;
119 ITEM* prioritized[candidateCount];
120 SEG::ecoord dist[candidateCount];
121
122 for( int i = 0; i < candidateCount; i++ )
123 {
124 prioritized[i] = nullptr;
125 dist[i] = VECTOR2I::ECOORD_MAX;
126 }
127
128 auto haveCandidates =
129 [&]()
130 {
131 for( ITEM* item : prioritized )
132 {
133 if( item )
134 return true;
135 }
136
137 return false;
138 };
139
140 for( int slopRadius : { 0, maxSlopRadius } )
141 {
142 ITEM_SET candidates = m_router->QueryHoverItems( aWhere, slopRadius );
143
144 for( ITEM* item : candidates.Items() )
145 {
146 if( !item->IsRoutable() )
147 continue;
148
149 if( !m_iface->IsPNSCopperLayer( item->Layers().Start() ) )
150 continue;
151
152 if( !m_iface->IsAnyLayerVisible( item->Layers() ) )
153 continue;
154
155 if( alg::contains( aAvoidItems, item ) )
156 continue;
157
158 // fixme: this causes flicker with live loop removal...
159 //if( item->Parent() && !item->Parent()->ViewIsVisible() )
160 // continue;
161
162 if( item->OfKind( ITEM::SOLID_T ) && aIgnorePads )
163 {
164 continue;
165 }
166 else if( m_router->GetInterface()->GetNetCode( aNet) <= 0 || item->Net() == aNet )
167 {
168 if( item->OfKind( ITEM::VIA_T | ITEM::SOLID_T ) )
169 {
170 SEG::ecoord d = ( item->Shape( aLayer )->Centre() - aWhere ).SquaredEuclideanNorm();
171
172 if( d < dist[2] )
173 {
174 prioritized[2] = item;
175 dist[2] = d;
176 }
177
178 if( item->Layers().Overlaps( tl ) && d < dist[0] )
179 {
180 prioritized[0] = item;
181 dist[0] = d;
182 }
183 }
184 else // ITEM::SEGMENT_T | ITEM::ARC_T
185 {
186 LINKED_ITEM* li = static_cast<LINKED_ITEM*>( item );
187 SEG::ecoord d = std::min( ( li->Anchor( 0 ) - aWhere ).SquaredEuclideanNorm(),
188 ( li->Anchor( 1 ) - aWhere ).SquaredEuclideanNorm() );
189
190 if( d < dist[3] )
191 {
192 prioritized[3] = item;
193 dist[3] = d;
194 }
195
196 if( item->Layers().Overlaps( tl ) && d < dist[1] )
197 {
198 prioritized[1] = item;
199 dist[1] = d;
200 }
201 }
202 }
203 else if( item->OfKind( ITEM::SOLID_T ) && item->IsFreePad() )
204 {
205 // Allow free pads only when already inside pad
206 if( item->Shape( -1 )->Collide( aWhere ) )
207 {
208 prioritized[0] = item;
209 dist[0] = 0;
210 }
211 }
212 else if ( item->Net() == 0 && m_router->Settings().Mode() == RM_MarkObstacles )
213 {
214 // Allow unconnected items as last resort in RM_MarkObstacles mode
215 if( item->Layers().Overlaps( tl ) )
216 prioritized[4] = item;
217 }
218 }
219
220 if( haveCandidates() )
221 break;
222 }
223
224 ITEM* rv = nullptr;
225
226 bool highContrast = ( frame()->GetDisplayOptions().m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL );
227
228 for( ITEM* item : prioritized )
229 {
230 if( highContrast && item && !item->Layers().Overlaps( tl ) )
231 item = nullptr;
232
233 if( item && ( aLayer < 0 || item->Layers().Overlaps( aLayer ) ) )
234 {
235 rv = item;
236 break;
237 }
238 }
239
240 if( rv )
241 {
242 wxLogTrace( wxT( "PNS" ), wxT( "%s, layer : %d, tl: %d" ),
243 rv->KindStr().c_str(),
244 rv->Layers().Start(),
245 tl );
246 }
247
248 return rv;
249}
250
251
252void TOOL_BASE::highlightNets( bool aEnabled, std::set<NET_HANDLE> aNets )
253{
255 std::set<int> netcodes;
256
257 for( const NET_HANDLE& net : aNets )
258 netcodes.insert( m_router->GetInterface()->GetNetCode( net ) );
259
260 if( netcodes.size() > 0 && aEnabled )
261 {
262 // If the user has previously set some of the routed nets to be highlighted,
263 // we assume they want to keep them highlighted after routing
264
265 const std::set<int>& currentNetCodes = rs->GetHighlightNetCodes();
266 bool keep = false;
267
268 for( const int& netcode : netcodes )
269 {
270 if( currentNetCodes.find( netcode ) != currentNetCodes.end() )
271 {
272 keep = true;
273 break;
274 }
275 }
276
277 if( rs->IsHighlightEnabled() && keep )
278 m_startHighlightNetcodes = currentNetCodes;
279 else
281
282 rs->SetHighlight( netcodes, true );
283 }
284 else
285 {
287 }
288
289 // Do not remove this call. This is required to update the layers when we highlight a net.
290 // In this case, highlighting a net dims all other elements, so the colors need to update
292}
293
294
296{
297 // Sync PNS engine settings with the general PCB editor options.
299
300 // If we're dragging a track segment, don't try to snap to items that are part of the original line.
302 && m_router->GetDragger() )
303 {
304 DRAGGER* dragger = dynamic_cast<DRAGGER*>( m_router->GetDragger() );
305 LINKED_ITEM* linkedItem = dynamic_cast<LINKED_ITEM*>( aItem );
306
307 if( dragger && linkedItem && dragger->GetOriginalLine().ContainsLink( linkedItem ) )
308 return false;
309 }
310
311 MAGNETIC_SETTINGS* magSettings = frame()->GetMagneticItemsSettings();
312
313 pnss.SetSnapToPads( magSettings->pads == MAGNETIC_OPTIONS::CAPTURE_CURSOR_IN_TRACK_TOOL
314 || magSettings->pads == MAGNETIC_OPTIONS::CAPTURE_ALWAYS );
315
316 pnss.SetSnapToTracks( magSettings->tracks == MAGNETIC_OPTIONS::CAPTURE_CURSOR_IN_TRACK_TOOL
317 || magSettings->tracks == MAGNETIC_OPTIONS::CAPTURE_ALWAYS );
318
319 if( aItem )
320 {
322 return pnss.GetSnapToTracks();
323 else if( aItem->OfKind( ITEM::SOLID_T ) )
324 return pnss.GetSnapToPads();
325 }
326
327 return false;
328}
329
330
331void TOOL_BASE::updateStartItem( const TOOL_EVENT& aEvent, bool aIgnorePads )
332{
334 static_cast<PCB_LAYER_ID>( getView()->GetTopLayer() ) );
335 GAL* gal = m_toolMgr->GetView()->GetGAL();
336 VECTOR2I pos = aEvent.HasPosition() ? (VECTOR2I) aEvent.Position() : m_startSnapPoint;
337
338 pos = GetClampedCoords( pos, COORDS_PADDING );
339
340 if( aEvent.Modifier( MD_CTRL ) && aEvent.Modifier( MD_SHIFT ) )
341 {
342 m_startItem = nullptr;
345 return;
346 }
347
348 controls()->ForceCursorPosition( false );
350 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
351
352 m_startItem = pickSingleItem( pos, nullptr, -1, aIgnorePads );
353
355 m_startItem = nullptr;
356
359}
360
361
363{
364 int layer;
365 GAL* gal = m_toolMgr->GetView()->GetGAL();
366
368 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
369
370 controls()->ForceCursorPosition( false );
371
372 VECTOR2I mousePos = GetClampedCoords( controls()->GetMousePosition(), COORDS_PADDING );
373
374 if( m_router->GetState() == ROUTER::ROUTE_TRACK && aEvent.IsDrag() )
375 {
376 // If the user is moving the mouse quickly while routing then clicks will come in as
377 // short drags. In this case we want to use the drag origin rather than the current
378 // mouse position.
379 mousePos = aEvent.DragOrigin();
380 }
381
383 ( m_router->GetCurrentNets().empty() || m_router->GetCurrentNets().front() == nullptr ) )
384 {
385 m_endSnapPoint = snapToItem( nullptr, mousePos );
387 m_endItem = nullptr;
388
389 return;
390 }
391
392 if( m_router->IsPlacingVia() )
393 layer = -1;
394 else
395 layer = m_router->GetCurrentLayer();
396
397 ITEM* endItem = nullptr;
398
399 std::vector<NET_HANDLE> nets = m_router->GetCurrentNets();
400
401 for( NET_HANDLE net : nets )
402 {
403 endItem = pickSingleItem( mousePos, net, layer, false, { m_startItem } );
404
405 if( endItem )
406 break;
407 }
408
409 if( m_gridHelper->GetSnap() && checkSnap( endItem ) )
410 {
411 m_endItem = endItem;
412 m_endSnapPoint = snapToItem( endItem, mousePos );
413 }
414 else
415 {
416 m_endItem = nullptr;
418 : GRID_WIRES );
419 }
420
422
423 if( m_endItem )
424 {
425 wxLogTrace( wxT( "PNS" ), wxT( "%s, layer : %d" ),
426 m_endItem->KindStr().c_str(),
427 m_endItem->Layers().Start() );
428 }
429}
430
431
433{
434 return m_router;
435}
436
437
439{
440 return m_iface;
441}
442
443
444const VECTOR2I TOOL_BASE::snapToItem( ITEM* aItem, const VECTOR2I& aP )
445{
446 if( !aItem || !m_iface->IsItemVisible( aItem ) )
447 {
449 }
450
451 switch( aItem->Kind() )
452 {
453 case ITEM::SOLID_T:
454 {
455 SOLID* solid = static_cast<SOLID*>( aItem );
456
457 if( solid->AnchorPoints().empty() )
458 return solid->Anchor( 0 );
459
461 SEG::ecoord minDist = std::numeric_limits<SEG::ecoord>::max();
462
463 for( VECTOR2I anchorCandidate : solid->AnchorPoints() )
464 {
465 SEG::ecoord distSq = ( aP - anchorCandidate ).SquaredEuclideanNorm();
466
467 if( distSq < minDist )
468 {
469 minDist = distSq;
470 anchor = anchorCandidate;
471 }
472 }
473
474 return anchor;
475 }
476
477 case ITEM::VIA_T:
478 return static_cast<VIA*>( aItem )->Pos();
479
480 case ITEM::SEGMENT_T:
481 case ITEM::ARC_T:
482 {
483 LINKED_ITEM* li = static_cast<LINKED_ITEM*>( aItem );
484 VECTOR2I A = li->Anchor( 0 );
485 VECTOR2I B = li->Anchor( 1 );
486 SEG::ecoord w_sq = SEG::Square( li->Width() / 2 );
487 SEG::ecoord distA_sq = ( aP - A ).SquaredEuclideanNorm();
488 SEG::ecoord distB_sq = ( aP - B ).SquaredEuclideanNorm();
489
490 if( distA_sq < w_sq || distB_sq < w_sq )
491 {
492 return ( distA_sq < distB_sq ) ? A : B;
493 }
494 else if( aItem->Kind() == ITEM::SEGMENT_T )
495 {
496 // TODO(snh): Clean this up
497 SEGMENT* seg = static_cast<SEGMENT*>( li );
498 return m_gridHelper->AlignToSegment( aP, seg->Seg() );
499 }
500 else if( aItem->Kind() == ITEM::ARC_T )
501 {
502 ARC* arc = static_cast<ARC*>( li );
503 return m_gridHelper->AlignToArc( aP, *static_cast<const SHAPE_ARC*>( arc->Shape( -1 ) ) );
504 }
505
506 break;
507 }
508
509 default:
510 break;
511 }
512
514}
515
516}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
bool GetSnap() const
Definition: grid_helper.h:112
void SetSnap(bool aSnap)
Definition: grid_helper.h:111
bool GetUseGrid() const
Definition: grid_helper.h:115
void SetUseGrid(bool aSnapToGrid)
Definition: grid_helper.h:114
VECTOR2I GetGrid() const
Abstract interface for drawing on a 2D-surface.
bool GetGridSnapping() const
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const std::set< int > & GetHighlightNetCodes() const
Return the netcode of currently highlighted net.
bool IsHighlightEnabled() const
Return current highlight setting.
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition: view.cpp:804
GAL * GetGAL() const
Return the #GAL this view is using to draw graphical primitives.
Definition: view.h:203
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:221
std::unique_ptr< PNS::ROUTING_SETTINGS > m_PnsSettings
VECTOR2I AlignToArc(const VECTOR2I &aPoint, const SHAPE_ARC &aSeg)
VECTOR2I AlignToSegment(const VECTOR2I &aPoint, const SEG &aSeg)
virtual VECTOR2I Align(const VECTOR2I &aPoint, GRID_HELPER_GRIDS aGrid) const
Definition: grid_helper.h:65
T * frame() const
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
const SHAPE * Shape(int aLayer) const override
Return the geometrical shape of the item.
Definition: pns_arc.h:78
DRAGGER.
Definition: pns_dragger.h:48
const LINE & GetOriginalLine()
Definition: pns_dragger.h:103
std::vector< ITEM * > & Items()
Definition: pns_itemset.h:87
Base class for PNS router board items.
Definition: pns_item.h:97
const PNS_LAYER_RANGE & Layers() const
Definition: pns_item.h:199
PnsKind Kind() const
Return the type (kind) of the item.
Definition: pns_item.h:170
@ SEGMENT_T
Definition: pns_item.h:106
bool OfKind(int aKindMask) const
Definition: pns_item.h:178
virtual VECTOR2I Anchor(int n) const
Definition: pns_item.h:255
std::string KindStr() const
Definition: pns_item.cpp:306
virtual int Width() const
virtual int GetPNSLayerFromBoardLayer(PCB_LAYER_ID aLayer) const =0
virtual int GetNetCode(NET_HANDLE aNet) const =0
void ClearWorld()
Definition: pns_router.cpp:105
ROUTER_IFACE * GetInterface() const
Definition: pns_router.h:223
void UpdateSizes(const SIZES_SETTINGS &aSizes)
Applies stored settings.
Definition: pns_router.cpp:742
void LoadSettings(ROUTING_SETTINGS *aSettings)
Changes routing settings to ones passed in the parameter.
Definition: pns_router.h:211
const ITEM_SET QueryHoverItems(const VECTOR2I &aP, int aSlopRadius=0)
Definition: pns_router.cpp:124
void SetInterface(ROUTER_IFACE *aIface)
void SyncWorld()
Definition: pns_router.cpp:95
bool IsPlacingVia() const
ROUTING_SETTINGS & Settings()
Definition: pns_router.h:197
DRAG_ALGO * GetDragger()
Definition: pns_router.h:145
RouterState GetState() const
Definition: pns_router.h:143
int GetCurrentLayer() const
const std::vector< NET_HANDLE > GetCurrentNets() const
Definition: pns_router.cpp:995
Contain all persistent settings of the router, such as the mode, optimization effort,...
void SetSnapToTracks(bool aSnap)
void SetSnapToPads(bool aSnap)
PNS_MODE Mode() const
Set the routing mode.
const SEG & Seg() const
Definition: pns_segment.h:90
const std::vector< VECTOR2I > & AnchorPoints() const
Definition: pns_solid.h:114
virtual VECTOR2I Anchor(int aN) const override
Definition: pns_solid.cpp:95
bool checkSnap(ITEM *aItem)
virtual void updateStartItem(const TOOL_EVENT &aEvent, bool aIgnorePads=false)
ROUTER * Router() const
PNS_KICAD_IFACE * GetInterface() const
std::set< int > m_startHighlightNetcodes
Definition: pns_tool_base.h:73
const VECTOR2I snapToItem(ITEM *aSnapToItem, const VECTOR2I &aP)
virtual void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
virtual void highlightNets(bool aEnabled, std::set< NET_HANDLE > aNetcodes={})
virtual ~TOOL_BASE()
SIZES_SETTINGS m_savedSizes
Definition: pns_tool_base.h:70
PNS_KICAD_IFACE * m_iface
Definition: pns_tool_base.h:79
virtual ITEM * pickSingleItem(const VECTOR2I &aWhere, NET_HANDLE aNet=nullptr, int aLayer=-1, bool aIgnorePads=false, const std::vector< ITEM * > aAvoidItems={})
ITEM * m_startItem
Definition: pns_tool_base.h:71
virtual void updateEndItem(const TOOL_EVENT &aEvent)
TOOL_BASE(const std::string &aToolName)
static const unsigned int COORDS_PADDING
Definition: pns_tool_base.h:84
ROUTER * m_router
Definition: pns_tool_base.h:80
VECTOR2I m_endSnapPoint
Definition: pns_tool_base.h:76
PCB_GRID_HELPER * m_gridHelper
Definition: pns_tool_base.h:78
VECTOR2I m_startSnapPoint
Definition: pns_tool_base.h:72
void SetBoard(BOARD *aBoard)
bool IsPNSCopperLayer(int aPNSLayer) const override
void SetView(KIGFX::VIEW *aView)
virtual void SetHostTool(PCB_TOOL_BASE *aTool)
bool IsItemVisible(const PNS::ITEM *aItem) const override
bool IsAnyLayerVisible(const PNS_LAYER_RANGE &aLayer) const override
int Start() const
Definition: pns_layerset.h:86
bool Overlaps(const PNS_LAYER_RANGE &aOther) const
Definition: pns_layerset.h:67
VECTOR2I::extended_type ecoord
Definition: seg.h:44
static SEG::ecoord Square(int a)
Definition: seg.h:123
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:218
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
@ SHUTDOWN
Tool is being shut down.
Definition: tool_base.h:84
Generic, UI-independent tool event.
Definition: tool_event.h:167
bool HasPosition() const
Definition: tool_event.h:256
bool DisableGridSnapping() const
Definition: tool_event.h:363
const VECTOR2D Position() const
Returns the point where dragging has started.
Definition: tool_event.h:285
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:307
int Modifier(int aMask=MD_MODIFIER_MASK) const
Definition: tool_event.h:358
const VECTOR2D DragOrigin() const
Returns information about mouse buttons state.
Definition: tool_event.h:291
KIGFX::VIEW * GetView() const
Definition: tool_manager.h:391
static constexpr extended_type ECOORD_MAX
Definition: vector2d.h:76
a few functions useful in geometry calculations.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamps a vector to values that can be negated, respecting numeric limits of coordinates data type wit...
@ GRID_VIAS
Definition: grid_helper.h:48
@ GRID_WIRES
Definition: grid_helper.h:47
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:247
Push and Shove diff pair dimensions (gap) settings dialog.
@ RM_MarkObstacles
Ignore collisions, mark obstacles.
void * NET_HANDLE
Definition: pns_item.h:54
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:100
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
MAGNETIC_OPTIONS tracks
MAGNETIC_OPTIONS pads
@ MD_CTRL
Definition: tool_event.h:143
@ MD_SHIFT
Definition: tool_event.h:142
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691