magnetic_tracks_functions.cpp 8.97 KB
Newer Older
1 2 3
/**
 * @file magnetic_tracks_functions.cpp
 */
charras's avatar
charras committed
4

5
/* functions used to control the cursor position, when creating a track
charras's avatar
charras committed
6 7 8 9 10
 * and when the "magnetic tracks" option is on
 * (the current created track is kept near existing tracks
 *  the distance is the clearance between tracks)
 */

11 12 13 14 15
#include <fctsys.h>
#include <pcbnew.h>
#include <wxPcbStruct.h>
#include <macros.h>
#include <pcbcommon.h>
16

17 18
#include <class_board.h>
#include <class_track.h>
19

20 21
#include <protos.h>
#include <pcbnew_id.h>
charras's avatar
charras committed
22 23 24 25 26 27 28 29 30


/**
 * Function Join
 * finds the point where line segment (b1,b0) intersects with segment (a1,a0).
 * If that point would be outside of (a0,a1), the respective endpoint is used.
 * Join returns the point in "res" and "true" if a suitable point was found,
 * "false" if both lines are parallel or if the length of either segment is zero.
 */
31
static bool Join( wxPoint* aIntersectPoint, wxPoint a0, wxPoint a1, wxPoint b0, wxPoint b1 )
charras's avatar
charras committed
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
{
    /* References:
        http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
        http://www.gekkou.co.uk/blogs/monologues/2007/12/13/1197586800000.html
    */

    double  denom;
    double  t;

    // if either segment is zero length
    if( a1.x==a0.x && a1.y==a0.y )
        return false;

    if( b1.x==b0.x && b1.y==b0.y )
        return false;

    a1 -= a0;
    b1 -= b0;

    b0 -= a0;

    denom = (double) b1.y * a1.x - (double) b1.x * a1.y;
54

charras's avatar
charras committed
55 56 57 58 59 60 61
    if( !denom )
    {
        return false;       // parallel
    }

    t = ((double) b1.y * b0.x - (double) b1.x * b0.y ) / denom;

jean-pierre charras's avatar
jean-pierre charras committed
62
    t = std::min( std::max( t, 0.0 ), 1.0 );
charras's avatar
charras committed
63

64 65
    aIntersectPoint->x = KiROUND( a0.x + t * a1.x );
    aIntersectPoint->y = KiROUND( a0.y + t * a1.y );
charras's avatar
charras committed
66 67 68 69 70 71 72 73 74

    return true;
}


/*
 * "Project" finds the projection of a grid point on a track. This is the point
 * from where we want to draw new orthogonal tracks when starting on a track.
 */
75
bool Project( wxPoint* aNearPos, wxPoint on_grid, const TRACK* track )
charras's avatar
charras committed
76
{
77
    if( track->GetStart ()== track->GetEnd() )
charras's avatar
charras committed
78 79
        return false;

80
    wxPoint vec = track->GetEnd() - track->GetStart();
charras's avatar
charras committed
81

82 83
    double t = double( on_grid.x - track->GetStart().x ) * vec.x +
               double( on_grid.y - track->GetStart().y ) * vec.y;
charras's avatar
charras committed
84 85

    t /= (double) vec.x * vec.x + (double) vec.y * vec.y;
jean-pierre charras's avatar
jean-pierre charras committed
86
    t = std::min( std::max( t, 0.0 ), 1.0 );
charras's avatar
charras committed
87

88 89
    aNearPos->x = KiROUND( track->GetStart().x + t * vec.x );
    aNearPos->y = KiROUND( track->GetStart().y + t * vec.y );
charras's avatar
charras committed
90 91 92 93 94 95 96 97 98 99

    return true;
}


