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 Project( 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 = g_MagneticPadOption != CAPTURE_ALWAYS && g_Drc_On;
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 
159  doPad = true;
160 
162  doTrack = true;
163 
164  if( aCurrentTool == ID_TRACK_BUTT || amMovingVia )
165  {
167 
168  if( g_MagneticPadOption == q )
169  doPad = true;
170 
171  if( g_MagneticTrackOption == 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 Project( 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 && g_TwoSegmentTrackBuild && 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:101
KICAD_T Type() const
Function Type()
Definition: base_struct.h:198
bool g_Drc_On
Definition: pcbnew.cpp:70
STATUS_FLAGS GetFlags() const
Definition: base_struct.h:255
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...
BOARD * GetBoard() const
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.
PCB_LAYER_ID
A quick note on layer IDs:
Class LSET is a set of PCB_LAYER_IDs.
const wxPoint & GetStart() const
Definition: class_track.h:121
const wxPoint & GetPosition() const override
Definition: class_pad.h:170
bool Project(wxPoint *aNearPos, wxPoint on_grid, const TRACK *track)
Finds the projection of a grid point on a track.
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...
D_PAD * GetPad(unsigned aIndex) const
Function GetPad.
Definition: class_board.h:750
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).
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
int g_MagneticTrackOption
Definition: pcbnew.cpp:81
#define max(a, b)
Definition: auxiliary.h:86
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:166
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:244
int g_MagneticPadOption
Definition: pcbnew.cpp:80
BOARD_ITEM * GetCurItem()
bool g_TwoSegmentTrackBuild
Definition: pcbnew.cpp:76
#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:440