KiCad PCB EDA Suite
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-2021 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>
24 using namespace std::placeholders;
25 
26 #include <pcb_painter.h>
27 #include <pcbnew_settings.h>
28 
29 #include <tools/pcb_grid_helper.h>
30 #include <wx/log.h>
31 
32 #include "pns_kicad_iface.h"
33 #include "pns_tool_base.h"
34 #include "pns_arc.h"
35 #include "pns_solid.h"
36 
37 
38 using namespace KIGFX;
39 
40 namespace PNS {
41 
42 
43 TOOL_BASE::TOOL_BASE( const std::string& aToolName ) :
44  PCB_TOOL_BASE( aToolName )
45 {
46  m_gridHelper = nullptr;
47  m_iface = nullptr;
48  m_router = nullptr;
49  m_cancelled = false;
50 
51  m_startItem = nullptr;
52  m_startHighlight = false;
53 
54  m_endItem = nullptr;
55  m_gridHelper = nullptr;
56 
57  m_cancelled = false;
58 }
59 
60 
62 {
63  delete m_gridHelper;
64  delete m_iface;
65  delete m_router;
66 }
67 
68 
70 {
71  delete m_gridHelper;
72  delete m_iface;
73  delete m_router;
74 
76  m_iface->SetBoard( board() );
77  m_iface->SetView( getView() );
78  m_iface->SetHostTool( this );
79  m_iface->SetDisplayOptions( &( frame()->GetDisplayOptions() ) );
80 
81  m_router = new ROUTER;
85 
87 
88  PCBNEW_SETTINGS* settings = frame()->GetPcbNewSettings();
89 
90  if( !settings->m_PnsSettings )
91  settings->m_PnsSettings = std::make_unique<ROUTING_SETTINGS>( settings, "tools.pns" );
92 
93  m_router->LoadSettings( settings->m_PnsSettings.get() );
94 
95  m_gridHelper = new PCB_GRID_HELPER( m_toolMgr, frame()->GetMagneticItemsSettings() );
96 }
97 
98 
99 ITEM* TOOL_BASE::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLayer, bool aIgnorePads,
100  const std::vector<ITEM*> aAvoidItems )
101 {
102  int tl = aLayer > 0 ? aLayer : getView()->GetTopLayer();
103 
104  static const int candidateCount = 5;
105  ITEM* prioritized[candidateCount];
106  SEG::ecoord dist[candidateCount];
107 
108  for( int i = 0; i < candidateCount; i++ )
109  {
110  prioritized[i] = nullptr;
111  dist[i] = VECTOR2I::ECOORD_MAX;
112  }
113 
114  ITEM_SET candidates = m_router->QueryHoverItems( aWhere );
115 
116  if( candidates.Empty() )
117  candidates = m_router->QueryHoverItems( aWhere, true );
118 
119  for( ITEM* item : candidates.Items() )
120  {
121  if( !item->IsRoutable() )
122  continue;
123 
124  if( !IsCopperLayer( item->Layers().Start() ) )
125  continue;
126 
127  if( !m_iface->IsAnyLayerVisible( item->Layers() ) )
128  continue;
129 
130  if( alg::contains( aAvoidItems, item ) )
131  continue;
132 
133  // fixme: this causes flicker with live loop removal...
134  //if( item->Parent() && !item->Parent()->ViewIsVisible() )
135  // continue;
136 
137  if( aNet <= 0 || item->Net() == aNet )
138  {
139  if( item->OfKind( ITEM::VIA_T | ITEM::SOLID_T ) )
140  {
141  if( item->OfKind( ITEM::SOLID_T ) && aIgnorePads )
142  continue;
143 
144  SEG::ecoord d = ( item->Shape()->Centre() - aWhere ).SquaredEuclideanNorm();
145 
146  if( d < dist[2] )
147  {
148  prioritized[2] = item;
149  dist[2] = d;
150  }
151 
152  if( item->Layers().Overlaps( tl ) && d < dist[0] )
153  {
154  prioritized[0] = item;
155  dist[0] = d;
156  }
157  }
158  else // ITEM::SEGMENT_T | ITEM::ARC_T
159  {
160  LINKED_ITEM* li = static_cast<LINKED_ITEM*>( item );
161  SEG::ecoord d = std::min( ( li->Anchor( 0 ) - aWhere ).SquaredEuclideanNorm(),
162  ( li->Anchor( 1 ) - aWhere ).SquaredEuclideanNorm() );
163 
164  if( d < dist[3] )
165  {
166  prioritized[3] = item;
167  dist[3] = d;
168  }
169 
170  if( item->Layers().Overlaps( tl ) && d < dist[1] )
171  {
172  prioritized[1] = item;
173  dist[1] = d;
174  }
175  }
176  }
177  else if ( item->Net() == 0 && m_router->Settings().Mode() == RM_MarkObstacles )
178  {
179  // Allow unconnected items as last resort in RM_MarkObstacles mode
180  if( item->OfKind( ITEM::SOLID_T ) && aIgnorePads )
181  continue;
182 
183  if( item->Layers().Overlaps( tl ) )
184  prioritized[4] = item;
185  }
186  }
187 
188  ITEM* rv = nullptr;
189 
191 
192  for( int i = 0; i < candidateCount; i++ )
193  {
194  ITEM* item = prioritized[i];
195 
196  if( highContrast && item && !item->Layers().Overlaps( tl ) )
197  item = nullptr;
198 
199  if( item && ( aLayer < 0 || item->Layers().Overlaps( aLayer ) ) )
200  {
201  rv = item;
202  break;
203  }
204  }
205 
206  if( rv )
207  {
208  wxLogTrace( wxT( "PNS" ), wxT( "%s, layer : %d, tl: %d" ),
209  rv->KindStr().c_str(),
210  rv->Layers().Start(),
211  tl );
212  }
213 
214  return rv;
215 }
216 
217 
218 void TOOL_BASE::highlightNet( bool aEnabled, int aNetcode )
219 {
221 
222  if( aNetcode >= 0 && aEnabled )
223  {
224  // If the user has previously set the current net to be highlighted,
225  // we assume they want to keep it highlighted after routing
227  && rs->GetHighlightNetCodes().count( aNetcode ) );
228 
229  rs->SetHighlight( true, aNetcode );
230  }
231  else
232  {
233  if( !m_startHighlight )
234  rs->SetHighlight( false );
235 
236  m_startHighlight = false;
237  }
238 
240 }
241 
242 
244 {
245  // Sync PNS engine settings with the general PCB editor options.
246  auto& pnss = m_router->Settings();
247 
248  // If we're dragging a track segment, don't try to snap to items on the same copper layer.
249  // This way we avoid 'flickery' behaviour for short segments when the snap algo is trying to
250  // snap to the corners of the segments next to the one being dragged.
251  if( m_startItem && aItem && m_router->GetState() == ROUTER::DRAG_SEGMENT
252  && aItem->Layer() == m_startItem->Layer() && aItem->OfKind( ITEM::SEGMENT_T )
254  return false;
255 
256  pnss.SetSnapToPads(
257  frame()->GetMagneticItemsSettings()->pads == MAGNETIC_OPTIONS::CAPTURE_CURSOR_IN_TRACK_TOOL ||
258  frame()->GetMagneticItemsSettings()->pads == MAGNETIC_OPTIONS::CAPTURE_ALWAYS );
259 
260  pnss.SetSnapToTracks(
261  frame()->GetMagneticItemsSettings()->tracks == MAGNETIC_OPTIONS::CAPTURE_CURSOR_IN_TRACK_TOOL
262  || frame()->GetMagneticItemsSettings()->tracks == MAGNETIC_OPTIONS::CAPTURE_ALWAYS );
263 
264  if( aItem )
265  {
266  if( aItem->OfKind( ITEM::VIA_T | ITEM::SEGMENT_T | ITEM::ARC_T ) )
267  return pnss.GetSnapToTracks();
268  else if( aItem->OfKind( ITEM::SOLID_T ) )
269  return pnss.GetSnapToPads();
270  }
271 
272  return false;
273 }
274 
275 
276 void TOOL_BASE::updateStartItem( const TOOL_EVENT& aEvent, bool aIgnorePads )
277 {
278  int tl = getView()->GetTopLayer();
279  VECTOR2I cp = aEvent.IsPrime() ? aEvent.Position()
280  : controls()->GetCursorPosition( !aEvent.Modifier( MD_SHIFT ) );
281  VECTOR2I p;
282  GAL* gal = m_toolMgr->GetView()->GetGAL();
283 
284  controls()->ForceCursorPosition( false );
286  m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
287 
288  if( aEvent.IsMotion() || aEvent.IsClick() )
289  p = aEvent.Position();
290  else
291  p = cp;
292 
293  m_startItem = pickSingleItem( aEvent.IsClick() ? cp : p, -1, -1, aIgnorePads );
294 
296  m_startItem = nullptr;
297 
300 }
301 
302 
304 {
305  int layer;
306  GAL* gal = m_toolMgr->GetView()->GetGAL();
307 
309  m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
310 
311  controls()->ForceCursorPosition( false );
312  VECTOR2I mousePos = controls()->GetMousePosition();
313 
314  if( m_router->Settings().Mode() != RM_MarkObstacles &&
315  ( m_router->GetCurrentNets().empty() || m_router->GetCurrentNets().front() < 0 ) )
316  {
317  m_endSnapPoint = snapToItem( nullptr, mousePos );
319  m_endItem = nullptr;
320 
321  return;
322  }
323 
324  if( m_router->IsPlacingVia() )
325  layer = -1;
326  else
327  layer = m_router->GetCurrentLayer();
328 
329  ITEM* endItem = nullptr;
330 
331  std::vector<int> nets = m_router->GetCurrentNets();
332 
333  for( int net : nets )
334  {
335  endItem = pickSingleItem( mousePos, net, layer, false, { m_startItem } );
336 
337  if( endItem )
338  break;
339  }
340 
341  if( m_gridHelper->GetSnap() && checkSnap( endItem ) )
342  {
343  m_endItem = endItem;
344  m_endSnapPoint = snapToItem( endItem, mousePos );
345  }
346  else
347  {
348  m_endItem = nullptr;
349  m_endSnapPoint = m_gridHelper->Align( mousePos );
350  }
351 
353 
354  if( m_endItem )
355  {
356  wxLogTrace( wxT( "PNS" ), wxT( "%s, layer : %d" ),
357  m_endItem->KindStr().c_str(),
358  m_endItem->Layers().Start() );
359  }
360 }
361 
362 
364 {
365  return m_router;
366 }
367 
368 
369 const VECTOR2I TOOL_BASE::snapToItem( ITEM* aItem, const VECTOR2I& aP )
370 {
371  if( !aItem || !m_iface->IsItemVisible( aItem ) )
372  {
373  return m_gridHelper->Align( aP );
374  }
375 
376  switch( aItem->Kind() )
377  {
378  case ITEM::SOLID_T:
379  return static_cast<SOLID*>( aItem )->Pos();
380 
381  case ITEM::VIA_T:
382  return static_cast<VIA*>( aItem )->Pos();
383 
384  case ITEM::SEGMENT_T:
385  case ITEM::ARC_T:
386  {
387  LINKED_ITEM* li = static_cast<LINKED_ITEM*>( aItem );
388  VECTOR2I A = li->Anchor( 0 );
389  VECTOR2I B = li->Anchor( 1 );
390  SEG::ecoord w_sq = SEG::Square( li->Width() / 2 );
391  SEG::ecoord distA_sq = ( aP - A ).SquaredEuclideanNorm();
392  SEG::ecoord distB_sq = ( aP - B ).SquaredEuclideanNorm();
393 
394  if( distA_sq < w_sq || distB_sq < w_sq )
395  {
396  return ( distA_sq < distB_sq ) ? A : B;
397  }
398  else if( aItem->Kind() == ITEM::SEGMENT_T )
399  {
400  // TODO(snh): Clean this up
401  SEGMENT* seg = static_cast<SEGMENT*>( li );
402  return m_gridHelper->AlignToSegment( aP, seg->Seg() );
403  }
404  else if( aItem->Kind() == ITEM::ARC_T )
405  {
406  ARC* arc = static_cast<ARC*>( li );
407  return m_gridHelper->AlignToArc( aP, *static_cast<const SHAPE_ARC*>( arc->Shape() ) );
408  }
409 
410  break;
411  }
412 
413  default:
414  break;
415  }
416 
417  return m_gridHelper->Align( aP );
418 }
419 
420 }
void SetHostTool(PCB_TOOL_BASE *aTool)
Base class for PNS router board items.
Definition: pns_item.h:55
KIGFX::VIEW * GetView() const
Definition: tool_manager.h:283
bool GetSnap() const
Definition: grid_helper.h:65
void SetView(KIGFX::VIEW *aView)
virtual int Layer() const
Definition: pns_item.h:158
bool Empty() const
Definition: pns_itemset.h:130
virtual VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
BOARD * board() const
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:243
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const ITEM_SET QueryHoverItems(const VECTOR2I &aP, bool aUseClearance=false)
Definition: pns_router.cpp:120
VECTOR2I m_startSnapPoint
Definition: pns_tool_base.h:70
bool IsClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:199
ENTRIES & Items()
Definition: pns_itemset.h:135
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
VECTOR2I::extended_type ecoord
Definition: seg.h:43
SIZES_SETTINGS m_savedSizes
Definition: pns_tool_base.h:68
void SyncWorld()
Definition: pns_router.cpp:92
bool IsPlacingVia() const
Definition: pns_router.cpp:809
bool IsMotion() const
Definition: tool_event.h:300
bool Overlaps(const LAYER_RANGE &aOther) const
Definition: pns_layerset.h:67
const std::vector< int > GetCurrentNets() const
Definition: pns_router.cpp:781
GAL * GetGAL() const
Return the #GAL this view is using to draw graphical primitives.
Definition: view.h:190
const SEG & Seg() const
Definition: pns_segment.h:84
bool IsHighlightEnabled() const
Return current highlight setting.
static SEG::ecoord Square(int a)
Definition: seg.h:122
bool GetUseGrid() const
Definition: grid_helper.h:68
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:208
void ClearWorld()
Definition: pns_router.cpp:102
int Start() const
Definition: pns_layerset.h:82
virtual int Width() const
PCB_BASE_EDIT_FRAME * frame() const
virtual void updateStartItem(const TOOL_EVENT &aEvent, bool aIgnorePads=false)
std::string KindStr() const
Returns the kind of the item, as string.
Definition: pns_item.cpp:160
VECTOR2I AlignToArc(const VECTOR2I &aPoint, const SHAPE_ARC &aSeg)
virtual void updateEndItem(const TOOL_EVENT &aEvent)
bool GetGridSnapping() const
static constexpr extended_type ECOORD_MAX
Definition: vector2d.h:79
virtual void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
const VECTOR2I snapToItem(ITEM *aSnapToItem, const VECTOR2I &aP)
bool checkSnap(ITEM *aItem)
PCB_GRID_HELPER * m_gridHelper
Definition: pns_tool_base.h:76
virtual ~TOOL_BASE()
bool IsItemVisible(const PNS::ITEM *aItem) const override
virtual int GetTopLayer() const
Definition: view.cpp:817
Generic, UI-independent tool event.
Definition: tool_event.h:152
Inactive layers are shown normally (no high-contrast mode)
ITEM * m_startItem
Definition: pns_tool_base.h:69
ROUTER * m_router
Definition: pns_tool_base.h:78
PNS_MODE Mode() const
Set the routing mode.
HIGH_CONTRAST_MODE m_ContrastModeDisplay
How inactive layers are displayed.
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
void SetSnapToPads(bool aSnap)
bool DisableGridSnapping() const
Definition: tool_event.h:341
std::unique_ptr< PNS::ROUTING_SETTINGS > m_PnsSettings
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:99
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
void SetInterface(ROUTER_IFACE *aIface)
Definition: pns_router.cpp:849
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:808
const PCB_DISPLAY_OPTIONS & displayOptions() const
void SetBoard(BOARD *aBoard)
void SetSnap(bool aSnap)
Definition: grid_helper.h:64
virtual void highlightNet(bool aEnabled, int aNetcode=-1)
int Modifier(int aMask=MD_MODIFIER_MASK) const
Definition: tool_event.h:336
bool IsPrime() const
Returns information about key modifiers state (Ctrl, Alt, etc.)
Definition: tool_event.h:330
virtual VECTOR2I Anchor(int n) const
Definition: pns_item.h:217
virtual VECTOR2I Align(const VECTOR2I &aPoint) const
Definition: grid_helper.cpp:95
const SHAPE * Shape() const override
Return the geometrical shape of the item.
Definition: pns_arc.h:77
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
void UpdateSizes(const SIZES_SETTINGS &aSizes)
Applies stored settings.
Definition: pns_router.cpp:570
void SetUseGrid(bool aSnapToGrid)
Definition: grid_helper.h:67
Ignore collisions, mark obstacles.
KIGFX::VIEW_CONTROLS * controls() const
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
VECTOR2I m_endSnapPoint
Definition: pns_tool_base.h:74
bool OfKind(int aKindMask) const
Return true if the item's type matches the mask aKindMask.
Definition: pns_item.h:138
PCBNEW_SETTINGS * GetPcbNewSettings() const
virtual ITEM * pickSingleItem(const VECTOR2I &aWhere, int aNet=-1, int aLayer=-1, bool aIgnorePads=false, const std::vector< ITEM * > aAvoidItems={})
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:77
PnsKind Kind() const
Return the type (kind) of the item.
Definition: pns_item.h:130
void LoadSettings(ROUTING_SETTINGS *aSettings)
Changes routing settings to ones passed in the parameter.
Definition: pns_router.h:196
const std::set< int > & GetHighlightNetCodes() const
Return the netcode of currently highlighted net.
ROUTER * Router() const
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition: view.cpp:762
Push and Shove diff pair dimensions (gap) settings dialog.
void SetDisplayOptions(const PCB_DISPLAY_OPTIONS *aDispOptions)
PNS_KICAD_IFACE * m_iface
Definition: pns_tool_base.h:77
ROUTING_SETTINGS & Settings()
Definition: pns_router.h:182
RouterState GetState() const
Definition: pns_router.h:134
VECTOR2I AlignToSegment(const VECTOR2I &aPoint, const SEG &aSeg)
const VECTOR2D Position() const
Returns the point where dragging has started.
Definition: tool_event.h:263
const LAYER_RANGE & Layers() const
Definition: pns_item.h:154
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
TOOL_BASE(TOOL_TYPE aType, TOOL_ID aId, const std::string &aName=std::string(""))
Definition: tool_base.h:68
Abstract interface for drawing on a 2D-surface.
bool IsAnyLayerVisible(const LAYER_RANGE &aLayer) const override
int GetCurrentLayer() const
Definition: pns_router.cpp:792