/**
 * Function Magnetize
 * tests to see if there are any magnetic items within near reach of the given
 * "curpos".  If yes, then curpos is adjusted appropriately according to that
 * near magnetic item and true is returned.
100 101
 * @param frame = the current frame
 * @param aCurrentTool = the current tool id (from vertical right toolbar)
102 103
 * @param aGridSize = the current grid size
 * @param on_grid = the on grid position near initial position ( often on_grid = curpos)
charras's avatar
charras committed
104 105 106
 * @param curpos The initial position, and what to adjust if a change is needed.
 * @return bool - true if the position was adjusted magnetically, else false.
 */
107
bool Magnetize( PCB_EDIT_FRAME* frame, int aCurrentTool, wxSize aGridSize,
108
                wxPoint on_grid, wxPoint* curpos )
charras's avatar
charras committed
109
{
110
    bool    doCheckNet = g_MagneticPadOption != capture_always && g_Drc_On;
charras's avatar
charras committed
111 112 113 114
    bool    doTrack = false;
    bool    doPad = false;
    bool    amMovingVia = false;

115
    BOARD* m_Pcb = frame->GetBoard();
charras's avatar
charras committed
116 117
    TRACK*      currTrack = g_CurrentTrackSegment;
    BOARD_ITEM* currItem  = frame->GetCurItem();
118
    PCB_SCREEN* screen = frame->GetScreen();
119
    wxPoint     pos = frame->RefPos( true );
charras's avatar
charras committed
120 121 122

    // D( printf( "currTrack=%p currItem=%p currTrack->Type()=%d currItem->Type()=%d\n",  currTrack, currItem, currTrack ? currTrack->Type() : 0, currItem ? currItem->Type() : 0 ); )

123
    if( !currTrack && currItem && currItem->Type()==PCB_VIA_T && currItem->GetFlags() )
charras's avatar
charras committed
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
    {
        // moving a VIA
        currTrack = (TRACK*) currItem;
        amMovingVia = true;

        return false;   // comment this return out and play with it.
    }
    else if( currItem != currTrack )
    {
        currTrack = NULL;
    }

    if( g_MagneticPadOption == capture_always )
        doPad = true;

    if( g_MagneticTrackOption == capture_always )
        doTrack = true;

    if( aCurrentTool == ID_TRACK_BUTT || amMovingVia )
    {
        int q = capture_cursor_in_track_tool;

        if( g_MagneticPadOption == q )
            doPad = true;

        if( g_MagneticTrackOption == q )
            doTrack = true;
    }

    // D(printf("doPad=%d doTrack=%d aCurrentTool=%d amMovingVia=%d\n", doPad, doTrack, aCurrentTool, amMovingVia );)

    //  The search precedence order is pads, then tracks/vias

    if( doPad )
    {
159
        LAYER_MSK layer_mask = GetLayerMask( screen->m_Active_Layer );
160
        D_PAD* pad = m_Pcb->GetPad( pos, layer_mask );
161

charras's avatar
charras committed
162 163 164 165 166
        if( pad )
        {
            if( doCheckNet && currTrack && currTrack->GetNet() != pad->GetNet() )
                return false;

Dick Hollenbeck's avatar
Dick Hollenbeck committed
167
            *curpos = pad->GetPosition();
charras's avatar
charras committed
168 169 170 171 172 173 174
            return true;
        }
    }

    // after pads, only track & via tests remain, skip them if not desired
    if( doTrack )
    {
175
        LAYER_NUM layer = screen->m_Active_Layer;
charras's avatar
charras committed
176 177

        for( TRACK* via = m_Pcb->m_Track;
178
             via && (via = via->GetVia( *curpos, layer )) != NULL;
charras's avatar
charras committed
179 180 181 182 183 184
             via = via->Next() )
        {
            if( via != currTrack )   // a via cannot influence itself
            {
                if( !doCheckNet || !currTrack || currTrack->GetNet() == via->GetNet() )
                {
185
                    *curpos = via->GetStart();
charras's avatar
charras committed
186 187 188 189 190 191 192 193
                    // D(printf("via hit\n");)
                    return true;
                }
            }
        }

        if( !currTrack )
        {
194
            LAYER_MSK layer_mask = GetLayerMask( layer );
charras's avatar
charras committed
195

196
            TRACK* track = m_Pcb->GetTrace( m_Pcb->m_Track, pos, layer_mask );
197

198
            if( !track || track->Type() != PCB_TRACE_T )
charras's avatar
charras committed
199 200 201 202 203 204 205 206 207 208
            {
                // D(printf("!currTrack and track=%p not found, layer_mask=0x%X\n", track, layer_mask );)
                return false;
            }

            // D( printf( "Project\n" ); )
            return Project( curpos, on_grid, track );
        }

        /*
209
         * In two segment mode, ignore the final segment if it's inside a grid square.
charras's avatar
charras committed
210 211
         */
        if( !amMovingVia && currTrack && g_TwoSegmentTrackBuild && currTrack->Back()
212 213 214 215
            && currTrack->GetStart().x - aGridSize.x < currTrack->GetEnd().x
            && currTrack->GetStart().x + aGridSize.x > currTrack->GetEnd().x
            && currTrack->GetStart().y - aGridSize.y < currTrack->GetEnd().y
            && currTrack->GetStart().y + aGridSize.y > currTrack->GetEnd().y )
charras's avatar
charras committed
216 217 218 219 220
        {
            currTrack = currTrack->Back();
        }


221
        for( TRACK* track = m_Pcb->m_Track; track; track = track->Next() )
charras's avatar
charras committed
222
        {
223
            if( track->Type() != PCB_TRACE_T )
charras's avatar
charras committed
224 225 226 227 228
                continue;

            if( doCheckNet && currTrack && currTrack->GetNet() != track->GetNet() )
                continue;

229
            if( m_Pcb->IsLayerVisible( track->GetLayer() ) == false )
charras's avatar
charras committed
230 231 232 233 234 235 236 237 238
                continue;

            // omit the layer check if moving a via
            if( !amMovingVia && !track->IsOnLayer( layer ) )
                continue;

            if( !track->HitTest( *curpos ) )
                continue;

239
            // D(printf( "have track prospect\n");)
charras's avatar
charras committed
240

241
            if( Join( curpos, track->GetStart(), track->GetEnd(), currTrack->GetStart(), currTrack->GetEnd() ) )
charras's avatar
charras committed
242 243 244 245 246 247 248 249 250 251 252
            {
                // D(printf( "join currTrack->Type()=%d\n", currTrack->Type() );)
                return true;
            }

            if( aCurrentTool == ID_TRACK_BUTT || amMovingVia )
            {
                // At this point we have a drawing mouse on a track, we are drawing
                // a new track and that new track is parallel to the track the
                // mouse is on. Find the nearest end point of the track under mouse
                // to the mouse and return that.
253 254
                double distStart = GetLineLength( *curpos, track->GetStart() );
                double distEnd   = GetLineLength( *curpos, track->GetEnd() );
charras's avatar
charras committed
255 256

                // if track not via, or if its a via dragging but not with its adjacent track
257
                if( currTrack->Type() != PCB_VIA_T
258
                  || ( currTrack->GetStart() != track->GetStart() && currTrack->GetStart() != track->GetEnd() ))
charras's avatar
charras committed
259
                {
260
                    if( distStart <= currTrack->GetWidth()/2 )
charras's avatar
charras committed
261 262
                    {
                        // D(printf("nearest end is start\n");)
263
                        *curpos = track->GetStart();
charras's avatar
charras committed
264 265 266
                        return true;
                    }

267
                    if( distEnd <= currTrack->GetWidth()/2 )
charras's avatar
charras committed
268 269
                    {
                        // D(printf("nearest end is end\n");)
270
                        *curpos = track->GetEnd();
charras's avatar
charras committed
271 272 273
                        return true;
                    }

274
                    // @todo otherwise confine curpos such that it stays centered within "track"
charras's avatar
charras committed
275 276 277 278 279 280 281
                }
            }
        }
    }

    return false;
}