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-2020 KiCad Developers, see AUTHORS.txt for contributors.
6  * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
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 
31 #include "pns_kicad_iface.h"
32 #include "pns_tool_base.h"
33 #include "pns_arc.h"
34 #include "pns_solid.h"
35 
36 
37 using namespace KIGFX;
38 
39 namespace PNS {
40 
41 
42 TOOL_BASE::TOOL_BASE( const std::string& aToolName ) :
43  PCB_TOOL_BASE( aToolName )
44 {
45  m_gridHelper = nullptr;
46  m_iface = nullptr;
47  m_router = nullptr;
48  m_cancelled = false;
49 
50  m_startItem = nullptr;
51  m_startHighlight = false;
52 
53  m_endItem = nullptr;
54  m_gridHelper = nullptr;
55 
56  m_cancelled = false;
57 }
58 
59 
61 {
62  delete m_gridHelper;
63  delete m_iface;
64  delete m_router;
65 }
66 
67 
69 {
70  delete m_gridHelper;
71  delete m_iface;
72  delete m_router;
73 
75  m_iface->SetBoard( board() );
76  m_iface->SetView( getView() );
77  m_iface->SetHostTool( this );
78  m_iface->SetDisplayOptions( &( frame()->GetDisplayOptions() ) );
79 
80  m_router = new ROUTER;
84 
86 
87  PCBNEW_SETTINGS* settings = frame()->GetPcbNewSettings();
88 
89  if( !settings->m_PnsSettings )
90  settings->m_PnsSettings = std::make_unique<ROUTING_SETTINGS>( settings, "tools.pns" );
91 
92  m_router->LoadSettings( settings->m_PnsSettings.get() );
93 
94  m_gridHelper = new PCB_GRID_HELPER( m_toolMgr, frame()->GetMagneticItemsSettings() );
95 }
96 
97 
98 ITEM* TOOL_BASE::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLayer, bool aIgnorePads,
99  const std::vector<ITEM*> aAvoidItems )
100 {
101  int tl = aLayer > 0 ? aLayer : getView()->GetTopLayer();
102 
103  static const int candidateCount = 5;
104  ITEM* prioritized[candidateCount];
105  SEG::ecoord dist[candidateCount];
106 
107  for( int i = 0; i < candidateCount; i++ )
108  {
109  prioritized[i] = nullptr;
110  dist[i] = VECTOR2I::ECOORD_MAX;
111  }
112 
113  ITEM_SET candidates = m_router->QueryHoverItems( aWhere );
114 
115  for( ITEM* item : candidates.Items() )
116  {
117  if( !item->IsRoutable() )
118  continue;
119 
120  if( !IsCopperLayer( item->Layers().Start() ) )
121  continue;
122 
123  if( !m_iface->IsAnyLayerVisible( item->Layers() ) )
124  continue;
125 
126  if( alg::contains( aAvoidItems, item ) )
127  continue;
128 
129  // fixme: this causes flicker with live loop removal...
130  //if( item->Parent() && !item->Parent()->ViewIsVisible() )
131  // continue;
132 
133  if( aNet <= 0 || item->Net() == aNet )
134  {
135  if( item->OfKind( ITEM::VIA_T | ITEM::SOLID_T ) )
136  {
137  if( item->OfKind( ITEM::SOLID_T ) && aIgnorePads )
138  continue;
139 
140  SEG::ecoord d = ( item->Shape()->Centre() - aWhere ).SquaredEuclideanNorm();
141 
142  if( d < dist[2] )
143  {
144  prioritized[2] = item;
145  dist[2] = d;
146  }
147 
148  if( item->Layers().Overlaps( tl ) && d < dist[0] )
149  {
150  prioritized[0] = item;
151  dist[0] = d;
152  }
153  }
154  else // ITEM::SEGMENT_T | ITEM::ARC_T
155  {
156  LINKED_ITEM* li = static_cast<LINKED_ITEM*>( item );
157  SEG::ecoord d = std::min( ( li->Anchor( 0 ) - aWhere ).SquaredEuclideanNorm(),
158  ( li->Anchor( 1 ) - aWhere ).SquaredEuclideanNorm() );
159 
160  if( d < dist[3] )
161  {
162  prioritized[3] = item;
163  dist[3] = d;
164  }
165 
166  if( item->Layers().Overlaps( tl ) && d < dist[1] )
167  {
168  prioritized[1] = item;
169  dist[1] = d;
170  }
171  }
172  }
173  // Allow unconnected items as last resort in RM_MarkObstacles mode
174  else if ( item->Net() == 0 && m_router->Settings().Mode() == RM_MarkObstacles )
175  {
176  if( item->OfKind( ITEM::SOLID_T ) && aIgnorePads )
177  continue;
178 
179  if( item->Layers().Overlaps( tl ) )
180  prioritized[4] = item;
181  }
182  }
183 
184  ITEM* rv = NULL;
185 
187 
188  for( int i = 0; i < candidateCount; i++ )
189  {
190  ITEM* item = prioritized[i];
191 
192  if( highContrast && item && !item->Layers().Overlaps( tl ) )
193  item = nullptr;
194 
195  if( item && ( aLayer < 0 || item->Layers().Overlaps( aLayer ) ) )
196  {
197  rv = item;
198  break;
199  }
200  }
201 
202  if( rv )
203  {
204  wxLogTrace( "PNS", "%s, layer : %d, tl: %d", rv->KindStr().c_str(), rv->Layers().Start(), tl );
205  }
206 
207  return rv;
208 }
209 
210 
211 void TOOL_BASE::highlightNet( bool aEnabled, int aNetcode )
212 {
214 
215  if( aNetcode >= 0 && aEnabled )
216  {
217  // If the user has previously set the current net to be highlighted,
218  // we assume they want to keep it highlighted after routing
220  && rs->GetHighlightNetCodes().count( aNetcode ) );
221 
222  rs->SetHighlight( true, aNetcode );
223  }
224  else
225  {
226  if( !m_startHighlight )
227  rs->SetHighlight( false );
228 
229  m_startHighlight = false;
230  }
231 
233 }
234 
236 {
237  // Sync PNS engine settings with the general PCB editor options.
238  auto& pnss = m_router->Settings();
239 
240  pnss.SetSnapToPads(
241  frame()->GetMagneticItemsSettings()->pads == MAGNETIC_OPTIONS::CAPTURE_CURSOR_IN_TRACK_TOOL ||
242  frame()->GetMagneticItemsSettings()->pads == MAGNETIC_OPTIONS::CAPTURE_ALWAYS );
243 
244  pnss.SetSnapToTracks(
245  frame()->GetMagneticItemsSettings()->tracks == MAGNETIC_OPTIONS::CAPTURE_CURSOR_IN_TRACK_TOOL
246  || frame()->GetMagneticItemsSettings()->tracks == MAGNETIC_OPTIONS::CAPTURE_ALWAYS );
247 
248  if( aItem )
249  {
250  if( aItem->OfKind( ITEM::VIA_T | ITEM::SEGMENT_T | ITEM::ARC_T ) )
251  return pnss.GetSnapToTracks();
252  else if( aItem->OfKind( ITEM::SOLID_T ) )
253  return pnss.GetSnapToPads();
254  }
255 
256  return false;
257 }
258 
259 void TOOL_BASE::updateStartItem( const TOOL_EVENT& aEvent, bool aIgnorePads )
260 {
261  int tl = getView()->GetTopLayer();
262  VECTOR2I cp = controls()->GetCursorPosition( !aEvent.Modifier( MD_SHIFT ) );
263  VECTOR2I p;
264  GAL* gal = m_toolMgr->GetView()->GetGAL();
265 
266  controls()->ForceCursorPosition( false );
268  m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
269 
270  if( aEvent.IsMotion() || aEvent.IsClick() )
271  p = aEvent.Position();
272  else
273  p = cp;
274 
275  m_startItem = pickSingleItem( p, -1, -1, aIgnorePads );
276 
278  m_startItem = nullptr;
279 
281 
282  if( checkSnap( m_startItem ) )
284 }
285 
286 
288 {
289  int layer;
290  GAL* gal = m_toolMgr->GetView()->GetGAL();
291 
293  m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
294 
295  controls()->ForceCursorPosition( false );
296  VECTOR2I mousePos = controls()->GetMousePosition();
297 
298  if( m_router->Settings().Mode() != RM_MarkObstacles &&
299  ( m_router->GetCurrentNets().empty() || m_router->GetCurrentNets().front() < 0 ) )
300  {
301  m_endSnapPoint = snapToItem( nullptr, mousePos );
303  m_endItem = nullptr;
304 
305  return;
306  }
307 
308  if( m_router->IsPlacingVia() )
309  layer = -1;
310  else
311  layer = m_router->GetCurrentLayer();
312 
313  ITEM* endItem = nullptr;
314 
315  std::vector<int> nets = m_router->GetCurrentNets();
316 
317  for( int net : nets )
318  {
319  endItem = pickSingleItem( mousePos, net, layer, false, { m_startItem } );
320 
321  if( endItem )
322  break;
323  }
324 
325  if( m_gridHelper->GetSnap() && checkSnap( endItem ) )
326  {
327  m_endItem = endItem;
328  m_endSnapPoint = snapToItem( endItem, mousePos );
329  }
330  else
331  {
332  m_endItem = nullptr;
333  m_endSnapPoint = m_gridHelper->Align( mousePos );
334  }
335 
337 
338  if( m_endItem )
339  {
340  wxLogTrace( "PNS", "%s, layer : %d",
341  m_endItem->KindStr().c_str(),
342  m_endItem->Layers().Start() );
343  }
344 }
345 
346 
348 {
349  return m_router;
350 }
351 
352 
354 {
355  if( !aItem || !m_iface->IsItemVisible( aItem ) )
356  {
357  return m_gridHelper->Align( aP );
358  }
359 
360  switch( aItem->Kind() )
361  {
362  case ITEM::SOLID_T:
363  return static_cast<SOLID*>( aItem )->Pos();
364 
365  case ITEM::VIA_T:
366  return static_cast<VIA*>( aItem )->Pos();
367 
368  case ITEM::SEGMENT_T:
369  case ITEM::ARC_T:
370  {
371  LINKED_ITEM* li = static_cast<LINKED_ITEM*>( aItem );
372  VECTOR2I A = li->Anchor( 0 );
373  VECTOR2I B = li->Anchor( 1 );
374  SEG::ecoord w_sq = SEG::Square( li->Width() / 2 );
375  SEG::ecoord distA_sq = ( aP - A ).SquaredEuclideanNorm();
376  SEG::ecoord distB_sq = ( aP - B ).SquaredEuclideanNorm();
377 
378  if( distA_sq < w_sq || distB_sq < w_sq )
379  {
380  return ( distA_sq < distB_sq ) ? A : B;
381  }
382  // TODO(snh): Clean this up
383  else if( aItem->Kind() == ITEM::SEGMENT_T )
384  {
385  SEGMENT* seg = static_cast<SEGMENT*>( li );
386  return m_gridHelper->AlignToSegment( aP, seg->Seg() );
387  }
388  else if( aItem->Kind() == ITEM::ARC_T )
389  {
390  ARC* arc = static_cast<ARC*>( li );
391  return m_gridHelper->AlignToArc( aP, *static_cast<const SHAPE_ARC*>( arc->Shape() ) );
392  }
393  }
394  break;
395 
396  default:
397  break;
398  }
399 
400  return m_gridHelper->Align( aP );
401 }
402 
403 }
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:289
bool GetSnap() const
Definition: grid_helper.h:65
void SetView(KIGFX::VIEW *aView)
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:175
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
VECTOR2I m_startSnapPoint
Definition: pns_tool_base.h:70
const ITEM_SET QueryHoverItems(const VECTOR2I &aP)
Definition: pns_router.cpp:120
bool IsClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:181
ENTRIES & Items()
Definition: pns_itemset.h:138
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:215
VECTOR2I::extended_type ecoord
Definition: seg.h:44
SIZES_SETTINGS m_savedSizes
Definition: pns_tool_base.h:68
void SyncWorld()
Definition: pns_router.cpp:93
bool IsPlacingVia() const
Definition: pns_router.cpp:763
bool IsMotion() const
Definition: tool_event.h:316
bool Overlaps(const LAYER_RANGE &aOther) const
Definition: pns_layerset.h:67
const std::vector< int > GetCurrentNets() const
Definition: pns_router.cpp:735
GAL * GetGAL() const
Return the #GAL this view is using to draw graphical primitives.
Definition: view.h:189
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:123
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:207
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:126
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.
bool checkSnap(ITEM *aItem)
PCB_GRID_HELPER * m_gridHelper
Definition: pns_tool_base.h:76
#define NULL
virtual ~TOOL_BASE()
bool IsItemVisible(const PNS::ITEM *aItem) const override
virtual int GetTopLayer() const
Definition: view.cpp:828
Generic, UI-independent tool event.
Definition: tool_event.h:173
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:357
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:81
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:805
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:352
virtual VECTOR2I Anchor(int n) const
Definition: pns_item.h:213
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.
const VECTOR2I snapToItem(ITEM *aSnapToItem, VECTOR2I aP)
void UpdateSizes(const SIZES_SETTINGS &aSizes)
Applies stored settings.
Definition: pns_router.cpp:535
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:134
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:78
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
PnsKind Kind() const
Return the type (kind) of the item.
Definition: pns_item.h:126
void LoadSettings(ROUTING_SETTINGS *aSettings)
Changes routing settings to ones passed in the parameter.
Definition: pns_router.h:198
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:773
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:184
VECTOR2I AlignToSegment(const VECTOR2I &aPoint, const SEG &aSeg)
const VECTOR2D Position() const
Returns the point where dragging has started.
Definition: tool_event.h:284
const LAYER_RANGE & Layers() const
Definition: pns_item.h:150
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:69
Abstract interface for drawing on a 2D-surface.
bool IsAnyLayerVisible(const LAYER_RANGE &aLayer) const override
int GetCurrentLayer() const
Definition: pns_router.cpp:746