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 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
19 * along with this program. If not, see <https://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
32#include <tool/tool_manager.h>
34#include <wx/log.h>
35
36#include "pns_kicad_iface.h"
37#include "pns_tool_base.h"
38#include "pns_arc.h"
39#include "pns_solid.h"
40#include "pns_dragger.h"
41
42const unsigned int PNS::TOOL_BASE::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
43
44using namespace KIGFX;
45
46namespace PNS {
47
48
49TOOL_BASE::TOOL_BASE( const std::string& aToolName ) :
50 PCB_TOOL_BASE( aToolName )
51{
52 m_gridHelper = nullptr;
53 m_iface = nullptr;
54 m_router = nullptr;
55 m_cancelled = false;
56
57 m_startItem = nullptr;
58
59 m_endItem = nullptr;
60 m_gridHelper = nullptr;
61
62 m_cancelled = false;
63}
64
65
67{
68 delete m_gridHelper;
69 delete m_router;
70 delete m_iface; // Delete after m_router because PNS::NODE dtor needs m_ruleResolver
71}
72
73
75{
76 delete m_gridHelper;
77 delete m_router;
78 delete m_iface; // Delete after m_router because PNS::NODE dtor needs m_ruleResolver
79
80 if( aReason == RESET_REASON::SHUTDOWN )
81 {
82 m_gridHelper = nullptr;
83 m_router = nullptr;
84 m_iface = nullptr;
85 return;
86 }
87
89 m_iface->SetBoard( board() );
90 m_iface->SetView( getView() );
91 m_iface->SetHostTool( this );
92
93 m_router = new ROUTER;
94 m_router->SetInterface( m_iface );
95 m_router->ClearWorld();
96 m_router->SyncWorld();
97
98 m_router->UpdateSizes( m_savedSizes );
99
100 PCBNEW_SETTINGS* settings = frame()->GetPcbNewSettings();
101
102 if( !settings->m_PnsSettings )
103 settings->m_PnsSettings = std::make_unique<ROUTING_SETTINGS>( settings, "tools.pns" );
104
105 m_router->LoadSettings( settings->m_PnsSettings.get() );
106
107 m_gridHelper = new PCB_GRID_HELPER( m_toolMgr, frame()->GetMagneticItemsSettings() );
108}
109
110
111ITEM* TOOL_BASE::pickSingleItem( const VECTOR2I& aWhere, NET_HANDLE aNet, int aLayer,
112 bool aIgnorePads, const std::vector<ITEM*> aAvoidItems )
113{
114 int tl = aLayer > 0 ? aLayer
115 : m_router->GetInterface()->GetPNSLayerFromBoardLayer(
116 static_cast<PCB_LAYER_ID>( getView()->GetTopLayer() ) );
117 int maxSlopRadius = std::max( m_gridHelper->GetGrid().x, m_gridHelper->GetGrid().y );
118
119 static const int candidateCount = 5;
120 ITEM* prioritized[candidateCount];
121 SEG::ecoord dist[candidateCount];
122
123 for( int i = 0; i < candidateCount; i++ )
124 {
125 prioritized[i] = nullptr;
126 dist[i] = VECTOR2I::ECOORD_MAX;
127 }
128
129 auto haveCandidates =
130 [&]()
131 {
132 for( ITEM* item : prioritized )
133 {
134 if( item )
135 return true;
136 }
137
138 return false;
139 };
140
141 for( int slopRadius : { 0, maxSlopRadius } )
142 {
143 ITEM_SET candidates = m_router->QueryHoverItems( aWhere, slopRadius );
144
145 for( ITEM* item : candidates.Items() )
146 {
147 if( !item->IsRoutable() )
148 continue;
149
150 if( !m_iface->IsPNSCopperLayer( item->Layers().Start() ) )
151 continue;
152
153 if( !m_iface->IsAnyLayerVisible( item->Layers() ) )
154 continue;
155
156 if( alg::contains( aAvoidItems, item ) )
157 continue;
158
159 // fixme: this causes flicker with live loop removal...
160 //if( item->Parent() && !item->Parent()->ViewIsVisible() )
161 // continue;
162
163 if( item->OfKind( ITEM::SOLID_T ) && aIgnorePads )
164 {
165 continue;
166 }
167 else if( m_router->Mode() == PNS::PNS_MODE_ROUTE_DIFF_PAIR || m_router->GetInterface()->GetNetCode( aNet) <= 0 || item->Net() == aNet )
168 {
169 if( item->OfKind( ITEM::VIA_T | ITEM::SOLID_T ) )
170 {
171 SEG::ecoord d = ( item->Shape( aLayer )->Centre() - aWhere ).SquaredEuclideanNorm();
172
173 if( d < dist[2] )
174 {
175 prioritized[2] = item;
176 dist[2] = d;
177 }
178
179 if( item->Layers().Overlaps( tl ) && d < dist[0] )
180 {
181 prioritized[0] = item;
182 dist[0] = d;
183 }
184 }
185 else // ITEM::SEGMENT_T | ITEM::ARC_T
186 {
187 LINKED_ITEM* li = static_cast<LINKED_ITEM*>( item );
188 SEG::ecoord d = std::min( ( li->Anchor( 0 ) - aWhere ).SquaredEuclideanNorm(),
189 ( li->Anchor( 1 ) - aWhere ).SquaredEuclideanNorm() );
190
191 if( d < dist[3] )
192 {
193 prioritized[3] = item;
194 dist[3] = d;
195 }
196
197 if( item->Layers().Overlaps( tl ) && d < dist[1] )
198 {
199 prioritized[1] = item;
200 dist[1] = d;
201 }
202 }
203 }
204 else if( item->OfKind( ITEM::SOLID_T ) && item->IsFreePad() )
205 {
206 // Allow free pads only when already inside pad
207 if( item->Shape( -1 )->Collide( aWhere ) )
208 {
209 prioritized[0] = item;
210 dist[0] = 0;
211 }
212 }
213 else if ( item->Net() == 0 && m_router->Settings().Mode() == RM_MarkObstacles )
214 {
215 // Allow unconnected items as last resort in RM_MarkObstacles mode
216 if( item->Layers().Overlaps( tl ) )
217 prioritized[4] = item;
218 }
219 }
220
221 if( haveCandidates() )
222 break;
223 }
224
225 ITEM* rv = nullptr;
226
227 bool highContrast = ( frame()->GetDisplayOptions().m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL );
228
229 for( ITEM* item : prioritized )
230 {
231 if( highContrast && item && !item->Layers().Overlaps( tl ) )
232 item = nullptr;
233
234 if( item && ( aLayer < 0 || item->Layers().Overlaps( aLayer ) ) )
235 {
236 rv = item;
237 break;
238 }
239 }
240
241 if( rv )
242 {
243 wxLogTrace( wxT( "PNS" ), wxT( "%s, layer : %d, tl: %d" ),
244 rv->KindStr().c_str(),
245 rv->Layers().Start(),
246 tl );
247 }
248
249 return rv;
250}
251
252
253void TOOL_BASE::highlightNets( bool aEnabled, std::set<NET_HANDLE> aNets )
254{
256 std::set<int> netcodes;
257
258 for( const NET_HANDLE& net : aNets )
259 netcodes.insert( m_router->GetInterface()->GetNetCode( net ) );
260
261 if( netcodes.size() > 0 && aEnabled )
262 {
263 // If the user has previously set some of the routed nets to be highlighted,
264 // we assume they want to keep them highlighted after routing
265
266 const std::set<int>& currentNetCodes = rs->GetHighlightNetCodes();
267 bool keep = false;
268
269 for( const int& netcode : netcodes )
270 {
271 if( currentNetCodes.find( netcode ) != currentNetCodes.end() )
272 {
273 keep = true;
274 break;
275 }
276 }
277
278 if( rs->IsHighlightEnabled() && keep )
279 m_startHighlightNetcodes = currentNetCodes;
280 else
282
283 rs->SetHighlight( netcodes, true );
284 }
285 else
286 {
288 }
289
290 // Do not remove this call. This is required to update the layers when we highlight a net.
291 // In this case, highlighting a net dims all other elements, so the colors need to update
293}
294
295
297{
298 // Sync PNS engine settings with the general PCB editor options.
299 ROUTING_SETTINGS& pnss = m_router->Settings();
300
301 // If we're dragging a track segment, don't try to snap to items that are part of the original line.
302 if( m_startItem && aItem && m_router->GetState() == ROUTER::DRAG_SEGMENT
303 && m_router->GetDragger() )
304 {
305 DRAGGER* dragger = dynamic_cast<DRAGGER*>( m_router->GetDragger() );
306 LINKED_ITEM* linkedItem = dynamic_cast<LINKED_ITEM*>( aItem );
307
308 if( dragger && linkedItem && dragger->GetOriginalLine().ContainsLink( linkedItem ) )
309 return false;
310 }
311
312 MAGNETIC_SETTINGS* magSettings = frame()->GetMagneticItemsSettings();
313
315 || magSettings->pads == MAGNETIC_OPTIONS::CAPTURE_ALWAYS );
316
318 || magSettings->tracks == MAGNETIC_OPTIONS::CAPTURE_ALWAYS );
319
320 if( aItem )
321 {
323 return pnss.GetSnapToTracks();
324 else if( aItem->OfKind( ITEM::SOLID_T ) )
325 return pnss.GetSnapToPads();
326 }
327
328 return false;
329}
330
331
332void TOOL_BASE::updateStartItem( const TOOL_EVENT& aEvent, bool aIgnorePads )
333{
334 int tl = m_router->GetInterface()->GetPNSLayerFromBoardLayer( ToLAYER_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 // Snapping uses ViewGetLOD(), which use the layerVisibilityCache. Make sure the cache is up-to-date.
349 m_toolMgr->GetView()->SyncLayerVisibilityCache();
350
351 controls()->ForceCursorPosition( false );
352 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
353 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
354
355 m_startItem = pickSingleItem( pos, nullptr, -1, aIgnorePads );
356
357 if( !m_gridHelper->GetUseGrid() && m_startItem && !m_startItem->Layers().Overlaps( tl ) )
358 m_startItem = nullptr;
359
362}
363
364
366{
367 int layer;
368 GAL* gal = m_toolMgr->GetView()->GetGAL();
369
370 // Snapping uses ViewGetLOD(), which use the layerVisibilityCache. Make sure the cache is up-to-date.
371 m_toolMgr->GetView()->SyncLayerVisibilityCache();
372
373 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
374 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
375
376 controls()->ForceCursorPosition( false );
377
378 VECTOR2I mousePos = GetClampedCoords( controls()->GetMousePosition(), COORDS_PADDING );
379
380 if( m_router->GetState() == ROUTER::ROUTE_TRACK && aEvent.IsDrag() )
381 {
382 // If the user is moving the mouse quickly while routing then clicks will come in as
383 // short drags. In this case we want to use the drag origin rather than the current
384 // mouse position.
385 mousePos = aEvent.DragOrigin();
386 }
387
388 if( m_router->Settings().Mode() != RM_MarkObstacles &&
389 ( m_router->GetCurrentNets().empty() || m_router->GetCurrentNets().front() == nullptr ) )
390 {
391 m_endSnapPoint = snapToItem( nullptr, mousePos );
393 m_endItem = nullptr;
394
395 return;
396 }
397
398 if( m_router->IsPlacingVia() )
399 layer = -1;
400 else
401 layer = m_router->GetCurrentLayer();
402
403 ITEM* endItem = nullptr;
404
405 std::vector<NET_HANDLE> nets = m_router->GetCurrentNets();
406
407 for( NET_HANDLE net : nets )
408 {
409
410 endItem = pickSingleItem( mousePos, net, layer, false, {} ); //{ m_startItem } );
411
412 if( endItem )
413 break;
414 }
415
416 if( m_gridHelper->GetSnap() && checkSnap( endItem ) )
417 {
418 m_endItem = endItem;
419 m_endSnapPoint = snapToItem( endItem, mousePos );
420 }
421 else
422 {
423 m_endItem = nullptr;
424 m_endSnapPoint = m_gridHelper->Align( mousePos, m_router->IsPlacingVia() ? GRID_VIAS
425 : GRID_WIRES );
426 }
427
429
430 if( m_endItem )
431 {
432 wxLogTrace( wxT( "PNS" ), wxT( "%s, layer : %d" ),
433 m_endItem->KindStr().c_str(),
434 m_endItem->Layers().Start() );
435 }
436}
437
438
440{
441 return m_router;
442}
443
444
446{
447 return m_iface;
448}
449
450
451const VECTOR2I TOOL_BASE::snapToItem( ITEM* aItem, const VECTOR2I& aP )
452{
453 if( !aItem || !m_iface->IsItemVisible( aItem ) )
454 {
455 return m_gridHelper->Align( aP, m_router->IsPlacingVia() ? GRID_VIAS : GRID_WIRES );
456 }
457
458 switch( aItem->Kind() )
459 {
460 case ITEM::SOLID_T:
461 {
462 SOLID* solid = static_cast<SOLID*>( aItem );
463
464 if( solid->AnchorPoints().empty() )
465 return solid->Anchor( 0 );
466
468 SEG::ecoord minDist = std::numeric_limits<SEG::ecoord>::max();
469
470 for( VECTOR2I anchorCandidate : solid->AnchorPoints() )
471 {
472 SEG::ecoord distSq = ( aP - anchorCandidate ).SquaredEuclideanNorm();
473
474 if( distSq < minDist )
475 {
476 minDist = distSq;
477 anchor = anchorCandidate;
478 }
479 }
480
481 return anchor;
482 }
483
484 case ITEM::VIA_T:
485 return static_cast<VIA*>( aItem )->Pos();
486
487 case ITEM::SEGMENT_T:
488 case ITEM::ARC_T:
489 {
490 LINKED_ITEM* li = static_cast<LINKED_ITEM*>( aItem );
491 VECTOR2I A = li->Anchor( 0 );
492 VECTOR2I B = li->Anchor( 1 );
493 SEG::ecoord w_sq = SEG::Square( li->Width() / 2 );
494 SEG::ecoord distA_sq = ( aP - A ).SquaredEuclideanNorm();
495 SEG::ecoord distB_sq = ( aP - B ).SquaredEuclideanNorm();
496
497 if( distA_sq < w_sq || distB_sq < w_sq )
498 {
499 return ( distA_sq < distB_sq ) ? A : B;
500 }
501 else if( aItem->Kind() == ITEM::SEGMENT_T )
502 {
503 // TODO(snh): Clean this up
504 SEGMENT* seg = static_cast<SEGMENT*>( li );
505 return m_gridHelper->AlignToSegment( aP, seg->Seg() );
506 }
507 else if( aItem->Kind() == ITEM::ARC_T )
508 {
509 ARC* arc = static_cast<ARC*>( li );
510 return m_gridHelper->AlignToArc( aP, *static_cast<const SHAPE_ARC*>( arc->Shape( -1 ) ) );
511 }
512
513 break;
514 }
515
516 default:
517 break;
518 }
519
520 return m_gridHelper->Align( aP, m_router->IsPlacingVia() ? GRID_VIAS : GRID_WIRES );
521}
522
523}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
@ NORMAL
Inactive layers are shown normally (no high-contrast mode)
Abstract interface for drawing on a 2D-surface.
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:862
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition view.h:225
std::unique_ptr< PNS::ROUTING_SETTINGS > m_PnsSettings
T * frame() const
KIGFX::VIEW_CONTROLS * controls() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
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()
std::vector< ITEM * > & Items()
Definition pns_itemset.h:95
Base class for PNS router board items.
Definition pns_item.h:98
const PNS_LAYER_RANGE & Layers() const
Definition pns_item.h:212
PnsKind Kind() const
Return the type (kind) of the item.
Definition pns_item.h:173
bool OfKind(int aKindMask) const
Definition pns_item.h:181
virtual VECTOR2I Anchor(int n) const
Definition pns_item.h:268
std::string KindStr() const
Definition pns_item.cpp:315
virtual int Width() const
Contain all persistent settings of the router, such as the mode, optimization effort,...
void SetSnapToTracks(bool aSnap)
const SEG & Seg() const
const std::vector< VECTOR2I > & AnchorPoints() const
Definition pns_solid.h:132
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
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
PNS_KICAD_IFACE * m_iface
virtual ITEM * pickSingleItem(const VECTOR2I &aWhere, NET_HANDLE aNet=nullptr, int aLayer=-1, bool aIgnorePads=false, const std::vector< ITEM * > aAvoidItems={})
virtual void updateEndItem(const TOOL_EVENT &aEvent)
TOOL_BASE(const std::string &aToolName)
static const unsigned int COORDS_PADDING
ROUTER * m_router
VECTOR2I m_endSnapPoint
PCB_GRID_HELPER * m_gridHelper
VECTOR2I m_startSnapPoint
int Start() const
VECTOR2I::extended_type ecoord
Definition seg.h:40
static SEG::ecoord Square(int a)
Definition seg.h:119
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:34
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
@ SHUTDOWN
Tool is being shut down.
Definition tool_base.h:80
Generic, UI-independent tool event.
Definition tool_event.h:167
bool HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition tool_event.h:256
bool DisableGridSnapping() const
Definition tool_event.h:367
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:289
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition tool_event.h:311
int Modifier(int aMask=MD_MODIFIER_MASK) const
Return information about key modifiers state (Ctrl, Alt, etc.).
Definition tool_event.h:362
const VECTOR2D DragOrigin() const
Return the point where dragging has started.
Definition tool_event.h:295
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:72
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:61
@ GRID_WIRES
Definition grid_helper.h:60
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:750
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:30
Push and Shove diff pair dimensions (gap) settings dialog.
@ RM_MarkObstacles
Ignore collisions, mark obstacles.
void * NET_HANDLE
Definition pns_item.h:55
@ PNS_MODE_ROUTE_DIFF_PAIR
Definition pns_router.h:69
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
MAGNETIC_OPTIONS tracks
MAGNETIC_OPTIONS pads
@ MD_CTRL
Definition tool_event.h:140
@ MD_SHIFT
Definition tool_event.h:139
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683