KiCad PCB EDA Suite
magnetic_tracks_functions.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2009-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
29 /* functions used to control the cursor position, when creating a track
30  * and when the "magnetic tracks" option is on
31  * (the current created track is kept near existing tracks
32  * the distance is the clearance between tracks)
33  */
34 
35 #include <fctsys.h>
36 #include <pcbnew.h>
37 #include <wxPcbStruct.h>
38 #include <macros.h>
39 
40 #include <class_board.h>
41 #include <class_track.h>
42 
43 #include <pcbnew_id.h>
44 
45 
53 static bool Join( wxPoint* aIntersectPoint, wxPoint a0, wxPoint a1, wxPoint b0, wxPoint b1 )
54 {
55  /* References:
56  http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
57  http://www.gekkou.co.uk/blogs/monologues/2007/12/13/1197586800000.html
58  */
59 
60  double denom;
61  double t;
62 
63  // if either segment is zero length
64  if( a1.x==a0.x && a1.y==a0.y )
65  return false;
66 
67  if( b1.x==b0.x && b1.y==b0.y )
68  return false;
69 
70  a1 -= a0;
71  b1 -= b0;
72 
73  b0 -= a0;
74 
75  denom = (double) b1.y * a1.x - (double) b1.x * a1.y;
76 
77  if( !denom )
78  {
79  return false; // parallel
80  }
81 
82  t = ((double) b1.y * b0.x - (double) b1.x * b0.y ) / denom;
83 
84  t = std::min( std::max( t, 0.0 ), 1.0 );
85 
86  aIntersectPoint->x = KiROUND( a0.x + t * a1.x );
87  aIntersectPoint->y = KiROUND( a0.y + t * a1.y );
88 
89  return true;
90 }
91 
92 
93 /*
94  * "Project" finds the projection of a grid point on a track. This is the point
95  * from where we want to draw new orthogonal tracks when starting on a track.
96  */
97 bool FindBestGridPointOnTrack( wxPoint* aNearPos, wxPoint on_grid, const TRACK* track )
98 {
99  if( track->GetStart ()== track->GetEnd() )
100  return false;
101 
102  wxPoint vec = track->GetEnd() - track->GetStart();
103 
104  double t = double( on_grid.x - track->GetStart().x ) * vec.x +
105  double( on_grid.y - track->GetStart().y ) * vec.y;
106 
107  t /= (double) vec.x * vec.x + (double) vec.y * vec.y;
108  t = std::min( std::max( t, 0.0 ), 1.0 );
109 
110  aNearPos->x = KiROUND( track->GetStart().x + t * vec.x );
111  aNearPos->y = KiROUND( track->GetStart().y + t * vec.y );
112 
113  return true;
114 }
115 
116 
129 bool Magnetize( PCB_EDIT_FRAME* frame, int aCurrentTool, wxSize aGridSize,
130  wxPoint on_grid, wxPoint* curpos )
131 {
132  bool doCheckNet = frame->Settings().m_magneticPads != CAPTURE_ALWAYS && frame->Settings().m_legacyDrcOn;
133  bool doTrack = false;
134  bool doPad = false;
135  bool amMovingVia = false;
136 
137  BOARD* m_Pcb = frame->GetBoard();
138  TRACK* currTrack = g_CurrentTrackSegment;
139  BOARD_ITEM* currItem = frame->GetCurItem();
140  PCB_SCREEN* screen = frame->GetScreen();
141  wxPoint pos = frame->RefPos( true );
142 
143  // D( printf( "currTrack=%p currItem=%p currTrack->Type()=%d currItem->Type()=%d\n", currTrack, currItem, currTrack ? currTrack->Type() : 0, currItem ? currItem->Type() : 0 ); )
144 
145  if( !currTrack && currItem && currItem->Type()==PCB_VIA_T && currItem->GetFlags() )
146  {
147  // moving a VIA
148  currTrack = (TRACK*) currItem;
149  amMovingVia = true;
150 
151  return false; // comment this return out and play with it.
152  }
153  else if( currItem != currTrack )
154  {
155  currTrack = NULL;
156  }
157 
158  if( frame->Settings().m_magneticPads == CAPTURE_ALWAYS )
159  doPad = true;
160 
161  if( frame->Settings().m_magneticTracks == CAPTURE_ALWAYS )
162  doTrack = true;
163 
164  if( aCurrentTool == ID_TRACK_BUTT || amMovingVia )
165  {
167 
168  if( frame->Settings().m_magneticPads == q )
169  doPad = true;
170 
171  if( frame->Settings().m_magneticTracks == q )
172  doTrack = true;
173  }
174 
175  // The search precedence order is pads, then tracks/vias
176  if( doPad )
177  {
178  LSET layer_mask( screen->m_Active_Layer );
179  D_PAD* pad = m_Pcb->GetPad( pos, layer_mask );
180 
181  if( pad )
182  {
183  if( doCheckNet && currTrack && currTrack->GetNetCode() != pad->GetNetCode() )
184  return false;
185 
186  *curpos = pad->GetPosition();
187  return true;
188  }
189  }
190 
191  // after pads, only track & via tests remain, skip them if not desired
192  if( doTrack )
193  {
194  PCB_LAYER_ID layer = screen->m_Active_Layer;
195 
196  for( TRACK* via = m_Pcb->m_Track;
197  via && (via = via->GetVia( *curpos, layer )) != NULL;
198  via = via->Next() )
199  {
200  if( via != currTrack ) // a via cannot influence itself
201  {
202  if( !doCheckNet || !currTrack || currTrack->GetNetCode() == via->GetNetCode() )
203  {
204  *curpos = via->GetStart();
205  // D(printf("via hit\n");)
206  return true;
207  }
208  }
209  }
210 
211  if( !currTrack )
212  {
213  LSET layers( layer );
214 
215  TRACK* track = m_Pcb->GetVisibleTrack( m_Pcb->m_Track, pos, layers );
216 
217  if( !track || track->Type() != PCB_TRACE_T )
218  {
219  return false;
220  }
221 
222  return FindBestGridPointOnTrack( curpos, on_grid, track );
223  }
224 
225  /*
226  * In two segment mode, ignore the final segment if it's inside a grid square.
227  */
228  if( !amMovingVia && currTrack && frame->Settings().m_legacyUseTwoSegmentTracks && currTrack->Back()
229  && currTrack->GetStart().x - aGridSize.x < currTrack->GetEnd().x
230  && currTrack->GetStart().x + aGridSize.x > currTrack->GetEnd().x
231  && currTrack->GetStart().y - aGridSize.y < currTrack->GetEnd().y
232  && currTrack->GetStart().y + aGridSize.y > currTrack->GetEnd().y )
233  {
234  currTrack = currTrack->Back();
235  }
236 
237 
238  for( TRACK* track = m_Pcb->m_Track; track; track = track->Next() )
239  {
240  if( track->Type() != PCB_TRACE_T )
241  continue;
242 
243  if( doCheckNet && currTrack && currTrack->GetNetCode() != track->GetNetCode() )
244  continue;
245 
246  if( m_Pcb->IsLayerVisible( track->GetLayer() ) == false )
247  continue;
248 
249  // omit the layer check if moving a via
250  if( !amMovingVia && !track->IsOnLayer( layer ) )
251  continue;
252 
253  if( !track->HitTest( *curpos ) )
254  continue;
255 
256  if( Join( curpos, track->GetStart(), track->GetEnd(), currTrack->GetStart(), currTrack->GetEnd() ) )
257  {
258  return true;
259  }
260 
261  if( aCurrentTool == ID_TRACK_BUTT || amMovingVia )
262  {
263  // At this point we have a drawing mouse on a track, we are drawing
264  // a new track and that new track is parallel to the track the
265  // mouse is on. Find the nearest end point of the track under mouse
266  // to the mouse and return that.
267  double distStart = GetLineLength( *curpos, track->GetStart() );
268  double distEnd = GetLineLength( *curpos, track->GetEnd() );
269 
270  // if track not via, or if its a via dragging but not with its adjacent track
271  if( currTrack->Type() != PCB_VIA_T ||
272  ( currTrack->GetStart() != track->GetStart() && currTrack->GetStart() != track->GetEnd() ))
273  {
274  double max_dist = currTrack->GetWidth() / 2.0f;
275 
276  if( distStart <= max_dist )
277  {
278  // D(printf("nearest end is start\n");)
279  *curpos = track->GetStart();
280  return true;
281  }
282 
283  if( distEnd <= max_dist )
284  {
285  // D(printf("nearest end is end\n");)
286  *curpos = track->GetEnd();
287  return true;
288  }
289 
290  // @todo otherwise confine curpos such that it stays centered within "track"
291  }
292  }
293  }
294  }
295 
296  return false;
297 }
#define g_CurrentTrackSegment
most recently created segment
Definition: pcbnew.h:95
KICAD_T Type() const
Function Type()
Definition: base_struct.h:212
STATUS_FLAGS GetFlags() const
Definition: base_struct.h:269
double GetLineLength(const wxPoint &aPointA, const wxPoint &aPointB)
Function GetLineLength returns the length of a line segment defined by aPointA and aPointB...
Definition: trigo.h:183
static int KiROUND(double v)
KiROUND rounds a floating point number to an int using "round halfway cases away from zero"...
Definition: common.h:107
Class BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class...
Class BOARD to handle a board.
bool Magnetize(PCB_EDIT_FRAME *frame, int aCurrentTool, wxSize aGridSize, wxPoint on_grid, wxPoint *curpos)
Function Magnetize tests to see if there are any magnetic items within near reach of the given "curpo...
MAGNETIC_PAD_OPTION_VALUES m_magneticPads
BOARD * GetBoard() const
D_PAD * GetPad(unsigned aIndex) const
Function GetPad.
PCB_LAYER_ID m_Active_Layer
TRACK * Back() const
Definition: class_track.h:99
const wxPoint & GetEnd() const
Definition: class_track.h:118
Functions relatives to tracks, vias and segments used to fill zones.
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:107
This file contains miscellaneous commonly used macros and functions.
MAGNETIC_PAD_OPTION_VALUES m_magneticTracks
PCB_LAYER_ID
A quick note on layer IDs:
Class LSET is a set of PCB_LAYER_IDs.
PCB_GENERAL_SETTINGS & Settings()
const wxPoint & GetStart() const
Definition: class_track.h:121
const wxPoint & GetPosition() const override
Definition: class_pad.h:205
TRACK * GetVisibleTrack(TRACK *aStartingTrace, const wxPoint &aPosition, LSET aLayerSet) const
Function GetVisibleTrack finds the neighboring visible segment of aTrace at aPosition that is on a la...
static bool Join(wxPoint *aIntersectPoint, wxPoint a0, wxPoint a1, wxPoint b0, wxPoint b1)
Function Join finds the point where line segment (b1,b0) intersects with segment (a1,a0).
bool FindBestGridPointOnTrack(wxPoint *aNearPos, wxPoint on_grid, const TRACK *track)
Finds the projection of a grid point on a track.
int GetNetCode() const
Function GetNetCode.
wxPoint RefPos(bool useMouse) const
Function RefPos Return the reference position, coming from either the mouse position or the cursor po...
TRACK * Next() const
Definition: class_track.h:98
#define max(a, b)
Definition: auxiliary.h:86
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:169
int GetWidth() const
Definition: class_track.h:115
PCB_SCREEN * GetScreen() const override
Function GetScreen returns a pointer to a BASE_SCREEN or one of its derivatives.
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:108
DLIST< TRACK > m_Track
Definition: class_board.h:246
BOARD_ITEM * GetCurItem()
#define min(a, b)
Definition: auxiliary.h:85
bool IsLayerVisible(PCB_LAYER_ID aLayer) const
Function IsLayerVisible is a proxy function that calls the correspondent function in m_BoardSettings ...
Definition: class_board.h:451